The case for a rewrite is always the same and always compelling: the current system is understood by three people, changes take months, and the technology is out of support. The case against it is only visible later — a rewrite freezes the business for the duration, and the requirements it was scoped against will have moved by delivery.
Incremental modernization is slower on paper and faster in practice, because value ships continuously and the organization never carries two systems' worth of risk at once.
Start by finding the seams
A seam is a place where behaviour can be intercepted without changing the code on either side of it — an API boundary, a message queue, a scheduled batch, a database view. Modernization progresses at the rate you find seams. Systems with no seams need one built first, and that is a legitimate, fundable first phase.
Route, replace, retire
Put a facade in front of the legacy system so callers address the facade rather than the system. Route one capability at a time to a new implementation behind that facade. When no traffic reaches the legacy path, retire it. This is the strangler fig pattern, and its virtue is that every step is independently reversible.
- Choose the first capability for low coupling, not high value. The first slice proves the mechanism.
- Run new and old in parallel with output comparison before switching traffic. Discrepancies are usually undocumented legacy behaviour, which is exactly what you needed to find.
- Delete the legacy path as soon as it is idle. Un-retired code is the reason these programmes stall — the estate grows instead of shrinking.
Data is the hard part, not the code
Application logic can be replaced behind a facade. Data cannot be duplicated indefinitely without a source of truth decision. Decide early, per entity, which system owns writes during the transition, and make that ownership explicit in the architecture rather than implicit in a sync job. Bidirectional sync between two systems that both accept writes is the most reliable way to turn a modernization into a data-integrity incident.
Every modernization that went badly had a period where two systems both believed they owned the same record.
Keep the lights on budget separate
If modernization and production support share a team and a budget, support wins every sprint — correctly, because production incidents are real and modernization deadlines are internal. Fund and staff them separately, even if the same people rotate between them.
How to know it is working
Not by percentage complete. Track the number of capabilities served by the new path, the volume of legacy code deleted, and the lead time for a change in the modernized area versus the legacy area. If lead time in the new path is not measurably better within two quarters, the new architecture has inherited the old constraints and the approach needs revisiting before more scope moves.



