Every institution we work with has a system that everybody agrees needs replacing and nobody wants to be responsible for replacing. It works, it has absorbed fifteen years of undocumented edge cases, and the person who wrote it left in 2019. The instinct is to build the replacement in parallel and cut over on a long weekend.
That plan has a failure mode with no graceful exit. On Monday morning the new system is either right or the business is not trading, and every hour of the decision is made under maximum pressure with minimum information.
Strangle rather than replace
The alternative is to put a boundary in front of the old system and move responsibilities across it one at a time. Each move is small, independently reversible, and provable in production before it carries any weight. The old system shrinks; at no point is there a day where everything changes.
The first move is usually not a feature at all — it is the boundary itself. Getting an interface in front of a legacy system, with traffic flowing through it unchanged, is a week or two of work that buys you every subsequent option.
Shadow before you switch
For each responsibility being moved, run the new implementation alongside the old on live inputs with its outputs suppressed, and diff. Not "spot-check a few cases" — diff every decision, every day, and require a clean run for a defined period before the new path takes over.
This is where the undocumented edge cases surface, and they always do. The rounding rule for one instrument class. The venue that sends a fill message twice. The overnight batch that quietly corrects the previous day. You want to find these in a diff report, not in a reconciliation break.
Which piece goes first?
Pick the piece with the highest ratio of pain to risk. Usually that is reporting or analytics: heavily used, frequently complained about, and incapable of causing a trading incident if it is wrong for an afternoon. It also gets the team fluent in the shadow-and-diff process before that process is protecting anything expensive.
Save the order path for last. By the time you reach it, the boundary is mature, the diffing infrastructure is proven, and the team has done the manoeuvre a dozen times on lower stakes.
Keep the old path alive longer than feels necessary
Once a responsibility has moved, the temptation is to delete the old implementation immediately — it is dead code, it is confusing, and removing it feels like progress. Resist for one full cycle: a month-end, a quarter-end, an expiry, whatever the longest period in your business is. Those are the moments that expose behaviour nobody tested for.
The migration is finished when the old path has been switched off for a full cycle and nobody noticed. That is a quieter milestone than a cutover weekend, which is rather the point.