The full rewrite is almost always the wrong call, and almost always the first instinct. Four tests to decide, and the middle path.
A developer meeting someone else's code says roughly the same thing every time: it's unreadable, we have to start over. Sometimes they're right. But that judgement is also the cheapest one to make and the most expensive one to follow.
Why the rewrite is seductive.
Taking over existing code means understanding someone else's decisions, many of which look absurd until the day you find out why they were made. Starting from zero means understanding only your own. That's more pleasant — and it has nothing to do with what serves your business.
What ugly code contains.
What looks like disorder is often accumulated knowledge. The strange condition on line 340 fixes an edge case found in production two years ago. The workaround in the billing module handles a legacy client on special terms. Nobody remembers, nothing is documented — but the behaviour is correct.
A rewrite starts from an idealised understanding of the problem and reintroduces every edge case, one at a time, in production, in front of your clients.
Four tests to decide.
The startup test: can a new developer run the project locally within a day? If not, the problem may be the tooling rather than the code — and tooling is a week's work.
The change test: how long for a simple, well-bounded change? If a one-day modification takes five because of side effects, the code is genuinely costing you money. That's the only sound financial argument for a rewrite.
The test test: are there automated tests on the critical paths? If not, you can neither repair nor rewrite safely, because nothing will tell you the behaviour changed. Writing those tests is the first job, whichever way you decide.
The dependency test: does the code rest on a technology that no longer receives security patches? That's the one case where a rewrite is beyond argument — the debt has become a risk rather than an annoyance.
The middle path.
Between repairing and rewriting there's a third option that almost always works better than either. Isolate one function of the system, rewrite it cleanly behind a stable interface, move traffic to the new one, and the old one dies on its own. Then do the next.
Three decisive advantages: production never stops, each step delivers measurable value, and you can stop when the value stops justifying the effort — which happens often, and is good news. A full rewrite has no value at all until it's finished to the last percent.
The freeze trap.
The real cost of a rewrite isn't the development. It's the quarter — or the year — during which your product stops moving. Your competitors ship. Your clients ask for features you decline “until the migration is done”. Your salespeople lose deals over a missing feature that existed in the original plan and hasn't been ported yet.
Plenty of rewrites succeed technically and lose commercially.●
What we don't do.
We don't take over existing code without an audit first — one to two weeks, billed, before any commitment. A vendor who commits to code they haven't read is committing to a guess, and you'll pay the difference. The audit may well conclude the code is sound and the problem is elsewhere. That has happened, and it's a good result.
