Why rewrites fail, and what to do instead
The rewrite is the most confidently proposed and least frequently completed project in enterprise software. The failure mode is structural, not managerial.
Marcus Ihejirika
Principal Architect
Every legacy system eventually produces the same meeting. The code is unmaintainable, velocity has collapsed, and someone proposes starting fresh. The estimate is eighteen months. It is always eighteen months.
The structural problem
Rewrites fail for a reason that has nothing to do with team quality. While you rebuild, the original system does not stand still. It keeps accruing bug fixes, regulatory changes, and features the business needs this quarter. You are trying to hit a target that moves at the speed of your own organization.
This produces a brutal dynamic. Either you freeze the old system — which the business will not tolerate for eighteen months — or you maintain both, which means doing every piece of work twice with a team that was already stretched.
The undocumented behavior problem
The second issue is subtler. A legacy system's actual behavior is not its documented behavior. It's the documented behavior plus a decade of accumulated special cases, undocumented workarounds, and quiet bugs that downstream systems now depend on.
Nobody knows the full set. It isn't written down anywhere, and the people who would have known have moved on. You discover these behaviors one at a time, in production, after cutover, when something breaks in a way nobody can explain.
Strangler-fig, concretely
The alternative is incremental extraction. Put a facade in front of the legacy system. Route all traffic through it. Then extract one capability at a time behind that facade, with production traffic proving each extraction before you move to the next.
This is slower on paper and finishes more often. Each step is small enough to reason about, reversible if it goes wrong, and delivers value independently — which means if priorities shift halfway through, you're not stranded holding a half-built replacement.
Characterization tests
For the undocumented behavior problem, characterization tests are the tool. Rather than testing what the system should do, you capture what it actually does — run production-shaped inputs through it, record the outputs, and treat those recordings as the specification.
This feels wrong to engineers who care about correctness, because you're enshrining bugs. But the bugs are the specification, in the sense that downstream consumers have adapted to them. Deciding to fix one becomes an explicit, separate decision rather than an accidental side effect of the rewrite.
When a rewrite is right
Sometimes it genuinely is the answer. When the system is small enough to rebuild in a few months. When the platform is end-of-life with a hard deadline. When the domain has changed so fundamentally that the old model doesn't describe the business anymore.
What these have in common is a bounded, well-understood scope. The rewrites that fail are the ones proposed for large systems whose behavior nobody fully understands — which is, unfortunately, exactly the category that most often prompts the proposal.