SyncTrix logoSyncTrix
All articles
Engagement11 min read

Rewrite or refactor: making the call with something other than instinct

Engineers want to rewrite. Executives want to refactor. Both positions are usually held before anyone has measured which problem they actually have.

By Marcus Hale
Rewrite or refactor: making the call with something other than instinct

The rewrite argument arrives with confidence and a timeline, and the timeline is almost always wrong for a reason that is structural rather than optimistic: the existing system's behaviour is not written down anywhere except in the existing system. Refactoring has its own failure mode, which is that it never quite happens. Choosing between them should start with measurement rather than preference.

01Diagnose the actual problem first

'The code is bad' is not a diagnosis. Is change slow because the code is tangled, or because there are no tests and every change requires manual verification? Are incidents frequent because of architecture, or because of one unreliable dependency? Is the technology genuinely unsupportable, or merely unfashionable?

These have different remedies. Missing tests are addressed by adding tests, not by rewriting. A tangled module is refactored. Only a small subset of problems - a fundamentally wrong data model, a runtime that is genuinely end-of-life, an architecture that cannot meet a hard requirement - actually indicate a rewrite.

SymptomUsual causeRemedy
Every change takes weeksNo tests, manual verificationTest harness first
Frequent production incidentsSpecific weak pointsFix those, add observability
Cannot hire for the stackGenuinely obsolete runtimeIncremental migration
Cannot meet a performance requirementArchitectural limitTargeted rewrite of that path
Data model is fundamentally wrongOriginal assumptions no longer holdRewrite is defensible
Nobody likes working in itOften the above, sometimes fashionDiagnose further
Symptom to remedy

02Why rewrites overrun so consistently

The old system encodes years of edge cases, regulatory adjustments and one-off fixes that nobody remembers making. A rewrite must reproduce all of them to reach parity, and there is no document listing what they are - you discover them as bug reports after launch.

Meanwhile the old system cannot be frozen, because the business keeps needing changes. So you maintain two systems and build one, with the target moving throughout. This is why rewrite timelines are commonly out by a factor of two or more, and why so many are cancelled partway.

03The strangler pattern is usually the answer

Put a facade in front of the old system, then move capabilities behind it one at a time. Each piece is small, independently valuable and reversible. The old system shrinks gradually rather than being replaced in one terrifying event.

It is slower in aggregate and dramatically less risky, and crucially it can be paused. Priorities change, and a strangler migration that stops halfway leaves you with a working system. A big-bang rewrite that stops halfway leaves you with nothing.

Big-bang rewriteStrangler
Time to first valueAt the end, if it landsWeeks
RiskConcentrated in one cutoverDistributed, each step reversible
Can be pausedNoYes, at any point
Total effortLower in theoryHigher, with overhead
Historical success ratePoorGood
Comparing the approaches honestly

04When a rewrite is genuinely right

Small systems, where the whole thing can be rebuilt in weeks rather than years. Systems where the data model is fundamentally wrong and no amount of refactoring changes that. Runtimes that are genuinely unsupported with real security implications. And cases where the business requirement has changed so much that the existing system solves the wrong problem.

The common thread is that incremental change cannot get there from here. If a plausible incremental path exists, it is nearly always the better choice regardless of how unappealing the existing code is.

05Before committing either way

Write down what specifically is wrong, with evidence - deployment frequency, incident rates, time to implement a representative change. Then estimate both paths honestly, including the cost of maintaining the old system during a rewrite, and double the rewrite estimate because that is what the historical record supports.

If the rewrite still wins after that, it is probably genuinely the right call. If the case only works with an optimistic timeline and a frozen old system, you have found the reason most rewrites end badly.

Topics

rewrite vs refactorlegacy system decisionstrangler fig migrationtechnical debt decisionsoftware modernization strategy

Marcus Hale

Lead Architect · SyncTrix

Writes about the engineering decisions behind production systems - architecture, delivery and the trade-offs that only show up at scale.

Building something like this?

SyncTrix engineers AI, SaaS, platform and cloud systems for enterprises and high-growth teams. Tell us what you're shipping and we'll scope it with you.

Talk to an engineer