Large rewrites concentrate technical and delivery risk. Teams can spend months reproducing existing behavior before users receive any benefit, while the original system continues to change. The strangler fig pattern takes a different approach: replace capabilities incrementally and route traffic to the new implementation as each slice becomes ready.
Choose a bounded slice
Start with a capability that has a clear input, output, and ownership boundary. Good first candidates are important enough to validate the migration approach but not so central that every subsystem must move at once.
Avoid slicing only by technical layer. Replacing “the database layer” while every business workflow still spans old and new code often creates more coupling. A vertical capability such as document export or notification preferences is usually easier to own end to end.
Put routing at a controlled boundary
A gateway, reverse proxy, application facade, or internal adapter can decide whether a request reaches the old or new implementation.
Keep routing rules explicit and observable. Operators should be able to answer which implementation served a request without reading deployment history.
Deal with data ownership early
The hardest migrations are usually about data, not request routing. Decide which system owns writes for each migrated entity.
Dual writes are tempting but introduce partial-failure and ordering problems. Prefer a single authoritative writer where possible, then propagate data through a defined replication or event mechanism.
If temporary synchronization is unavoidable, document conflict resolution and build reconciliation tooling before relying on it.
Verify behavior before cutover
Useful techniques include replaying sanitized requests in a test environment, comparing read-only outputs, shadowing selected traffic without serving the new response, and using contract tests at the boundary.
Differences are not automatically bugs. A migration may intentionally correct old behavior. Classify expected and unexpected differences rather than requiring byte-for-byte equivalence everywhere.
Make rollback narrow
Incremental migration should improve rollback. If a new slice fails, route that capability back to the old implementation when data compatibility permits.
Rollback becomes harder after the new system changes data in ways the old system cannot understand. Treat schema and event compatibility as part of the rollout design.
Common pitfalls
Building a permanent proxy maze
Routing infrastructure is transitional architecture. Track migrated routes and delete obsolete forwarding logic after the old capability is retired.
Migrating shared data last without a plan
A new service that still reaches directly into a legacy database may be deployment-separated but not actually decoupled.
Recreating every legacy quirk
Preserve required contracts, but do not automatically copy accidental implementation details. Decide which behaviors are externally relied upon.
Never deleting the old path
A migration is not complete when traffic reaches the new system. Remove old code, jobs, tables, dashboards, and operational procedures once rollback windows and retention requirements permit.
Measure progress by retired responsibility
The value of incremental modernization is not the amount of new code written. It is the amount of legacy responsibility safely removed. Define slices, control routing, establish data ownership, verify behavior, and finish each migration by deleting the path it replaced.