Strangler Fig: Incremental Replacement
Wrap the old system behind a router and replace it one route at a time.
Strangler Fig is the default approach to modernization. You wrap the old system behind a routing layer, build replacements behind that layer, and migrate one feature at a time until the old system is empty. Then you delete it. The name comes from the plant that grows around a host tree and gradually takes its place.
When to use this play#
Reach for Strangler Fig when:
- The system is in production and must stay running.
- You cannot afford a full cutover.
- The seam between old and new lives at the network or HTTP layer.
- Leadership wants continuous shipping rather than one big release.
If the seam is inside the code rather than at a network boundary, look at Branch by Abstraction instead.
How to run it#
1. Stand up a dumb routing layer. Put a gateway or proxy in front of the existing system. It should do one thing: send each request either to the old system or to the new one. Resist the urge to make it clever.
2. Build new functionality behind it in a new service. New work lands in a new service that sits behind the router. The old system keeps serving everything that has not been migrated.
3. Migrate one route or feature at a time. Move a single slice of behavior from old to new, point the router at the new implementation for that slice, and confirm it works in production before moving on.
4. Keep both running until the old one is empty. The two systems coexist for the whole migration. That is the point: every step is small and reversible.
5. Remove the old system. Once the router sends nothing to the old system, delete it, along with the routing entries it no longer needs. Migration is not finished until the old code is gone.
Choosing what to migrate first#
The order of slices matters as much as the slicing itself. Pick early slices that teach you the most while risking the least. Good first candidates share a few traits:
- Low blast radius. Migrate something that, if it goes wrong, affects a contained set of users or a non-critical path. Save the revenue-critical flows for after the pattern is proven.
- A clean seam. Prefer a feature whose boundary at the network layer is already crisp, so the router does not have to untangle shared state to send it elsewhere.
- Representative shape. Choose a slice typical enough that what you learn migrating it transfers to the rest, rather than an oddball that teaches you nothing reusable.
Once a few representative slices have gone smoothly, the remaining migrations become routine, and the risky ones can ride on a pattern the team already trusts.
Common traps#
- A "smart" routing layer. When the router starts holding business logic, it becomes a third system you now have to migrate. Keep it dumb.
- The new service depending on the old one indefinitely. A temporary dependency during migration is fine; a permanent one means you never actually strangled anything.
- Never deleting migrated code. If the old paths linger after their traffic is gone, you have doubled your surface area instead of reducing it.
- Calling it Strangler Fig while doing big-bang slices. Migrating half the system in one move is just a cutover wearing a costume. Keep the slices genuinely small.
Signals it's working#
- The share of traffic hitting the old system drops steadily, slice by slice.
- Each migrated route ships independently and can be rolled back on its own.
- The new service grows while the old one shrinks, rather than both growing.
How it ends#
It ends with a deletion. The old system stops receiving traffic, you remove it and its routing entries, and what remains is the new system plus, if you still need it, a now-trivial router. If you cannot bring yourself to delete the old code, the play is not done.