What Is the Strangler Fig Pattern?

What Is the Strangler Fig Pattern?

What Is the Strangler Fig Pattern?

A complete, book-quality guide to safely replacing an old legacy system piece by piece — the same way a strangler fig plant grows around an oak tree until the oak can be removed and the fig stands on its own.

01 · Foundations

Introduction & History

A gentle way to replace what a big bang would blow up.

Picture a giant old oak tree standing in a forest. A tiny seed from a fig plant lands in a crack high up in the oak’s bark. The fig seed sprouts, and instead of growing in the ground like a normal plant, it sends roots downward, wrapping around the oak’s trunk. Slowly, year after year, the fig’s roots thicken and spread, surrounding more and more of the old oak. Eventually, the fig has built an entire new structure around the original tree. At that point, the old oak inside can die and completely rot away — and the fig tree, now fully grown, stands on its own, in exactly the same spot, without the forest ever being without a tree in that spot. This is a real phenomenon in nature, and it happens gradually, safely, with the old structure supporting things right up until the new structure is ready to take over completely.

This is exactly the mental picture behind the Strangler Fig Pattern in software: instead of tearing down an old, critical software system all at once and replacing it with a new one (a risky “big bang” rewrite), you build the new system gradually around and alongside the old one, redirecting small pieces of functionality to the new system bit by bit, until one day the old system does nothing at all — and can be safely switched off.

1.1 · A short history

The term was coined by renowned software architect Martin Fowler in 2004, in a short but highly influential article. Fowler had seen many companies attempt full rewrites of important legacy systems, and he noticed a repeating disaster pattern: teams would spend a year or two building a shiny new replacement, only to discover that the old system had grown so many small, undocumented features and edge cases over the years that the new system could never fully catch up. Meanwhile, the business still needed new features added to the old system while the rewrite was happening, making the target keep moving. Many of these full-rewrite projects were cancelled, hugely over budget, or simply failed.

Fowler observed a different, safer approach some teams were using instinctively, inspired by the strangler fig plant he had seen described by biologists and travel writers during a trip to Australia. He wrote it up as a named pattern, and it has since become one of the most widely recommended strategies in the entire software industry for modernising legacy systems — especially as companies began breaking monolithic applications into microservices from around 2012 onward.

Evolution of Legacy Modernization Thinking 1990s – 2000s Big Bang Rewrites frequent overruns, cancelled rewrites 2004 Martin Fowler names the “strangler fig application” 2010s Rise of Microservices strangler fig becomes the standard migration 2015 – present Cloud & Meshes API gateways, service meshes make it easy Industry thinking shifted from risky full rewrites to gradual, well-supported incremental migration.
Figure 1 — How industry thinking evolved from risky full rewrites to the gradual, well-supported Strangler Fig approach.

In one line

The Strangler Fig Pattern replaces a legacy system by quietly growing a new one around it, one small feature at a time, until the old system can be safely retired — without ever having to switch everything off at once.

02 · Motivation

The Problem & Motivation

Why a full rewrite is so tempting — and so dangerous.

Let’s use one running example throughout this whole tutorial: an old, 15-year-old Order Management System at a fictional retail company. It is a single, enormous application (a monolith) written in an older version of Java, running on one big server, using one giant database. It handles order creation, payments, inventory, shipping, returns, and reporting — all bundled together in one codebase that almost nobody fully understands anymore, because the original developers left the company years ago.

Beginner example — the family house

Imagine your family has lived in the same old house for 40 years. Wires are outdated, some rooms have plumbing issues, and the layout doesn’t fit how you live today. You have two choices: (1) move everyone out, bulldoze the house, and build a brand new one from scratch (risky — where do you live during construction? What if you forgot to plan for something the old house quietly handled, like where the water shutoff valve is?), or (2) keep living in the house while slowly renovating one room at a time — fix the kitchen first while still using the rest of the house, then the bathroom, then the bedrooms — until eventually every part has been rebuilt, without ever having to live outside or stop using the house.

2.1 · Why a “big bang” rewrite is so risky

  • Scope creep and hidden knowledge: old systems accumulate years of small business rules, edge cases, and bug fixes that were never written down anywhere except in the old code itself. A rewrite team almost always underestimates this “hidden” functionality.
  • The business doesn’t stop moving: while your team spends 18 months rewriting the system, the business still needs new features, and those get added to the old system too — meaning the rewrite target keeps changing, and the new system is often already outdated on the day it launches.
  • All-or-nothing launch risk: with a big bang rewrite, one launch day switches everything at once. If anything is wrong, the blast radius is the entire business, and rolling back is often extremely difficult once data has flowed into the new system.
  • No incremental value: stakeholders (and the business) get zero benefit until the entire rewrite is 100% finished — which could be years away, making it hard to justify continued investment.
Big Bang Rewrite — Risk Profile Start rewriting everything 18–24 months no visible progress no incremental value Launch Day Success finally live Failure massive outage hard rollback Long invisible development, then a single high-stakes event with a very expensive downside.
Figure 2 — The big bang rewrite risk profile: long periods of invisible progress followed by one very high-stakes launch event.

