Legacy modernisation without the big rewrite
The replacement project has a poor track record, and the reasons are structural rather than technical. Here's the alternative.
Every legacy system was, at some point, someone's competent solution to a real problem. It became legacy because the business changed, the people who understood it left, and the cost of touching it rose until nobody would.
The instinct is to replace it. The instinct is usually wrong — not because the system is good, but because the replacement project as normally conceived has a poor track record, and the reasons are structural rather than technical.
Why the big rewrite fails
The plan is always the same: build the new system alongside the old one, then switch over. Three things reliably go wrong.
The old system doesn't stand still. The business keeps needing changes. Now you're maintaining two systems and every change is made twice — which is where the schedule goes.
Nobody knows what it actually does. The documented behaviour and the real behaviour diverged years ago. Every edge case discovered mid-build is scope that wasn't estimated, and there is no way to enumerate them in advance.
Value arrives only at the end. Eighteen months of spend with nothing usable delivered. When budgets tighten or the sponsor moves on — and over eighteen months, one of those usually happens — the project dies with nothing to show.
The pattern we look for: if a modernisation plan has a single go-live date more than six months out, it will slip, and the slip will be discovered late. Structure it so that value lands every few weeks instead.
Incremental replacement instead
The alternative is to route traffic through a layer you control, then move one capability at a time behind it. The old system shrinks; the new one grows; the business never experiences a cutover.
1. Put a seam in front of it
An API gateway, a proxy, or an adapter service — something every consumer talks to instead of talking to the legacy system directly. Initially it forwards everything unchanged. This step delivers no visible value and is the most important one in the whole programme, because nothing else is possible without it.
2. Pick the first capability by risk, not by size
Not the biggest module. The one that's reasonably self-contained, changes often enough to be annoying, and would be recoverable if it went wrong. You are proving the mechanism works, not making a dent in the total.
3. Build it, run both, compare
Run the new implementation in shadow mode: it receives the same input and produces output that nobody acts on, while you compare it against the old system's result. This is where you discover the undocumented behaviour — and you discover it safely, in a log file, rather than in production.
4. Switch the route
Flip that one capability at the seam. Keep the old path warm so you can switch back in seconds if something surfaces. Then repeat.
Data is the hard part
Everyone expects the code to be difficult. It's the data that decides whether this works.
Decide where truth lives, per entity. During transition, both systems have customer records. One must be authoritative and the other must follow. Ambiguity here produces divergence, and divergence produces the bug reports that end programmes.
Prefer one-way sync. Bidirectional sync means conflict resolution, and conflict resolution means a business rule nobody wants to own. Wherever possible, move ownership of an entity wholesale rather than sharing it.
Expect the data to be worse than described. Twenty years of a system produces records that violate every constraint anyone believes exists — customers with no country, orders predating the company, duplicate keys that "can't happen". Budget a cleaning and reconciliation phase explicitly. It is not optional and it is not small.
Getting the behaviour out of people's heads
The most valuable artefact from a modernisation isn't the new code; it's the first accurate description of what the system does. Three techniques, in order of reliability:
- Read the logs. Production traffic tells you which paths actually get used. It is common to find that a third of a system's features have had no traffic in a year — that's a third you may not need to rebuild.
- Characterisation tests. Write tests that assert what the old system currently does, including behaviour that looks like a bug. You are capturing reality, not correctness. Those tests become the acceptance criteria for the replacement.
- Sit with the users. Not a requirements workshop — actually watch them work. The workarounds people have built around the system's failings are requirements, and they never appear in a workshop.
What to leave alone
A modernisation programme should have an explicit list of things it will not touch. Good candidates:
- Components that work, rarely change, and have no dependencies blocking other work. Ugly is not the same as broken.
- Anything scheduled for retirement anyway — don't rebuild what the business is about to stop doing.
- Integrations with third parties who are themselves mid-migration. Wait for them.
The goal is not a clean codebase. It is a business that can change quickly again. Those overlap, but they're not the same thing, and conflating them is how modernisation budgets get spent on the wrong modules.
How to tell it's working
Lines of code migrated is a vanity metric. Better signals:
- Lead time for a typical change — the number everyone actually cares about, even if they express it as frustration rather than a metric.
- Number of capabilities behind the new seam — concrete, countable, honest.
- Incidents attributable to the legacy path — should trend down as traffic moves.
- People who can safely change each area — if it's still one person, the knowledge problem hasn't been solved regardless of what the code looks like.
The uncomfortable part
Incremental modernisation is slower to start and less satisfying to describe than a rewrite. There's no launch. There's no moment where the old system is ceremonially switched off — it just gets smaller until someone notices it's handling nothing and quietly decommissions it.
What it does have is a much better completion rate, value delivered continuously, and the ability to stop at any point with everything built so far still in production and still useful. On a multi-year programme with changing sponsors and shifting budgets, that optionality is worth more than the elegance of a clean-slate design.
Working on something like this?
Book a free 30-minute call. It's a working session with an engineer — bring the problem and we'll tell you what it would realistically take to build.
Related reading
All articles →Why ERP rollouts fail, and how to phase one that sticks
ERP projects rarely fail technically. They fail when half the organisation is still using the old spreadsheet six months later.
AIWhere AI automation actually pays: choosing your first process
Most companies pick the process that annoys them most rather than the one that costs the most. Those are rarely the same thing.
AIWhat it takes to run AI agents in production
An agent demo takes an afternoon. An agent you'd let touch a customer record takes considerably longer — and almost none of the extra work is prompt engineering.
Ready to Build Your Next AI Product?
Book a free 30-minute call. We'll go through your requirements and tell you what it would realistically take to build.