Why rewrites fail
A legacy system that has run the business for a decade contains a decade of decisions — edge cases, regulatory quirks, workarounds for a supplier who sends malformed files every March. Almost none of it is documented.
A rewrite has to rediscover all of it while the old system keeps changing underneath, and it delivers no value until the day it replaces everything at once. That day tends to slip.
Strangle it instead
The alternative is to route traffic through a layer in front of the old system, then move one capability at a time behind it. Each move is small, independently testable, and reversible.
The business gets improvements continuously rather than waiting for a single cutover, and if a piece goes badly you roll back that piece rather than the programme.
- Put an API layer in front of the legacy system
- Pick the capability with the highest pain and lowest coupling
- Rebuild that one capability, route to it, keep the old path as fallback
- Migrate the data it owns, verify against the old system in parallel
- Repeat, and retire old code only once nothing calls it
Start with the seams
Look for boundaries that already exist: a reporting module that only reads, a batch job that runs nightly, an integration that talks to one external service. These come away cleanly.
Avoid starting with the piece that touches everything, however tempting it is — that is the last thing to move, not the first.
Know when to stop
Not every legacy system needs to be fully replaced. Some are stable, well-understood, and cheap to run. If a component is not changing and not costing you, leaving it alone is a legitimate engineering decision.
Modernisation is a means to lower cost and faster change — not an end in itself.