2.2 · The motivation for the Strangler Fig Pattern

The Strangler Fig Pattern exists to solve exactly this problem: it lets you replace a legacy system piece by piece, feature by feature, while the old system keeps running and keeps serving the parts not yet migrated. Users (and the business) barely notice the change happening, because at every point in time, the system as a whole keeps working — just with a growing slice of it powered by new code instead of old code.

03 · Vocabulary

Core Concepts

A shared vocabulary before we go any deeper.

Legacy System Strangler Fig Application Facade / Router Incremental Migration Seam / Vertical Slice Anti-Corruption Layer

3.1 · Legacy System

What it is: an existing, usually older, software system that is still in active use, often built with outdated technology, and often difficult to change safely.

Why it exists: every successful system eventually becomes “legacy” simply by surviving long enough — technology changes, the original team moves on, and requirements evolve far beyond what the system was first designed for.

Where it’s used: banking cores, airline reservation systems, government systems, large retail order-management platforms, insurance claim systems — legacy systems exist in almost every large, long-running company.

Analogy — grandma’s recipe card

A legacy system is like a very old, well-worn recipe card passed down in a family. It works, everyone relies on it, but nobody remembers exactly why grandma added that one strange step — and everyone is scared to remove it in case it was actually important.

3.2 · Strangler Fig Application

What it is: a migration strategy where a new system is built gradually around an old one, capturing more and more functionality over time, until the old system can be safely retired.

Why it exists: to reduce the risk, cost, and disruption of legacy modernisation by making it incremental and reversible at every step, instead of one giant, irreversible leap.

Where it’s used: any large-scale migration — legacy monolith to microservices, old on-premise system to cloud-native system, outdated framework to a modern one.

3.3 · Facade / Routing Layer

What it is: a component placed in front of both the old and new systems that decides, for each incoming request, whether to send it to the legacy system or the new system.

Why it exists: without this “traffic director,” clients (web apps, mobile apps, other services) would need to know themselves which system currently owns which feature — which is fragile and gets more complicated with every migration step. The facade hides this complexity completely.

Analogy — the hotel front desk

Think of a hotel’s front desk during a renovation. Guests don’t need to know which specific rooms or floors are finished and which are still being worked on — they simply tell the front desk what they need, and the front desk (the facade) quietly directs them to the right, ready part of the building.

3.4 · Incremental Migration

What it is: the practice of moving functionality from an old system to a new one in small, independent, low-risk steps, rather than all at once.

Why it exists: small steps are individually easy to test, easy to roll back, and easy to reason about — reducing the blast radius of any single mistake to a tiny fraction of the whole system.

3.5 · Vertical Slice / Seam

What it is: a natural “cut point” within the legacy system where a specific feature or capability can be extracted with minimal entanglement with the rest of the code — often called a “seam” (a term popularised by Michael Feathers in his classic book on legacy code).

Why it exists: not every part of an old system is equally easy to pull apart. Some features are naturally more independent, while others are deeply tangled with other logic. Finding good seams is the key skill in planning a successful strangler fig migration.

Analogy — the LEGO castle

If you’re taking apart an old LEGO castle to rebuild parts of it, you look for natural joints — a tower that’s only loosely connected is a good “seam” to detach first. A wall that’s deeply interlocked with five other pieces is a much harder, riskier place to start.

3.6 · Anti-Corruption Layer (ACL)

What it is: a thin translation layer placed between the new system and the old legacy system, converting data and calls between the two so that the new system’s clean design isn’t “corrupted” by the old system’s messy data formats or outdated concepts.

Why it exists: legacy systems often have inconsistent, poorly named, or outdated data structures. If the new system talks to the legacy system directly without any translation, those old bad habits leak into the new, clean codebase. This term comes from Domain-Driven Design (a broader software design approach) and is a key supporting tool for strangler fig migrations.

Analogy — the business meeting translator

It’s like having a translator at a business meeting between two companies that speak different languages and use different units of measurement. The translator (the ACL) converts everything cleanly, so neither side has to awkwardly adapt their own way of speaking just to understand the other.

04 · Structure

Architecture & Components

Three parts, one facade, and a decision rule.

4.1 · The three core components

