A working system nobody enjoys is not automatically a system worth rebuilding. Here's how to decide honestly whether legacy software is a real liability or just unfashionable — and the safer middle paths nobody reaches for first.
- Old and unloved is not the same as worth replacing — the honest test is whether the system causes measurable harm, not whether it's dated.
- A full rewrite is compared against a fantasy of the replacement working perfectly; count the real cost, including the undocumented logic you'd lose.
- Replace-or-keep is a false binary — wrapping, incremental replacement, and fixing the one real problem are usually safer and cheaper.
Every business I've worked with has one. An old, unglamorous system tucked in a corner — the one that processes the orders, or tracks the stock, or reconciles the payments. It's slow to change. The person who built it left years ago. Nobody fully understands it. And every time someone new joins and gets shown around, they pull a face and say "we should really replace this."
And yet it runs. Day after day, it does the thing it was built to do, without much drama. That combination — universally disliked, quietly indispensable — is exactly what makes these decisions so hard to make well. The pressure to rip it out and start clean is strong and it comes from a genuine place. But "old and unloved" is not the same as "worth replacing," and treating them as the same is precisely how businesses talk themselves into expensive, risky rebuilds that leave them no better off, sometimes worse. The honest question was never whether the system is dated. It's whether it's costing you enough to justify the very real cost and risk of change.
Start by separating discomfort from harm
The first thing to pull apart is aesthetic discomfort from actual damage, because they feel identical from the inside and they are not the same thing at all.
A system can be thoroughly out of fashion — built with tools nobody would choose today, looking like software from another decade — and still do its job perfectly well. Ugliness is not a business problem. Slowness nobody actually feels is not a business problem. "It makes our engineers wince" is a real feeling and not, on its own, a business problem. Engineers wince at old code the way a chef winces at someone else's messy kitchen; it's a professional reflex, and it's a poor basis for spending six figures.
A legacy system earns replacement when it's causing measurable harm, and it's worth being concrete about what that actually looks like:
- It blocks something you need to do. A feature the business genuinely needs can't be added, or the system can't be connected to another one, because the old design simply won't allow it.
- It's expensive to keep alive. Licensing, hosting, or the specialist effort to maintain it has become a real, recurring drain rather than a rounding error.
- It's a genuine risk. It runs on something unsupported with open security holes, or it depends entirely on one irreplaceable person, or it's fragile and a failure would seriously hurt the business.
- It's actively slowing the business down. People burn significant time working around its limitations, or it can no longer keep pace with your volume.
If a system does none of these — if it's merely old — the honest advice is usually to leave it alone. Every rebuild carries cost and risk, and spending both to replace something that isn't actually hurting you is a poor trade no matter how much nicer the replacement would look in a demo.
The comparison everyone gets wrong
Here's the mistake that drives most bad modernisation decisions: people compare the shiny imagined future against the annoying present, and quietly leave out the cost of getting from one to the other.
Keeping a legacy system has ongoing costs — maintenance, the risk it carries, the opportunities it blocks, the hours lost to its quirks. Those are real and worth tallying honestly. But replacing it has costs too, and they're routinely underestimated, because the replacement is costed as a fantasy: on time, on budget, and working perfectly the day it launches. Set the real numbers side by side and the picture usually shifts.
| Keeping it | Replacing it | |
|---|---|---|
| The cost you see | Maintenance, licensing, the odd workaround | The quoted build cost |
| The cost you forget | Blocked features, key-person risk, security exposure | Running two systems in parallel, frozen improvements, rediscovering undocumented logic |
| The risk profile | Known and gradual — you live with it daily | Concentrated at the switchover — late, over budget, or worse than what it replaced |
| What it's compared against | The annoying present, felt every day | An imagined future that arrives free and works flawlessly |
An honest decision weighs the ongoing cost of keeping against the full, unromantic cost of replacing — months of building, two systems running at once, the risk of a launch that's late or worse than what it replaced. Not the cost of keeping against a daydream.
The wisdom hiding inside the mess
One line in that table deserves its own section, because it's the cost most often ignored and least often recovered: the undocumented logic a rebuild throws away.
A system that's run for years has quietly absorbed an enormous amount of reality. Every strange conditional, every special case, every "if it's this kind of customer on this kind of order at this time of month, do this odd thing" represents a real situation the business actually hit and someone actually solved. It looks like mess. Some of it is mess. But a great deal of it is knowledge — the precise difference between software that handles your real business and software that handles the tidy imaginary version someone sketched in a planning meeting.
The old code's ugliness and its hard-won wisdom are usually the same lines. The hard part of a rewrite is keeping the second while discarding the first.
When you rebuild from scratch, you start from the tidy version and throw the accumulated reality away. Then, over the following months, you rediscover those edge cases one painful incident at a time, because the old system silently handled them and the new one doesn't. This is why "just rewrite it clean" so reliably produces something worse before it's better, and occasionally something that never catches up at all. Before you commit to a rewrite, the question to sit with is uncomfortable: do we actually understand what this system does well enough to rebuild it without regressing — or are we about to pay, in outages, to relearn what it already knows?
The middle paths nobody reaches for first
Here's what gets lost in the replace-or-keep framing: those were never the only two options. The riskiest approach imaginable — switch the old system off, switch a brand-new one on, and hope — is also, strangely, the one people reach for first, because it's the one that offers the clean break the frustration is craving. There are far safer roads, and they're usually the right ones.
Wrap it, don't replace it. Very often the real pain isn't the old system at all — it's that you can't connect anything to it. Build a modern layer around it, so newer systems can talk to it cleanly while the core keeps running untouched, and you've solved the actual problem without the risk of a rebuild. This is frequently the highest-return move available and the most consistently overlooked.
Replace it piece by piece. Rather than rebuilding everything at once, carve off one part, replace just that, prove it works in production, and only then move to the next. Old and new run side by side and you migrate gradually. It's slower and far less satisfying than a clean-slate rebuild, but it's dramatically less risky — and at every step you can stop, adjust, or change your mind, instead of betting the whole business on one midnight switchover.
Fix the specific pain and leave the rest. Sometimes the entire case for modernisation rests on one or two genuine problems. Solve those directly and the pressure to rebuild quietly evaporates, because it turns out the system was mostly fine and a single limitation was doing all the damage.
The incremental paths don't give you the emotional release of a fresh start. That's exactly why they're underused, and exactly why they're how modernisation actually tends to succeed.
Five questions that cut through it
If you're weighing up a legacy system, most of the noise clears once you can answer these honestly:
- 01What specific, measurable harm is this system causing — beyond being old and unloved?
- 02What would replacing it truly cost, including the months of building, the parallel running, the risk, and the undocumented logic we'd have to rediscover?
- 03Could we solve the actual pain by wrapping or extending it rather than replacing it?
- 04If we do replace it, can we do it in pieces so we're never betting everything on a single switchover?
- 05Do we understand the accumulated logic well enough to rebuild it without regressing?
Answer those and the right path usually reveals itself. Sometimes it genuinely is a full replacement, and when it is, it deserves to be done properly and without flinching. But at least as often the honest answer is to wrap it, extend it, fix the one real problem, or — the most underrated option of all — leave a working system alone and spend the money and the risk somewhere it actually moves the business.
Because the goal was never to have modern software. It was to have software that serves the business well. Sometimes those are the same thing. Surprisingly often, the tired old system you keep wanting to replace is already doing exactly that, and the genuinely mature move is to let it.