Modernize without rewriting
Every senior engineer eventually inherits a codebase they'd rather rewrite. Almost every rewrite ships late, ships less, and ships bugs. Here's the incremental playbook I use instead — including how I moved 250+ files onto React 19 without a freeze.

Every senior engineer inherits, at some point, a codebase they'd rewrite from scratch if they could. Sometimes the framework is two majors behind. Sometimes the state management is a slow-motion car crash. Sometimes the tests are written in three different eras.
And the reflex — I've had it, you've had it — is: rewrite.
Almost every rewrite I've watched ships late, ships less than the original, and ships with a fresh set of bugs the old code didn't have. Meanwhile the business paused for six months. Nobody remembers this until they're two months into their next rewrite.
The rule I hold to now: modernize without rewriting.
Why rewrites fail even when you plan for them
A rewrite is not "write the same thing in a nicer stack." It's reinvent every edge case, every workaround, every quiet business rule the old code accumulated. The old system contains three years of "oh, this one customer needs it this way" — none of which is documented, most of which the original authors have left. You rediscover each one in production, one angry ticket at a time.
Meanwhile you can't ship features. You can barely ship bug fixes. The team is stretched between "keep old code alive" and "get new code to feature parity" and the second bucket is always losing.
The strangler pattern, applied properly

The alternative is boring, slow, and works: keep the old system running, wrap it, and replace it piece by piece.
Concretely, in the codebase, that looks like:
New code goes in the new pattern. Old code stays in the old pattern. Both work.
When a piece of old code needs to change, migrate that piece to the new pattern as part of the change.
Every quarter, count what's still on the old pattern. If it's shrinking, you're winning. If it's stable, you're losing to entropy — you need dedicated migration time.
The team never has to stop shipping features to modernize. Modernization is shipping features. And the old code deletes itself, module by module, without a big-bang cutover.
A worked example: React 19 across 250+ files

Recently at Diligent I inherited a React 18 codebase that needed to move to React 19. About 250 files. The team was mid-roadmap. A freeze was not on the table.
The playbook we ran:
Land the upgrade with a compatibility shim. React 19 has real breaking changes (ref-as-prop, deprecated
propTypes, updated types). Ship a small compat layer that lets old and new patterns coexist. Nothing breaks on merge.Migrate by module, not by file. A single "modernize this file" PR is boring. A single "migrate the entire billing module" PR tells a story, and lets the reviewer verify the module still works end-to-end.
Automate the mechanical parts. Codemods for the 80% of syntactic changes. Human eyes only for the 20% that involve actual judgement.
Add a lint rule that blocks new usage of the old pattern. Otherwise the migration is a Sisyphean loop: you migrate five files, someone else adds three new ones the old way.
Track progress publicly. A single number in the PR template — "files still on the old pattern: 187 → 184." Momentum is contagious once it's measurable.
The migration finished with zero downtime, no freeze, and a ~30% improvement in developer productivity by the end because the new patterns are genuinely nicer. Also, notably: no rewrite happened. The codebase you started with is the codebase you ended with, just modernized in place.
When to actually rewrite
Almost never. But there is one honest case: when the domain model itself is wrong — not the code, the model. If the old system is built around assumptions that no longer describe the business, incremental migration will only entrench the wrong shape. That's a rewrite. But confirm you have a genuinely new model before you decide.
Everything else — old framework, ugly code, wrong patterns — is a strangler.
The habit worth building
Every time your fingers reach for "let's just start over," pause. Ask: what would the six-month strangler version of this look like? Nine times out of ten, that's the version that ships.
More Articles
Continue reading with these related posts



