The system still runs the business and nobody wants to touch it. Work that moves it onto something maintainable a piece at a time, with behaviour proven unchanged at each step.
Somewhere in your estate is a system everyone works around. It has not been replaced because the person who understood it left, the vendor stopped answering the phone, and the rules that keep the invoices correct live only in code. A rewrite sounds clean until you cost the parallel run and the freeze it needs. The quieter route is to establish what the thing really does, pin that down in tests, and move capabilities out one at a time while it keeps running.
Two ways through
The choice is when you take the risk.
Big-bang rewrite
Build the replacement alongside, in full, then switch over on a chosen date. It is honest about the end state and easy to fund as one project.
A clean design, unconstrained by the old system's shape
One budget, one plan, one date the board understands
The old system must be frozen while the new one catches up
Everything is unproven until the single day it all matters
Incremental replacement
A stable interface goes in front of the old system, then capabilities move behind it one at a time, with both paths live until the new one is trusted.
Each slice reaches production and can be rolled back alone
The business keeps releasing while the migration runs
Behaviour is compared against the old path before it is retired
It takes longer on paper and the interface costs real work
Four routes, and one that is doing nothing
Modernisation is a decision per capability. A system is rarely uniform: the pricing engine may be worth rebuilding while the batch that posts the ledger should be left exactly as it is. So the route gets chosen per capability rather than for the whole estate, and a single programme usually runs two or three of them at once.
Some systems should be left alone, and it is worth saying what one looks like. It is stable, the rate of change against it is close to zero, it runs on hardware and an operating system that are still patched, and the cost of its existence is a licence rather than a queue of blocked work. A system like that is a paid-off asset rather than technical debt, and touching it buys you nothing.
The four routes
What each route is for
Encapsulate, re-host, re-architect, rebuild. The choice between them is mostly economics: how often the capability changes, how well anyone understands its behaviour, and what the business would lose in the hours it was unavailable.
Encapsulate
Wrap it in an API and leave it
The system stays where it is and a documented interface goes in front of it, so everything new talks to that rather than to its internals. This is the right call when the code is stable but nothing can reach it. You buy integration and time, not maintainability.
Re-host
Move it, change as little as possible
Same code, newer place: a supported runtime, a current database version, hardware that is still sold. It is the cheapest route with the clearest end date, and it fixes nothing about the code. Worth doing when the deadline is a vendor's end-of-support notice rather than a business need.
Re-architect
Strangle it module by module
New code takes over one capability at a time behind a routing layer; the old path stays live until the new one has proven itself. The most work of the four, and the only route that keeps delivering to users throughout. It needs a seam a router can sit on.
Rebuild
Write the capability again
Sometimes the old behaviour is the problem. When a capability has drifted from what the business now needs, reproducing it faithfully is wasted effort and it should be specified afresh. This route has the longest gap before anything ships, so it suits a bounded capability rather than a whole system.
How the work runs
The order matters more than the tooling. Nothing moves until the current behaviour can be demonstrated, because a migration you cannot check is a rewrite with extra steps.
01
Read it
We work through the code, the schema, the jobs and the traffic it serves, separating what is used from what merely exists. You end up with a map of the system and a list of the behaviours any replacement has to reproduce.
02
Pin the behaviour
Characterisation tests capture what the system does today, correct or not, so a change in behaviour later shows up as a failing test rather than a support call. You review the suite and mark which behaviours are deliberate and which are bugs you live with.
03
Cut the seams
Before anything moves, the system needs places where traffic can be redirected and compared: an interface at the boundary, routing in front of it, and enough logging to tell the two paths apart. This is where the architecture decisions get written down, alternatives included.
04
Migrate a capability
One capability moves at a time. Both paths run against real traffic until the new one matches, then the old is switched off and its code deleted rather than left to rot as a fallback. Each slice ships and reverses on its own.
05
Hand it over
The last slice is not the end of the job. Runbooks, dashboards and on-call procedures ship with the system, and the people who built it are the ones who write them. You finish holding something your own team can run without us in the room.
Questions worth asking first
Most of it. The incremental route exists because a freeze long enough to rewrite a working system is rarely something a business can afford. New features land in whichever path already owns that capability, which does mean some work gets built twice in the overlap. That cost is real and we would rather name it than pretend the overlap is free.
Not sure which one you need?
Describe the problem in a paragraph and we will tell you which service applies.