Strangler Fig
Strangler Fig is a migration pattern: instead of rewriting a legacy system in one risky big-bang, you wrap it with a routing layer that incrementally redirects new features (or specific endpoints) to a new implementation, while the old system keeps running. Over time, the new system 'strangles' the old one — taking on more responsibility — until the legacy can be retired entirely. The pattern, named by Martin Fowler after a strangler fig vine that grows around and eventually replaces its host tree, trades a high-risk big-bang for a series of low-risk, reversible steps.
How it works
The Strangler Fig pattern, introduced by Martin Fowler in 2004, is the canonical strategy for incrementally replacing a legacy system. The name comes from the strangler fig vine: it sprouts in the canopy of a host tree, drops roots to the ground, and over decades the vine grows thicker and thicker until it eventually replaces — strangles — the host tree, leaving only the vine behind.
The software version works the same way:
- Put a routing façade in front of the legacy system. This façade (an API gateway, reverse proxy, or feature router) initially forwards 100% of traffic to the legacy.
- Build the new implementation of one slice of functionality — say, the
GET /orders/{id}endpoint, or the search feature. - Reconfigure the façade to route that one slice to the new implementation. Everything else still goes to legacy.
- Observe. If the new slice misbehaves, flip the routing back. Migration is reversible.
- Repeat for the next slice — endpoint by endpoint, feature by feature, domain by domain.
- Eventually, the legacy system handles nothing. Decommission it.
The key insight is that the routing façade gives you a reversible migration. Every step is small enough to validate and cheap enough to roll back. This is the opposite of a big-bang rewrite, where you bet the company on a single cutover date.
Choosing what to migrate first matters. Good first slices:
- Read-only endpoints — they don't have to handle writes, transactions, or consistency. Easy to migrate, easy to roll back.
- New features — instead of bolting new code onto legacy, build the new feature in the new system, route to it from day one. Legacy never has to learn about it.
- Low-traffic, low-risk endpoints — to validate the pipeline (deployment, monitoring, routing) before high-stakes migrations.
- A bounded subdomain — e.g., the entire 'orders' subdomain, including reads and writes, fully owned by the new service. This avoids the worst case: write split between legacy and new, requiring bidirectional data sync.
The hardest migrations are when data is shared between legacy and new — both must read and write the same data. Patterns for this:
- Change data capture (CDC): stream legacy DB changes to the new service's store.
- Dual writes: write to both, accept inconsistency temporarily.
- Anti-corruption layer: translate between legacy and new domain models.
- Expand/Contract: expand schema to support both, migrate, then contract.
The migration is done when the legacy system can be turned off without anyone noticing.
Big-bang rewrites feel heroic but fail often: the new system must reach feature parity before cutover, and the legacy keeps changing underneath, so 'parity' is a moving target. Years pass, the new system is never quite done, the team burns out, the business loses patience, and the rewrite is cancelled. Strangler Fig inverts this: ship value every week, validate every step, roll back when wrong. The total time may be similar, but the risk profile is vastly better. Joel Spolsky's famous 2000 essay 'Things You Should Never Do' warned against Netscape's big-bang rewrite; Strangler Fig is the answer to that warning.
The façade is the linchpin. Common implementations:
- Reverse proxy with path-based routing — nginx, HAProxy, Envoy:
location /orders/ { proxy_pass new-svc; } location / { proxy_pass legacy; }. Simple and battle-tested. - API gateway (Kong, AWS API Gateway, Apigee) — adds auth, rate limiting, observability.
- Service mesh (Istio, Linkerd) — if you're already in a mesh, virtual services + destination rules do this elegantly.
- Feature flags (LaunchDarkly, Unleash) — route per-request based on user ID, percentage, or environment. Useful for canary migrations: 1% of users go to new, 99% to legacy.
- DNS / traffic manager — for migrating entire domains.
Whatever you choose, the façade must support:
- Per-route decisions (not just 'all or nothing').
- Gradual rollout (1%, 10%, 50%, 100%).
- Fast rollback (flip a config, not redeploy).
- Observability (per-route error rates, latency, traffic) so you can spot regressions.
- Header/cookie-based routing for canaries.
Why does Strangler Fig generally produce lower migration risk than a big-bang rewrite?
Pick one answer.
You are migrating an e-commerce monolith. Which endpoint is the BEST candidate to migrate first under Strangler Fig?
Pick one answer.
Engineering mental model
Mental model. Think of Strangler Fig as a deliberate boundary in a system. The boundary exists because something becomes harder to manage when everything is done in one place: latency, scale, failure isolation, consistency, cost, or team ownership. The useful question is not “what does Strangler Fig mean?” but “what pressure makes this boundary worth introducing, and what new failure mode does it create?”
Before choosing Strangler Fig, name the workload, the critical user path, the dominant bottleneck, the failure you are trying to absorb, and the trade-off you are willing to accept. If you cannot name those five things, the design is probably premature.
// Pseudocode
request = receive()
result = strangler_fig(request)
return result
// Production questions:
// 1. What happens on timeout?
// 2. Can this operation be retried safely?
// 3. What is the bottleneck?Back-of-the-envelope reasoning
Numerical lens: write down traffic, payload size, read/write ratio, peak multiplier and durability target before choosing a component. The numbers should justify the architecture.
Interactive thought experiment: Strangler Fig
Change the variables below and predict what breaks first in Strangler Fig. The production lab can later reuse these same inputs.
Change one variable at a time. Predict the failure mode first, then move the slider and see whether your mental model matches the simplified system response.
If you are stuck on Strangler Fig, start by drawing the request path and marking every network hop, stateful component, queue, cache and failure boundary. Then estimate where the system will saturate.
You increase traffic by 10× in a system using Strangler Fig. What should you inspect first?
Pick one answer.
Which statement is the safest engineering habit when using Strangler Fig?
Pick one answer.
You have dashboards for traffic, latency, errors and saturation. You can change the architecture, but every change has operational cost.
Production scenario: your system uses Strangler Fig, traffic suddenly spikes, and p99 latency doubles. What is your first move?
Interview drill
Answer this without notes: When would you choose Strangler Fig, and when would you intentionally avoid it? Mention at least one bottleneck it addresses, one failure mode it introduces, and one alternative. Then quantify the workload you are designing for.
A useful engineering lens for Strangler Fig: define the problem it solves, the simpler design that fails first, the constraint that forces you to introduce this concept, and the new failure modes the concept creates.
Numerical sanity check
Back-of-the-envelope reasoning beats fake precision. State your traffic, payload, concurrency and growth assumptions explicitly, then calculate enough to know whether the current architecture is orders of magnitude away from the target.
Do not optimize for a memorized definition. Reason from the workload and failure mode.
Imagine the simplest version of a system using Strangler Fig. What breaks first as traffic grows by 10×, and what would you change before reaching 100×?
Pick one answer.
What you gain, what you pay
- +Migration is incremental and reversible — each step can be rolled back independently.
- +Value is delivered continuously — each migrated slice is a shippable improvement.
- +Legacy keeps running, so the business is never blocked.
- +Team learns the new stack by migrating real code, not by maintaining parity in a vacuum.
- +Observability of the new system is built up gradually, with real traffic, before it takes over.
- −Running two systems in parallel costs money and operational complexity (monitoring both, deploying both, on-call for both).
- −Data may need to be synchronized between legacy and new (CDC, dual writes), which is its own complexity.
- −Routing façade must be reliable — it's now a critical single point of failure.
- −Migration can stall — 'we're 60% migrated' is a common plateau that's hard to push past.
How this breaks in production
- Routing façade itself fails — every request fails. The façade must be highly available.
- Data divergence between legacy and new — writes go to both, but one lags; users see inconsistent state.
- Migration stalls at 60-70% — the hardest endpoints are left for last and never get migrated.
- Cross-cutting concerns (auth, logging) drift between legacy and new — debugging becomes harder.
- Latency regression — new service adds an extra hop through the façade that wasn't there before.
- The legacy system can't be safely turned off because nobody is sure what still depends on it.
Don't fall into these traps
- •Migrating write paths before read paths — writes have consistency and transaction concerns that reads don't.
- •Not building observability into the façade — you can't migrate safely if you can't compare error rates side-by-side.
- •Treating 'migrated' as binary — gradual, percentage-based rollout (1% → 10% → 50% → 100%) is much safer.
- •Forgetting to plan legacy decommissioning — the goal isn't 'migrate'; it's 'turn off the legacy.'
- •Not budgeting for the dual-running period — running two systems indefinitely is expensive.
- •Choosing the hardest slice first — start with reads and new features, save the gnarly write paths for when the pipeline is proven.
Real systems using this
How real systems implement this
- Martin Fowler's original write-up (2004) — Fowler coined the pattern while observing a client gradually replacing a legacy system via a routing façade. The article is the canonical reference cited in nearly every successful monolith-decomposition story.
- Amazon retail platform migration (2001-2006) — Amazon's well-known shift from a monolithic retail service to a service-oriented architecture followed Strangler Fig principles: services were extracted behind routing layers while the monolith kept serving everything else.
- GitHub's gradual migration off Rails monolith — GitHub extracted services (e.g., the pull-request UI, code search) incrementally behind routing, while the Rails monolith continued serving most of the application — a multi-year Strangler Fig migration.
Practice saying it out loud
- Q1You have a 10-year-old monolith handling all of your company's traffic. How would you migrate it to microservices without taking the business offline?
- Q2What is Strangler Fig? Where does the name come from, and what are the key ingredients you need to apply it?
- Q3How do you handle data that needs to be read and written by both legacy and new services during migration?
- Q4How do you choose what to migrate first under Strangler Fig? What makes a good first slice?
- Q5When does Strangler Fig NOT make sense? When would you actually prefer a big-bang rewrite?
Further reading & references
Core explanations are original NO CAP material. External references are provided for deeper study and standards.
What next?
Mark as understood once the mental model clicks.
Next recommended
Anti-Corruption Layer