AXENTED — Blog Article
Slug: /blog-posts/software-rewrite-vs-refactor |
Meta description: The pull toward a full rewrite is almost always emotional, not analytical. An objective decision framework with criteria that separate the two cases clearly. |
Target keywords: software rewrite vs refactor, should i rewrite codebase, legacy code refactor, software rewrite decision |
"We need to rewrite this" is one of the most expensive sentences in software. Not because rewrites are always wrong — sometimes they're the right call. But the decision is made emotionally far more often than rationally, and the emotional version almost always points toward a rewrite when the rational version would point toward incremental improvement.
This is a decision framework with objective criteria.
Rewrites feel decisive. They offer the promise of starting clean, of making all the good decisions that were missed the first time, of eliminating every legacy compromise in one bold move. Refactoring feels like patching a leaky boat. The pull toward rewrite is almost always emotional, not analytical.
The engineering literature has a consistent finding: most software rewrites take three to five times longer than estimated, deliver fewer features than the system they replaced, and reproduce a significant portion of the original system's bugs because the requirements for the edge cases lived in the code, not in the documentation. Joel Spolsky called it "the single worst strategic mistake any software company can make." Netscape's 5.0 rewrite. The FogBugz rewrite. The pattern repeats.
Rewrites are justified in a narrow set of circumstances. The underlying technology stack is genuinely end-of-life with no upgrade path — not "old," but actually unsupported and incompatible with the tools and infrastructure you need to use. The system has fundamental architectural constraints that make the required functionality impossible to add, not just difficult. The codebase has been modified by so many people over so many years without any coherent design that the accumulated complexity is higher than starting fresh.
That last criterion is rarer than teams think. A codebase that feels messy to engineers joining it is usually navigable by the engineers who have worked in it for a year. "New engineers are confused" is a documentation and onboarding problem, not a rewrite problem.
For most systems that genuinely need major improvement, the strangler fig pattern is safer than a full rewrite. Build new functionality in a clean new system alongside the existing one. Gradually route traffic to the new system module by module. Retire the old system as each part is replaced. The old system never stops running, so you never have a moment where nothing works.
This approach is slower and less satisfying than a clean rewrite. It also ships features during the process, keeps the existing system running for real users, and surfaces integration problems incrementally rather than all at once at the end of a multi-year project.
Refactoring makes sense when the business logic is correct and the execution is messy. The system does what it's supposed to do, but changing it is slow and risky. The right intervention is incremental improvement: extract the most painful module, clean it up, add tests, move on to the next. Each improvement makes the next one cheaper.
The discipline is not trying to refactor everything at once. Pick the module that creates the most friction for current work. Clean it. Repeat. Teams that try to refactor the entire system in a single initiative usually abandon the effort halfway through, leaving the codebase in a worse state than before they started.
Before deciding, answer these questions honestly: Does the system prevent us from building features that our product requires? Are the engineers who built it still here to explain the logic? Is the underlying technology actually unsupported, or just old? Could the most painful 20% of the codebase, if cleaned up, resolve 80% of the daily friction?
If the system prevents required features: consider a partial rewrite of the affected component. If the original engineers are gone: prioritize documentation and knowledge recovery before any structural changes. If the technology is just old but functional: refactor incrementally. If 20% of the codebase causes 80% of the problems: start there.
Most codebase problems are refactor problems. The ones that require rewrites are genuinely rare.