ComponentRole
Legacy SystemThe existing old application, still fully functional, gradually shrinking in responsibility over time.
New System(s)The new application(s) or microservice(s) being built to take over specific pieces of functionality, one at a time.
Facade / Strangler ProxyThe routing layer that inspects each incoming request and decides, based on current migration progress, whether to forward it to the legacy system or the new system.
The Core Strangler Fig Architecture Client web · mobile · API Strangler Facade route by feature Legacy Order Management System order creation · not yet migrated New Payments Service already migrated New Inventory Service already migrated /orders/* /payments/* /inventory/* The facade routes each request to whichever system currently owns that capability — legacy or new.
Figure 3 — The core Strangler Fig architecture. The facade routes each request to whichever system currently owns that piece of functionality — legacy or new.

4.2 · Common facade implementations

Implementation ChoiceDescriptionTypical Use Case
Reverse Proxy (e.g., NGINX, HAProxy)Routes HTTP requests based on URL path, header, or other rules, at the network level.Web applications, REST APIs organised by URL path (e.g., /api/payments/* → new service).
API Gateway (e.g., Kong, AWS API Gateway, Apigee)A managed, feature-rich routing layer with built-in support for authentication, rate limiting, and traffic splitting.Cloud-native microservices migrations.
Application-level FacadeA thin custom application or code module that receives calls and internally decides which backend to call.Cases needing complex business logic to decide routing (e.g., “route by customer ID region”).
Feature Flags / Toggle RoutingConfiguration-driven switches that control which system handles a request, often changeable without a deployment.Fine-grained, percentage-based rollout (e.g., “10% of traffic to new system”).

Reverse Proxy

Cheap, fast, mature. Perfect first facade if you already run NGINX or HAProxy.

API Gateway

Batteries included. Auth, quotas, rate limits, traffic splits — all in one product.

App-level Facade

Ultimate flexibility. Route by any custom rule you can write in code.

Feature Flags

Live, config-driven cutovers with no redeploy. The safest of them all when done well.

05 · Mechanics

Internal Working (Step by Step)

The migration engine, expressed as seven repeatable steps.

Let’s walk through exactly how a strangler fig migration proceeds, in concrete steps, using our Order Management System example.

  1. Identify a Seam

    The team studies the legacy system and identifies “Inventory Management” as a relatively self-contained feature — it has clear inputs (product ID, quantity) and outputs (updated stock count), and isn’t too deeply tangled with unrelated logic.

  2. Build the Facade

    Before touching any functionality, the team places a facade (say, an API Gateway) in front of the legacy system. Initially, the facade forwards 100% of all traffic straight through to the legacy system, unchanged. This step alone is valuable — it proves the facade works correctly with zero behaviour change, before anything risky is attempted.

  3. Build the New Inventory Service

    The team builds a brand new, independent Inventory Service — using modern technology, its own database, proper automated tests, and clean code — that replicates (and ideally improves) the legacy system’s inventory behaviour.

  4. Redirect Traffic for That One Feature

    The facade’s routing rule is updated: requests matching inventory-related URLs or operations are now sent to the New Inventory Service instead of the legacy system. Everything else still goes to the legacy system exactly as before.

  5. Verify, Monitor, and Roll Back If Needed

    The team closely watches error rates, latency, and business metrics (e.g., “does stock still update correctly?”). If anything looks wrong, the facade’s routing rule can simply be flipped back to the legacy system in seconds — a very low-risk rollback, unlike a big bang rewrite’s rollback.

  6. Repeat for the Next Feature

    Once Inventory is stable and proven in production, the team picks the next seam (say, Payments), and repeats steps 1–5. Over months (sometimes years, for very large systems), more and more functionality moves to new systems.

  7. Retire the Legacy System

    Once the facade routes 100% of traffic to new systems and the legacy system receives zero real traffic, the team can decommission it entirely — turning off servers, archiving the old codebase, and closing the old database (after final data migration/archival).

The Seven-Step Migration Cycle 1. Identify a Seam e.g. Inventory 2. Add Facade 100% traffic to legacy 3. Build New Service for that one feature 4. Redirect Traffic via facade route rule 5. Verify & Monitor roll back if needed 6. More features to migrate? yes → loop · no → retire 7. Retire Legacy decommission old code yes · loop no Each cycle migrates one feature; the loop continues until nothing is left on legacy. small steps · each one independently testable · each one instantly reversible This cyclical rhythm is what makes strangler fig safe where big bang is not.
Figure 4 — The repeatable, cyclical process at the heart of every strangler fig migration.

Java example — a simple facade routing rule

StranglerFacadeController.java
@RestController
public class StranglerFacadeController {

    private final LegacyOrderSystemClient legacyClient;
    private final InventoryServiceClient newInventoryClient;
    private final MigrationFlags migrationFlags; // simple feature-flag lookup

    @GetMapping("/api/inventory/{productId}")
    public ResponseEntity<InventoryResponse> getInventory(@PathVariable String productId) {

        if (migrationFlags.isMigrated("inventory")) {
            // Route to the NEW system
            return ResponseEntity.ok(newInventoryClient.getStock(productId));
        } else {
            // Route to the OLD legacy system
            return ResponseEntity.ok(legacyClient.getStock(productId));
        }
    }
}

The important idea here: the client calling /api/inventory/{productId} never needs to know or care which system actually served the request. That single migrationFlags.isMigrated("inventory") check is the entire “strangling” mechanism for this feature — and it can be flipped instantly, without redeploying any client code.

06 · Data

Data Flow & Lifecycle

Routing is easy. Keeping two databases in sync is the hard part.

6.1 · The trickiest part — data

Routing requests is the easy part. The much harder part of a real strangler fig migration is data — specifically, keeping the legacy database and the new service’s database in sync while both systems might be operating on overlapping data during the transition period.

6.2 · Common data migration approaches

ApproachHow it worksBest for
Database Synchronisation (dual write)Both legacy and new systems write to their own databases; a syncing mechanism (batch job or event stream) keeps them aligned.Early migration stages, when legacy system still owns the “source of truth”.
Change Data Capture (CDC)A tool (e.g., Debezium) watches the legacy database’s transaction log and streams every change as an event to the new system in near real-time.Reliable, low-latency syncing without modifying legacy application code.
New System as Source of TruthOnce confidence is high, the new system’s database becomes authoritative, and the legacy system reads from it (via API or a reverse sync) if it still needs the data.Later migration stages, as trust in the new system grows.
Change Data Capture — Keeping Both Databases in Sync Client Facade Legacy DB Change Data Capture New Service Update stock request route to legacy (not yet migrated) commit to txn log transaction log change detected stream change as event update new service DB CDC keeps the new system’s data current without touching the legacy application code.
Figure 5 — Using Change Data Capture to keep the new system’s database in sync with the legacy database, before the new system fully takes over writes.

6.3 · Full lifecycle of a single feature’s migration

Each feature moves through a small, well-defined set of states, from “fully owned by legacy” on the left to “fully migrated, legacy code removed” on the right. Advancing between states requires evidence — not just optimism.

Feature Migration Lifecycle Owned by Legacy 100% legacy Shadow Mode new runs silently Partial Traffic e.g. 10% to new Full Traffic 100% to new Legacy Code Removed — Migration Complete build new start rollout confidence grows A feature moves right, one confidence step at a time.
Figure 6 — The lifecycle states a single feature passes through, from fully legacy-owned to fully migrated.

Shadow Mode / Dark Launching

What it is: running the new system in parallel with the legacy system, sending it a copy of real production traffic, but not using its response for real users yet — only comparing its output against the legacy system’s output to check for correctness.

Why it exists: this lets teams gain confidence that the new system behaves correctly under real-world traffic patterns, with zero risk to actual users, before ever routing real decisions to it.

07 · Trade-offs

Advantages, Disadvantages & Trade-offs

Every architecture is a trade — here is exactly what this one costs.

AdvantagesDisadvantages
Much lower risk — failures are isolated to one small migrated feature, not the whole system.Takes longer overall than a (theoretical, idealised) big bang rewrite — often months to years for large systems.
Delivers incremental business value throughout the migration, not just at the very end.Requires maintaining and understanding two systems at once for an extended period, which adds operational complexity.
Rollback is fast and cheap (flip a routing flag) instead of catastrophic.Data synchronisation between old and new systems during the transition is genuinely difficult engineering work.
Teams can learn and adjust their approach as they go, based on real production feedback from earlier migrated pieces.Requires discipline to actually finish — some strangler fig migrations stall halfway and never fully retire the legacy system, leaving permanent complexity.
Works well alongside modern practices like CI/CD, feature flags, and canary releases.Identifying good “seams” in deeply tangled legacy code can itself be a significant, skilled undertaking.

The interview one-liner — the key trade-off

The Strangler Fig Pattern trades migration speed for safety, reversibility, and continuous business value. A big bang rewrite trades safety for the appearance of speed — but that appearance is usually an illusion, since big bang rewrites frequently take longer than planned and often fail outright. In almost all real-world, large-scale legacy modernisation efforts, the Strangler Fig approach’s trade-offs are considered far more acceptable.

08 · Performance

Performance & Scalability

A small extra hop, in exchange for the ability to scale each piece on its own.

8.1 · Facade overhead

Adding a routing facade introduces a small amount of extra network latency (typically low single-digit milliseconds for a well-built reverse proxy or API gateway), since every request now passes through an additional hop. This is usually a negligible cost compared to the benefits, but it should be measured and monitored, especially for latency-sensitive systems.

8.2 · Scaling the new system independently

A major performance benefit emerges as migration progresses: newly built services can be scaled independently of the legacy monolith. If “Inventory” checks spike heavily during a big sale event, the new Inventory Service (once migrated) can be scaled up on its own, without needing to scale the entire legacy monolith (which might be far more expensive or even impossible if the legacy system wasn’t designed to scale horizontally).

Independent Scaling After Migration BEFORE MIGRATION Entire Legacy Monolith must scale together expensive, coarse-grained order · payment · inventory · shipping AFTER MIGRATING INVENTORY Inventory Service scales on its own e.g. 20 pods Remaining Legacy Monolith scales separately order · payment · shipping during a big sale — scale up alone Every migrated capability becomes a separately scalable unit — a benefit that compounds as the migration progresses.
Figure 7 — As pieces are migrated, each can be scaled independently — a scalability benefit that grows throughout the migration.

8.3 · Handling legacy performance bottlenecks during migration

Sometimes the legacy system itself is the bottleneck (e.g., a single-threaded batch process or an old database with poor indexing). Since the Strangler Fig Pattern doesn’t require fixing the legacy system’s internals, teams can prioritise migrating exactly the parts causing the worst bottlenecks first — turning the migration roadmap into a performance improvement roadmap at the same time.

~2–5 mstypical facade overhead
10×headroom on migrated services
0legacy code changes required
09 · Reliability

High Availability & Reliability

The facade quickly becomes the most important box on the diagram.

9.1 · The facade as a new critical component

Once introduced, the facade sits in front of all traffic, to both legacy and new systems. This means the facade itself must now be built for high availability — usually by running multiple redundant instances behind a load balancer, since if the facade goes down, both the legacy and new systems become unreachable, even if they’re both individually healthy.

9.2 · Circuit breakers between facade and backends

What it is: a safety mechanism where the facade detects if a backend (legacy or new) is failing repeatedly, and temporarily stops sending it traffic, failing fast instead of piling up slow, doomed requests.

Why it exists: during migration, the new system is inherently less battle-tested than the old one. A circuit breaker lets the facade automatically fall back to the legacy system if the new system starts misbehaving, without needing a human to intervene immediately.

Circuit Breaker — Automatic Fallback to Legacy Incoming Request via facade Circuit Breaker is new service healthy? New Service (Migrated) healthy → route here Legacy System (Fallback) breaker open → fail safely here healthy too many failures If the new service falters, the facade quietly falls back to the trusted legacy path.
Figure 8 — A circuit breaker lets the facade automatically fall back to the trusted legacy system if a newly migrated service starts failing.

9.3 · Rollback as a first-class reliability feature

Because routing decisions are typically driven by configuration or feature flags rather than code deployments, rolling back a problematic migration step can often be done in seconds, without a new deployment — a huge reliability advantage compared to rolling back a full rewrite’s database migrations or a big bang cutover.

10 · Security

Security

Two systems in flight means two attack surfaces at once — here is how not to leak in between.

10.1 · Consistent authentication across two systems

During migration, both the legacy system and new systems must enforce the same authentication and authorisation rules, or a gap could let requests bypass security checks. Often, the facade itself becomes the best place to centralise authentication (e.g., validating a JWT token once at the facade), so neither the legacy system nor the new system needs to duplicate that logic inconsistently.

10.2 · The Anti-Corruption Layer as a security boundary too

Legacy systems sometimes have outdated, weaker security practices (e.g., storing passwords with old hashing algorithms, or accepting unvalidated input in ways modern systems wouldn’t). The Anti-Corruption Layer (introduced in Section 3) can double as a security boundary, sanitising and validating data flowing from the legacy system into the new system, preventing old vulnerabilities from being “inherited” by the new codebase.

10.3 · Expanded attack surface during migration

Security consideration — more moving parts

While two systems are running simultaneously, there are, technically, more components, more network paths, and more configuration surfaces than before or after the migration — meaning the migration period itself can temporarily increase the attack surface. Teams should apply the same security scanning, access control review, and monitoring to the facade and new services as they do to the legacy system, rather than assuming the legacy system’s existing security posture “covers” the new pieces too.

11 · Observability

Monitoring, Logging & Metrics

You can only migrate what you can measure — and this pattern lives on measurement.

11.1 · Why monitoring matters more than ever during migration

Since correctness confidence is the main thing standing between “still on legacy” and “safe to migrate this feature,” strong observability isn’t optional — it’s the primary tool that tells the team when it’s safe to proceed to the next step.

11.2 · Key things to monitor

  • Routing distribution — what percentage of traffic for each feature is currently going to legacy vs. new? (Useful as a live “migration progress” dashboard.)
  • Error rate comparison — are new-system error rates comparable to or better than legacy error rates for the same operations?
  • Latency comparison (p50/p95/p99) — is the new system at least as fast as the legacy system for equivalent requests?
  • Data drift / reconciliation checks — automated jobs comparing legacy and new databases for the same records, flagging mismatches during dual-write periods.
  • Shadow mode comparison logs — for features in shadow mode, logging every case where the new system’s computed answer differed from the legacy system’s answer, for manual review.
The Migration Dashboard Routing % per feature 58% new 42% legacy live progress bar Error Rate legacy vs new is new ≤ legacy? Latency p50 · p95 · p99 new (red) vs legacy (blue) Data Drift Alerts reconciliation checks ! 3 records mismatched catch silent drift early These four tiles tell the team when it is safe to advance a feature to its next lifecycle state.
Figure 9 — A migration-specific dashboard gives the team the confidence signals needed to safely advance each feature through its lifecycle.

11.3 · Distributed tracing across old and new

Using a trace ID that follows a request even as it potentially crosses the facade into either the legacy system or a new microservice makes it possible to debug issues end-to-end, exactly as covered for microservices generally — this becomes especially valuable when comparing shadow-mode responses or diagnosing subtle behaviour differences.

Pro tip — propagate a trace ID at the facade

Have the facade attach a single unique trace ID (e.g., X-Trace-Id) to every incoming request. Every backend — legacy or new — should log this ID with every log line. A single grep across all logs then reconstructs the full journey of any user complaint, no matter which side handled it.

12 · Cloud

Deployment & Cloud

Modern platforms turn the pattern from a “custom project” into a “configuration exercise”.

12.1 · Cloud-native facades

Modern cloud platforms make the Strangler Fig Pattern much easier than it was in the 2000s. Managed API gateways (AWS API Gateway, Azure API Management, Google Cloud API Gateway) and service mesh technologies (Istio, Linkerd) provide built-in traffic splitting, canary routing, and percentage-based rollout capabilities — meaning teams often don’t need to hand-build a custom facade at all.

12.2 · Canary releases as a migration tool

What it is: gradually shifting a small percentage of traffic (e.g., 1%, then 5%, then 25%, then 100%) to a new version, watching metrics closely at each step.

Why it’s relevant here: the Strangler Fig Pattern’s “redirect traffic for one feature” step is essentially a canary release, except the “new version” is an entirely new system rather than just a new version of the same code.

Canary Rollout — Gradual Traffic Shift 1% traffic to new 10% traffic to new 50% traffic to new 100% fully migrated monitor monitor monitor 24–48 hrs a few days a week complete Each step is a stopping point: metrics healthy → advance, metrics red → roll back with one flag flip.
Figure 10 — A gradual, percentage-based traffic shift — a standard, low-risk way to execute the “redirect traffic” step of a strangler fig migration.

12.3 · Containers and Kubernetes

New services built during a strangler fig migration are typically containerised (Docker) and deployed onto Kubernetes or a similar orchestrator, allowing them to scale independently, be deployed frequently, and be rolled back quickly — all properties that support the incremental, low-risk philosophy of the pattern.

13 · Data plane

Databases, Caching & Load Balancing

A layer-7 router with a data-consistency problem attached.

13.1 · The “shared database” temptation

Early in a migration, it’s tempting for the new service to just read and write directly to the legacy database, to avoid solving the harder data-sync problem. This is usually a mistake long-term (it recreates tight coupling and blocks the legacy database from ever being retired), but it’s sometimes accepted as a deliberate, temporary stepping stone — as long as the team has an explicit plan to move the new service to its own database later.

13.2 · Load balancing across legacy and new

The facade often functions as a specialised load balancer: instead of just spreading traffic evenly across identical servers (traditional load balancing), it routes traffic based on which feature is being requested and whether that feature has been migrated yet — a content-aware or rule-based routing style, sometimes called “layer 7” (application-layer) routing, since it needs to understand the request’s meaning, not just its network address.

13.3 · Caching considerations during migration

If a cache (e.g., Redis) sits in front of the legacy database, teams must ensure the new system either shares that cache correctly or has its own cache that gets invalidated consistently — otherwise, users could see inconsistent results depending on which system happened to serve their request, which is especially confusing (and risky) during shadow mode or partial rollout stages.

Data-consistency gotcha

Two systems with two caches and two databases can silently disagree with each other for weeks before anyone notices — usually when a customer complains that their stock count “jumps around” between refreshes. Reconciliation jobs and cache-invalidation contracts are not optional; they are the load-bearing plumbing of every real migration.

14 · APIs

APIs & Microservices

The de-facto path from monolith to microservices.

14.1 · Strangler Fig as THE standard way into microservices

The Strangler Fig Pattern is, today, widely considered the default, recommended strategy for breaking apart a monolith into microservices. Rather than trying to design a “perfect” microservices architecture up front and rewriting everything at once (a big bang, essentially), teams extract one bounded, well-understood capability at a time into its own service, validate it in production, and repeat.

14.2 · API versioning during migration

Since the facade may expose a stable external API while internally routing to very different implementations (legacy monolith code vs. a modern microservice), API versioning practices (e.g., /v1/, /v2/ URL prefixes, or content negotiation via headers) help ensure external clients aren’t broken by internal migration changes.

Java example — facade with percentage-based rollout

InventoryFacadeController.java
@RestController
public class InventoryFacadeController {

    private final LegacyOrderSystemClient legacyClient;
    private final InventoryServiceClient newInventoryClient;
    private final RolloutConfig rolloutConfig; // e.g., "inventory: 25%"

    @GetMapping("/api/inventory/{productId}")
    public ResponseEntity<InventoryResponse> getInventory(@PathVariable String productId) {

        int rolloutPercent = rolloutConfig.getPercentFor("inventory"); // e.g. 25
        int bucket = Math.abs(productId.hashCode()) % 100;

        if (bucket < rolloutPercent) {
            return ResponseEntity.ok(newInventoryClient.getStock(productId));
        }
        return ResponseEntity.ok(legacyClient.getStock(productId));
    }
}

Here, using a consistent hash of the productId ensures the same product always routes to the same system during a given rollout percentage — avoiding confusing situations where the same product looks different on every refresh.

15 · Patterns

Design Patterns & Anti-patterns

Three close cousins — and two ways to fail.

15.1 · Branch by Abstraction (a close cousin pattern)

What it is: a technique where, inside the same codebase, you introduce an abstraction (an interface) in front of a piece of functionality, implement a new version behind that same interface, and switch between old and new implementations via configuration — all without maintaining a long-lived separate branch in version control.

Why it’s relevant here: it’s often used as a “mini strangler fig” applied within a single application’s codebase, useful when the migration is more about replacing an internal component (e.g., swapping an old payment library for a new one) than extracting an entirely separate service.

Branch by Abstraction — Old and New Behind One Interface «interface» InventoryChecker + checkStock(productId) LegacyInventoryChecker + checkStock(productId) NewInventoryChecker + checkStock(productId) implements implements
Figure 11 — Branch by Abstraction — both old and new implementations sit behind the same interface, letting code switch between them via configuration.

15.2 · Parallel Run

What it is: running both the legacy and new implementations for the same request, comparing their results, but only using the legacy result for the actual response (very similar to shadow mode, but often done synchronously, inline, rather than as separate background traffic).

Why it’s useful: it gives extremely high confidence before cutover, since every single production request is directly compared, not just a sample.

15.3 · Event Interception

What it is: instead of routing whole requests, the facade (or an intercepting layer) captures specific events (e.g., “OrderCreated”) flowing out of the legacy system and forwards copies to new services, letting new functionality be built as a reaction to legacy events without touching legacy code at all.

Why it’s useful: useful when the legacy system cannot easily be modified (e.g., vendor software with no source code access), but it still emits some kind of observable signal (log files, database triggers, message queue events) that can be intercepted.

15.4 · Anti-pattern — “Strangler Fig That Never Finishes”

Anti-pattern — the migration that never ends

The most common real-world failure of this pattern isn’t a technical one — it’s organisational. Teams migrate the “easy” 80% of features relatively quickly, then lose momentum, budget, or leadership priority before tackling the hardest, most tangled 20%. The result is a permanent hybrid system, running both legacy and new infrastructure forever, which is often more costly and complex to operate than either a pure legacy system or a pure new system would have been. A successful strangler fig migration needs an explicit, tracked plan (and organisational commitment) all the way to full legacy retirement — not just an open-ended “we’ll migrate things over time” intention.

15.5 · Anti-pattern — Migrating by Technical Layer Instead of Business Capability

Anti-pattern — horizontal slicing

Some teams try to strangle a system by technical layer — e.g., “first we’ll migrate all the database access code, then all the business logic, then all the UI” — rather than by business capability (e.g., “first we’ll fully migrate Inventory, end to end”). This tends to fail because it doesn’t produce an independently working, valuable slice at each step — you can’t safely cut over “half of the database layer” without the business logic layer being ready too. Migrating by business capability (a vertical slice) is almost always the more successful approach.

16 · Practice

Best Practices & Common Mistakes

The habits that separate migrations that finish from ones that don’t.

16.1 · Best practices

  1. Start with the facade, before any functional migration

    Prove the routing layer works correctly with zero behaviour change first — a “pass-through” deployment is the safest possible first step.

  2. Pick your first migration target carefully

    Choose a feature that is valuable enough to matter, but low-risk enough to be a safe first attempt (not the most business-critical, most tangled piece).

  3. Use shadow mode / parallel run before full cutover

    Gain confidence with real production data before real users depend on the new system.

  4. Make rollback trivial

    Routing decisions should live in fast-changing configuration or feature flags, not require a full redeployment to reverse.

  5. Track migration progress explicitly

    Maintain a clear, visible list of which features are migrated, in-progress, or still on legacy, and share it with stakeholders.

  6. Set an explicit legacy retirement date/goal

    Once the majority of functionality is migrated, name a “lights out” date to avoid the “never finishes” anti-pattern.

  7. Invest in automated reconciliation checks

    Between legacy and new data stores during dual-write periods, to catch silent data drift early.

16.2 · Common mistakes

MistakeWhy it hurts
Underestimating the data synchronisation problem.Teams often plan carefully for routing logic but treat data consistency as an afterthought — and then get surprised by silent drift in production.
Migrating too many features in parallel at once.Loses the pattern’s core safety benefit — small, isolated, easily-verified steps become a big-bang in disguise.
Forgetting to remove legacy code paths after a feature is fully migrated.Leaves dead code and confuses future maintainers about what’s actually in use.
Not communicating migration progress to the wider organisation.Support teams don’t know which system to check when debugging an issue, and stakeholders lose faith in the plan.
Treating the facade as a “temporary hack”.The facade becomes a critical piece of infrastructure the moment it’s introduced — underinvesting in it (testing, monitoring, HA) leads to painful outages.
17 · In the wild

Real-World & Industry Examples

Where the fig has already wrapped the oak.

Amazon

Amazon has publicly discussed, in various engineering talks over the years, how large parts of its retail platform were migrated from a large monolithic architecture toward independently deployable services incrementally over an extended period, rather than through a single rewrite — a textbook large-scale application of strangler-fig-style thinking, driven by the practical need to keep the world’s largest e-commerce platform running without interruption throughout the transition.

Shopify

Shopify has written publicly about modularising parts of its core Ruby on Rails monolith over time, extracting specific business domains into more independently manageable modules and services gradually, rather than attempting a full platform rewrite — reflecting the same underlying “extract a well-defined seam, prove it works, move to the next one” philosophy central to the Strangler Fig Pattern.

Government & Large Enterprise Systems

Large government IT modernisation efforts (for tax systems, benefits systems, and similar critical infrastructure in various countries) have increasingly adopted strangler-fig-style approaches after several well-publicised big bang rewrite failures in the 2000s and 2010s that ran hugely over budget or were scrapped entirely. Modern guidance from major government digital service standards bodies frequently recommends incremental modernisation over full rewrites for exactly the risk reasons covered in this tutorial.

Banking

Many banks running core systems written decades ago (some famously still involving very old programming languages) have adopted strangler-fig-style modernisation, wrapping old core banking engines with modern API layers and gradually moving specific capabilities (like customer-facing balance checks or transaction history) to new systems, while leaving the most sensitive, heavily regulated core ledger functionality on the legacy platform until very high confidence is reached — illustrating that even the highest-stakes, most risk-averse industries favour this gradual approach over big bang rewrites.

The common thread

In every one of these examples, the shared philosophy is the same: extract a well-defined seam, prove it works in production, retire the equivalent legacy code path, and repeat. Different industries, different technology stacks, same rhythm.

18 · FAQ

Frequently Asked Questions

The five questions that come up in almost every interview and design review.

Q1 · Is the Strangler Fig Pattern only useful for moving to microservices?

No. While it’s most commonly discussed in the context of monolith-to-microservices migrations, the same core idea applies to any legacy modernisation: moving from an old framework to a new one, migrating from an on-premise system to the cloud, or even replacing a single problematic module inside an otherwise healthy application.

Q2 · How long does a typical strangler fig migration take?

It varies enormously depending on the size and complexity of the legacy system — anywhere from a few months for a small application to several years for a large, deeply tangled enterprise system. The pattern doesn’t promise speed; it promises safety and continuous value delivery throughout whatever time it takes.

Q3 · Do you always need to build a custom facade?

No — many teams use existing tools (API gateways, reverse proxies, service mesh traffic-splitting features) instead of writing custom routing code, especially in cloud-native environments where this capability is often built in.

Q4 · What happens to the legacy database at the end?

Typically, once all functionality has moved to new systems and their own databases, the legacy database is put into read-only mode for a period (in case of audits or historical lookups), archived, and eventually decommissioned — following the organisation’s data retention and compliance requirements.

Q5 · Can the Strangler Fig Pattern fail?

Yes — most commonly not due to a technical flaw in the pattern itself, but due to organisational issues: losing momentum partway through (Section 15.4), poor planning of data synchronisation, or picking overly ambitious first migration targets that erode team and stakeholder confidence early on.

19 · Wrap-up

Summary & Key Takeaways

Ten lines you can carry into any interview or architecture review.

The Strangler Fig Pattern is a legacy modernisation strategy in which a new system is built gradually alongside an old one, with a routing facade progressively redirecting more and more functionality from the legacy system to the new system, one well-chosen “seam” at a time — until the legacy system can be safely and confidently retired. It replaces the high-risk, all-or-nothing “big bang rewrite” approach with a series of small, reversible, independently verifiable steps, each of which delivers real business value along the way, rather than requiring the entire migration to finish before anything is gained.

  1. The pattern is named after the strangler fig plant, which grows gradually around a host tree until it can stand on its own — the host (legacy system) can then be removed.
  2. It was named by Martin Fowler in 2004, as a direct response to the repeated failures of big bang legacy rewrites.
  3. Three core components: the legacy system, the new system(s), and a facade / routing layer that decides which one handles each request.
  4. Migration proceeds by identifying seams (naturally separable pieces of functionality), building new implementations, and gradually redirecting traffic — often via shadow mode and percentage-based rollout first.
  5. Data synchronisation between legacy and new systems during the transition (often via Change Data Capture) is typically the hardest engineering challenge, harder than the request-routing itself.
  6. Key advantages: low risk, fast rollback, continuous incremental value, and compatibility with modern practices like feature flags, canary releases, and service meshes.
  7. Key risks: it can take longer than a rewrite in ideal conditions, requires operating two systems simultaneously for a while, and can stall indefinitely if not actively driven to full legacy retirement.
  8. The pattern is now the de-facto standard approach for monolith-to-microservices migrations across the industry, and is also widely used for cloud migrations and framework upgrades.
  9. Real-world large-scale examples include Amazon’s retail platform, Shopify’s core monolith modularisation, government IT modernisation programmes, and core banking system upgrades.
  10. The central trade-off to remember: the Strangler Fig Pattern trades raw migration speed for safety, reversibility, and continuous delivered value — a trade nearly every large, real-world legacy modernisation effort ultimately chooses to make.

The single-sentence takeaway

Grow the new around the old, one seam at a time, and let the old rot away safely from the inside.

“Gradually create a new system around the edges of the old, letting it grow slowly over several years until the old system is strangled.” — Martin Fowler, 2004
#strangler-fig #legacy-modernization #microservices #system-design #martin-fowler #facade-pattern #feature-flags #canary-release #anti-corruption-layer #change-data-capture #cloud-migration #interview-prep