advanced 3 min answer

A legacy scheduling system must be replaced incrementally. How should a strangler approach, traffic shadowing, dual-run comparison and rollback be sequenced, and what reconciliation is required?

stranglershadow-trafficdual-runreconciliationmigration
Show the full answer Hide the answer

The sequence

1. Put a facade in front of the legacy system. All callers go through it; it forwards everything to the legacy system. Nothing has changed functionally, and this step is what makes everything after it possible — it is the routing point for every subsequent stage and the rollback mechanism for all of them.

2. Choose the first capability to extract. Ideally something with clear boundaries, meaningful value, and low blast radius — not the hardest part, and not the most trivial. The first extraction is establishing the pattern and the tooling, and its purpose is to be completed.

3. Build the replacement and shadow it. Real traffic is sent to both; the legacy response is returned to the user and the new response is discarded after comparison. Users are unaffected and the new system is exercised against real production inputs, including the ones nobody would have thought to test.

4. Compare, and treat divergence as the primary output. Most divergences are the new system being wrong. A meaningful minority are the legacy system having behaviour nobody documented — and some of that behaviour is depended upon. This is where the real requirements are discovered.

5. Dual-run with the new system authoritative for a subset. Route a small percentage of traffic to the new system for real, still writing to both and comparing, with instant rollback at the facade.

6. Increase the proportion, monitoring business-level outcomes rather than only technical metrics.

7. Make the new system authoritative for all traffic, with the legacy still running and still receiving writes, so rollback remains available.

8. Stop writing to the legacy system. This is the irreversible step and should be separated from the traffic cutover by a meaningful period.

9. Archive the legacy data and decommission, with a date, in the same funded project — or the migration produces two systems instead of one, which is the most common outcome of this pattern.

Reconciliation at each step

  • During shadowing: field-by-field comparison, with an explicit taxonomy of acceptable differences — timestamps, generated identifiers, ordering of unordered collections. Without that taxonomy the comparison drowns in noise and is switched off.
  • During dual-run: periodic reconciliation of the two systems' stored state, not just their responses. Responses agreeing does not mean the stored state agrees, and divergent state is what surfaces after cutover.
  • Before making the new system authoritative: a full data comparison, with every difference explained rather than tolerated.
  • After cutover, before stopping legacy writes: continued reconciliation, since this is the window in which rollback is still possible and a discovered problem is cheap.

What makes it fail

  • No facade, so each extraction requires changing every caller.
  • Starting with the hardest component, which stalls before the pattern is established and discredits the whole approach.
  • Shadow comparison producing noise, so it is disabled and the main benefit is lost.
  • Divergence assumed to be the new system's fault, so undocumented legacy behaviour is never discovered.
  • No decommissioning plan, leaving both systems running permanently — which is worse than either, since every change must now be made twice.
  • A migration with no end date, which loses sponsorship and leaves the estate half-migrated indefinitely.

The honest caveat

Strangler works well when the legacy system has extractable seams. A system with a single shared database that every component writes to, or with logic entangled across modules, may have no clean boundary to extract — in which case the first step is creating a seam, which is real work and must be planned rather than discovered.