The Strangler Fig Pattern: Incrementally Replacing a Legacy System (Visualized)
The strangler fig pattern replaces a legacy system gradually: a routing facade sends some requests to new services while the old app handles the rest, until the legacy is fully "strangled" and removed. This guide covers the facade, capability-by-capability migration, data coexistence, and when to use it โ with live animations.
The strangler fig pattern is a strategy for replacing a legacy system incrementally rather than all at once. It is named after the strangler fig vine, which germinates in the canopy of a host tree, grows roots downward around its trunk, and slowly takes over โ until the original tree dies away and the fig stands in its place. In software, a routing facade gradually takes over the legacy application's responsibilities, capability by capability, until the old system can be safely switched off.
The term was coined by Martin Fowler after observing strangler figs in Australia. The pattern lets you modernize a critical system that you cannot afford to take offline, replacing it piece by piece while it keeps serving real traffic the entire time.
The Core Idea: A Routing Facade
At the heart of the pattern sits a routing facade โ often an API gateway, reverse proxy, or thin routing layer โ placed in front of the legacy application. Every client request hits the facade first. The facade inspects each request and decides: send it to a new service that has taken over that capability, or pass it through to the legacy system that still owns everything else. Clients are unaware that anything is changing behind the single address they call.
Migration then proceeds one capability at a time. You build a new service for a single feature, flip the facade's routing rule for that feature's endpoints, verify it in production, and move on. The legacy app shrinks with every flip until it owns nothing and can be deleted.
Why Big-Bang Rewrites Fail
The tempting alternative is the big-bang rewrite: freeze the old system, build a complete replacement in parallel, then swap them in one cutover. This almost always goes badly. The new system has to reach feature parity with years of accumulated, often undocumented, legacy behavior before it can ship anything โ so there is no value delivered until the very end, and no production feedback along the way. Meanwhile the old system keeps changing, so you are chasing a moving target. The cutover itself is enormously risky: one event, one rollback plan, everything at stake at once.
The strangler fig pattern inverts all of this. Each migrated capability ships independently, delivers value immediately, and gets validated in production with a small blast radius. If one slice goes wrong, you reroute that one capability back to the legacy app at the facade โ not the whole system.
| Big-bang rewrite | Strangler fig | |
|---|---|---|
| Delivery | All value at the end | Value with each migrated slice |
| Risk | One huge cutover | Small, reversible steps |
| Rollback | Revert the entire system | Reroute one capability at the facade |
| Feedback | None until launch | Continuous, in production |
| Moving target | Legacy keeps changing under you | Migrate and freeze slice by slice |
| Time to first ship | Months to years | Days to weeks |
Carving Out One Capability
A single migration step has a predictable rhythm. Pick a capability with clear boundaries โ say, the search feature. Build a new service that implements it. Update the facade so requests to /search go to the new service while everything else still hits the legacy app. Often you start by routing a small percentage of traffic (a canary), watch metrics, then ramp to 100%. Once you are confident, delete the corresponding code path from the legacy system.
The Hard Part: Data and Coexistence
Routing requests is the easy part; sharing data during the transition is where strangler migrations get hard. While both old and new own pieces of the same domain, they often need the same data. Common tactics: have the new service read from the legacy database directly at first; replicate or synchronize data between the two stores; or write to both during a transition window. An anti-corruption layer translates between the legacy data model and the new service's cleaner model so the old design does not leak into the new code.
You also must handle features that straddle the boundary โ a transaction that touches both a migrated and a not-yet-migrated capability. These are the slices to migrate together, or to leave for last. Choosing migration order to minimize these cross-cutting dependencies is most of the design work.
The Monolith Shrinks as Services Grow
The end state is reached gradually. With every capability moved out, the legacy codebase shrinks, its database carries fewer tables, and its operational footprint drops โ while the constellation of new services grows. Eventually the legacy app owns nothing, and the facade can be simplified or removed entirely.
A Routing Rule in Practice
The facade rule itself is usually simple โ a path-based route on an API gateway or reverse proxy. The new service gets a specific path; a catch-all sends everything else to the legacy upstream.
# API gateway / reverse-proxy routing at the facade
routes:
# Already migrated: send to the new service
- path: /search
upstream: search-service:8080
canary_weight: 100 # ramp 5 -> 25 -> 100 while watching metrics
- path: /recommendations
upstream: recs-service:8080
canary_weight: 25 # still validating in production
# Everything else still belongs to the legacy app
- path: /
upstream: legacy-monolith:9000
When to Use It (and When Not To)
Reach for the strangler fig pattern when you must modernize a large, business-critical system that cannot go offline, when the legacy codebase is too big or risky to rewrite at once, and when capabilities have boundaries clean enough to peel off. It shines for monolith-to-microservices migrations, platform or language changes, and replacing a vendor system.
It is overkill for small applications where a direct rewrite is genuinely cheap, and it struggles when the legacy system is so tightly coupled that no capability can be cleanly extracted โ there you may need refactoring inside the monolith first to create seams. The pattern also has real costs: you maintain two systems and a facade for the duration, so the migration must actually finish. A strangler migration that stalls halfway leaves you with the worst of both worlds.
Frequently Asked Questions
What is the difference between the strangler fig pattern and a big-bang rewrite?
A big-bang rewrite builds a complete replacement in parallel and switches over in a single, high-risk cutover with no value delivered until the end. The strangler fig pattern migrates one capability at a time behind a routing facade, so each slice ships to production independently, delivers value early, and can be rolled back by simply rerouting that one capability back to the legacy system.
What role does an API gateway play in the strangler fig pattern?
The API gateway (or reverse proxy) is the routing facade. It gives clients one stable address and decides, per request, whether to forward to a new service or to the legacy app. Because all routing logic lives there, you migrate a capability by changing a gateway rule โ including canary ramps โ without touching clients, which is what makes each step small and reversible.
What is the hardest part of a strangler fig migration?
Data, not routing. During the transition the old and new systems often need the same data, forcing decisions about shared databases, replication, or dual writes, plus an anti-corruption layer to keep the legacy model from leaking into the new design. Choosing a migration order that minimizes capabilities straddling the boundary is the core design challenge.
Don't rewrite the system โ strangle it. Put a facade in front, move one capability at a time, and let the legacy quietly fade until there is nothing left to switch off.
โ alokknight Engineering
