Replacing a Legacy System Without a Big-Bang Cutover
By Maplecode
There is a familiar shape to legacy replacement programmes. A system everyone agrees is holding the business back. A two-year plan to replace it. A team assembled, a scope agreed, a launch date set. Eighteen months later the date has moved twice, the old system is still running, and the new one cannot go live because it does not yet do the eleven things nobody documented.
This happens often enough that it should be treated as the expected outcome of the approach rather than as bad luck. The problem is structural: a big-bang rewrite defers all of its risk to a single moment, and that moment arrives when the budget is spent and the appetite for discovering problems is at its lowest.
The alternative is not a better plan. It is a different shape.
Why rewrites go wrong
Three failures recur, and they compound each other.
The old system knows things nobody does. Two decades of accumulated behaviour includes rules that were never written down, encoded in a conditional branch by someone who left in 2011. Some of that behaviour is obsolete. Some of it is a regulatory requirement or the reason a major customer stays. You cannot reliably tell which from reading the code, and the people who could tell you have moved on.
The target keeps moving. The business does not stop for two years. New requirements arrive and must be built twice — once in the old system to keep operating, once in the new one to keep parity. The new system is chasing a system that is itself advancing, and the gap closes more slowly than anyone modelled.
Nothing is verified until the end. Every assumption made in month three remains untested until cutover. When one turns out to be wrong — and one will — it is discovered at the point of maximum cost and minimum flexibility.
Strangle it instead
The pattern that works, sometimes called the strangler approach after the fig that grows around a host tree until it stands alone, is to route traffic through a layer you control and move functionality across piece by piece.
Concretely: put a facade in front of the legacy system. Initially it forwards everything unchanged, which is a deliberately boring first milestone that proves the routing works. Then pick one capability, implement it in the new system, and switch that route. The old implementation stays in place, unused but available, until the new one has proven itself in production.
Repeat. Each increment is small enough to reason about, ships to real users, and can be reversed in minutes. The legacy system shrinks in responsibility until what remains is small enough to retire or is genuinely fine to leave alone — an outcome worth naming, because "we stopped here and that was correct" is a legitimate ending that big-bang programmes cannot reach.
Choosing what to move first
The instinct is to start with the most painful part. That is usually a mistake, because the most painful part tends to be the most entangled, and failing on the first increment costs you the organisational confidence the approach depends on.
Better criteria for the first slice: it should have clear boundaries, meaningful but not existential business value, and observable behaviour you can verify. Something read-only is ideal — a reporting view, a lookup, a search — because you can run the new implementation alongside the old and compare outputs on real traffic without risking anything.
That comparison is worth building properly. Sending production traffic to both implementations and diffing the results, while only the old one's response is returned to the user, is the single most effective de-risking technique available here. It finds the undocumented behaviour that no specification captured, at zero risk, before you depend on it.
The data problem is the actual problem
Routing requests is the easy half. Deciding where data lives during a transition that lasts eighteen months is the hard one, and it deserves more design attention than it usually gets.
The options, roughly in order of preference:
- Leave the data where it is. The new service reads and writes the legacy database. Unfashionable, and often correct for a long time. It removes synchronisation entirely, at the cost of coupling to a schema you dislike.
- One writer, replicated reads. One system remains the source of truth; the other receives a stream of changes. Manageable, with genuine eventual-consistency behaviour you must design around rather than hope about.
- Dual writes. Both systems written to on every change. Appealing and treacherous — partial failures leave the two divergent, and you will need reconciliation and repair tooling regardless. Sometimes necessary; never simple.
Whichever you choose, build the reconciliation check early. A job that compares the two stores and reports differences will find bugs you would otherwise learn about from a customer, and it becomes the thing that lets you cut over with confidence rather than hope.
What this costs
It would be dishonest to present this as free. The incremental approach carries real overhead the big-bang plan does not.
You maintain two systems for the duration, which is genuinely more work than maintaining one. You build routing and comparison infrastructure that has no value once the migration completes. You accept a period — potentially years — where the architecture is visibly transitional and nobody is proud of the diagram.
The trade is that value arrives continuously instead of at the end, risk is discovered in small pieces while it is cheap to address, and the programme can be paused, reprioritised or stopped at any point without writing off the investment. Given how often two-year rewrites fail outright, that optionality is worth a substantial premium.
The organisational part
The technique is well documented. What sinks these programmes more often is expectation management, and it needs handling explicitly.
Incremental migration produces no dramatic launch. There is no moment to celebrate, no press release, no slide showing the old system switched off. Sponsors who authorised a replacement programme will ask, at month nine, why the legacy system is still running — and the honest answer, that it is running considerably less, is unsatisfying if nobody agreed in advance that this was the plan.
So make the increments visible. Report what moved this quarter, what percentage of traffic the new system serves, what the legacy system no longer does. Give people the milestones the approach genuinely produces, rather than letting them measure it against a launch date that was never coming.
The programmes we have seen succeed all had that agreement in place at the start. The ones that struggled were incremental in engineering and big-bang in the minds of everyone funding them.