How to Implement the Strangler Fig Pattern

ModernLift · ·9 min read
Part 5 of 8

Implementing the strangler fig pattern follows a repeating loop: map the system's seams and carve it into slices, stand up the routing facade in front of the legacy system, pick a first slice that is low-risk but high-learning, build it behind an anti-corruption layer, prove it equivalent to the legacy behavior, ramp its traffic at the facade with rollback ready, retire the legacy code it replaced, and repeat — until nothing routes to legacy and the system is strangled.

Parts 3 and 4 gave you the two structural pieces of the strangler fig pattern: the facade that routes traffic, and the anti-corruption layer that keeps the legacy model from leaking into clean new code. This part puts them to work. Implementing the pattern isn’t a one-time setup — it’s a loop you run once per slice, dozens of times across a program, until the legacy system is empty. Here is the loop, in the order you actually run it.

Step 0: Map the seams and carve the slices

Before any facade or any code, you need two things: a map of the system’s seams — the points where traffic can be intercepted and split — and a carving of the system into slices, the vertical threads of business capability you’ll migrate one at a time.

This is discovery work, and it’s the part that most determines whether the rest goes well. A good slice boundary follows a business capability and falls along a seam that doesn’t entangle the slice’s data and contracts with its neighbors — so the slice can be routed, validated, and rolled back on its own. A bad boundary leaks complexity across the seam: a slice whose data is hopelessly interwoven with three others can’t be promoted or rolled back alone, and that’s a design smell worth catching before a line of code is written.

This is also where AI-accelerated analysis earns its place — reading the codebase, data, and dependencies end to end to surface the seams and the domain rules nobody wrote down, captured as living specs the team owns. The slice plan is grounded in the actual code, not an idealized diagram of it.

Step 1: Stand up the facade

With slices identified, install the strangler facade in front of the legacy system at the seam you’ll use. On day one it routes essentially everything to legacy — it’s a near-transparent pass-through, and the system behaves exactly as before. That’s the correct starting state. You’ve added the control surface without changing behavior, and now every future promotion is a routing change rather than a deploy.

Standing up the facade early, before the first slice is ready, is deliberate: it lets you confirm the routing layer itself is reliable and low-latency against real traffic while the stakes are still zero.

Step 2: Pick the first slice — low-risk, high-learning

The temptation is to start with the most valuable or most painful capability. Resist it. The first slice’s real job is to prove the whole apparatus — facade, anti-corruption layer, parity pipeline, ramp, rollback — works on your actual system, before you trust it with something critical.

So choose a slice that is low-risk but high-learning: a clean seam, a modest blast radius if it goes wrong, and enough realistic complexity that getting it across exercises every part of the machine. Prove the loop on something forgiving. Then point it at the capabilities that matter.

Step 3: Build the slice behind an anti-corruption layer

Build the modern implementation of the slice, and put an anti-corruption layer between it and any legacy system it still has to read from or write to. Inside the slice, the model is clean; the ACL translates to and from the legacy shapes at the boundary. This is what keeps the slice from quietly inheriting the old model’s corruption while the legacy database is still live underneath it.

Step 4: Prove parity before you route anything

A slice does not take live traffic because it passed its unit tests. It takes live traffic because it has been proven to behave like the legacy system it replaces — same outputs, same data effects, same handling of the edge cases the spec never captured. The mechanism is shadow traffic: the facade mirrors real production requests to the slice in parallel, the legacy system keeps serving every live response, and the two outputs are compared. Where they diverge, you have a ticket to resolve before promotion.

This series treats parity as a gate, not a topic — the full mechanism has its own home in What Is Parity Validation and the parts that follow it. The rule for this loop is simple: a slice clears its parity gate, or it doesn’t ramp.

Step 5: Ramp the traffic — don’t switch it

A proven slice still doesn’t go to 100% at once. Live traffic is the first time the slice’s output is the one returned and its writes are the ones that stick, and the ramp is where you find what shadowing couldn’t — real downstream consumers, real load, real interaction effects.

So you ramp at the facade: a small canary first, then a stepped increase, watching error rate, latency, and divergence at each stage, holding long enough to see the system’s real rhythms — including the periodic behavior that only appears at month-end or quarter-close. You advance on green signals, not on a calendar. The control surface for this is the feature flag, and the safety behind it is per-slice rollback; Part 7 covers both at the slice level, and the parity series goes deeper still.

Step 6: Retire the legacy slice — actually remove it

When the slice has held full traffic through a real settle window, finish the job: route the last of the slice’s traffic to the modern implementation, delete the dead legacy code path, and remove the now-unneeded routing branch and any anti-corruption layer that only existed to bridge that retired path. The legacy parachute stays attached until the slice has settled — and not one minute longer than it’s needed.

This step is the one teams skip, and skipping it is how a clean migration slowly turns back into a tangle. A slice isn’t done when the new code works. It’s done when the old code is gone.

Step 7: Repeat — and let velocity compound

Then you do it again with the next slice. The loop is the whole method, and it has a compounding benefit: each slice teaches the team and sharpens the toolchain for the next, so velocity tends to improve across a program as patterns repeat and the system becomes better understood — the opposite of a rewrite, whose uncertainty grows the deeper in you go.

StepActionOutput
0Map seams, carve slicesSlice plan grounded in real code
1Stand up facadeControl surface, behavior unchanged
2Pick first sliceLow-risk, high-learning target
3Build behind ACLClean slice, model isolated
4Prove parityEvidence the slice matches legacy
5Ramp trafficGradual, reversible promotion
6Retire legacy pathDead code deleted, facade collapsed
7RepeatCompounding velocity

The loop is simple, the seams are hard

The loop reads cleanly, and that clarity is real — but it hides where the genuine difficulty lives. It’s not in the steps; it’s in the slice boundaries. A tangled system resists clean carving, and a boundary drawn through entangled state will leak complexity no matter how well you run the rest of the loop. Expect the carving to be iterative, expect some boundaries to be redrawn after the first attempt teaches you something, and budget for the discovery work that finds the seams. A team that treats slicing as a quick upfront diagram rather than the central design problem will feel the loop fight them at every step.

Where this leads

Run this loop faithfully and the legacy system is strangled on schedule. The trouble is that programs don’t always run it faithfully — they stall, and strangler fig migrations stall in recognizable, avoidable ways. Part 6, Why Strangler Fig Projects Stall, names the failure modes specific to this pattern — the strangle that never finishes, the facade that becomes permanent, the boundaries that were wrong from the start — so you can see them coming before they cost you the program.

Frequently asked questions

What should the first strangler-fig slice be?
Something low-risk but high-learning — a slice with a clean seam, modest blast radius, and enough realistic complexity to exercise the facade, the anti-corruption layer, and the parity pipeline end to end. The goal of the first slice isn't maximum business value; it's to prove the whole apparatus works on your actual system before you bet a critical capability on it.
How do you decide where the slice boundaries go?
Along business capabilities and the seams in the system that don't entangle a slice's data and contracts with its neighbors. A good slice is a vertical thread of behavior that can be routed, validated, and rolled back on its own. Slice boundaries are a discovery-phase decision, because a badly drawn boundary leaks complexity across the seam and can make a slice impossible to roll back alone.
How do you know when a strangler fig migration is finished?
When no production traffic routes to the legacy system, every slice has been promoted and settled, and the legacy code and its anti-corruption layers have been removed. The migration isn't done when the new system works — it's done when the old one is gone and the facade's routing for those seams has collapsed away. Stopping short leaves two systems to maintain forever.
All 8 parts of The Strangler Fig Pattern & Incremental Migration →