Strangler Fig Pattern
Strangler Fig Pattern
"The Strangler Fig pattern involves incrementally migrating a legacy system by gradually replacing specific pieces of functionality with new applications and services." — Martin Fowler, martinfowler.com/bliki/StranglerFigApplication.html
Intent
The Strangler Fig pattern is an incremental migration strategy for replacing a monolith with a new system, one feature at a time. A facade or proxy receives all inbound traffic. At migration start, the facade routes 100% of requests to the legacy system. As each feature is migrated, the facade is updated to route those requests to the new system instead. The legacy system is progressively starved of traffic until, eventually, it handles nothing.
Named after the strangler fig tree, which grows around an existing host tree, eventually enveloping and replacing it. Unlike a big-bang rewrite — which requires cutting over an entire system at once — the Strangler Fig migration is continuous, reversible, and low-risk. Individual features can be rolled back by updating the facade's routing rules.
The critical insight that separates successful migrations from stalled ones: "done" means the legacy is deleted, not merely bypassed. When traffic reaches zero, the migration has reached a necessary milestone — but not the finish line. Deletion of legacy code, deprovisioning of infrastructure, and removal of the CI/CD pipeline are what define completion. The Anti-Corruption-Layer-Pattern (ACL) is the isolation companion during the coexistence period: it prevents the new system's domain model from being contaminated by legacy data concepts. When the migration is complete and the legacy is deleted, the ACL is removed — it was temporary scaffolding, not a permanent architectural structure.
When NOT to Use
- Greenfield projects — there is nothing to strangle; start with clean architecture from the outset.
- Legacy system without clear route boundaries — Strangler Fig works best when requests can be routed by URL path, feature area, or message type. If the legacy system interleaves concerns such that a single request exercises multiple feature areas simultaneously, the routing facade cannot cleanly dispatch to different backends.
- Legacy system already well-modularised with clean APIs — if the existing system already has clear internal boundaries, refactor in place rather than build a parallel system. The overhead of a parallel facade and dual-system maintenance is not justified when the existing codebase can be incrementally improved.
- Organisation without commitment to delete the legacy — without an explicit decommissioning plan (with a target date for legacy deletion), the Strangler Fig pattern produces two systems to maintain indefinitely. If the organisation is not prepared to delete the legacy, the migration will stall with the legacy "receiving zero traffic but still deployed" — the most common failure mode.
When to Use
- Any monolith-to-microservices migration, regardless of the monolith's size or age.
- Long-running migrations (months or years) where a big-bang rewrite carries unacceptable risk.
- When the legacy system has clear routing boundaries — URL paths, feature areas, message types — that allow traffic to be dispatched to either the legacy or new system without ambiguity.
- When an ACL can isolate the new system from legacy data model contamination during the coexistence period.
- When the team needs to ship value continuously during migration — each migrated feature goes live in the new system before the migration is complete.
How It Works
The migration proceeds in five phases:
Client -> Facade/Gateway -> [migrated routes -> New System]
-> [remaining routes -> Legacy System]
Phase 1 — Facade deployed: A routing facade (typically an API Gateway, nginx reverse proxy, or application-layer router) is deployed in front of the legacy system. All traffic routes through the facade to legacy. User impact: none — the facade is transparent.
Phase 2 — First feature migrated: The new system is built and deployed alongside the legacy. The facade is updated to route the migrated feature's paths to the new system; all other paths continue to the legacy. The ACL (if used) translates between the legacy data model and the new domain model for any shared data.
Phase 3 — Progressive migration: Additional features are migrated iteratively. With each migration, the facade gains a new route entry pointing to the new system. The legacy system's traffic share decreases with each iteration.
Phase 4 — All routes migrated: The legacy system receives zero traffic. This is a necessary milestone, but not the completion criterion. The legacy system is still deployed, still consuming cloud resources, and still requires security patching.
Phase 5 — Legacy deleted: The legacy system code is removed from the codebase. Its CI/CD pipeline is decommissioned. Its database schema is migrated to the new system or dropped. Its cloud resources are deprovisioned. This is the completion criterion. The facade may be retained as a permanent API-Gateway-Pattern or removed if no longer needed.
Done means deleted, not bypassed: A Strangler Fig migration is complete when the legacy system code is deleted from the codebase, its CI/CD pipeline is decommissioned, its database schema is migrated or dropped, and its cloud resources are deprovisioned. Zero remaining traffic is a necessary but not sufficient condition for completion. A legacy system receiving zero traffic but remaining deployed is a decommissioning failure — it still requires security patches, consumes cloud cost, and creates re-enablement risk.
Migration Diagram
Strangler-Fig-Pattern-diagram.excalidraw
ACL Relationship
The Anti-Corruption-Layer-Pattern is the isolation companion during the coexistence period. When both the legacy system and the new system are running simultaneously, the new system may need to read from or write to shared data stores, or call legacy services for data it has not yet migrated. Without an ACL, the new system's domain model is at risk of absorbing legacy naming conventions and data structures — CUST_ID, ACCT_NO, LEGACY_STATUS — corrupting the new codebase's ubiquitous language before the migration is complete.
The ACL translates between the legacy system's data model and the new system's domain model, keeping the boundary clean during coexistence. When the migration is complete and the legacy is deleted, the ACL is removed alongside it. The ACL was never intended as a permanent boundary — it was scaffolding to protect the new domain during construction. If the ACL persists after legacy deletion, it has become unnecessary complexity.
Warning: If the ACL remains in the codebase after the legacy system is deleted, it is a sign that the migration did not complete fully — there is likely residual legacy data or a legacy-format dependency that was not cleaned up.
TypeScript Example
// Strangler Fig Pattern — TypeScript (routing facade)
// Source: Fowler, "StranglerFigApplication", martinfowler.com, 2004
// Newman, Building Microservices 2nd ed., 2021, Ch. 3
//
// The facade routes incoming requests: legacy system until a feature is migrated, then new system.
// "Done" = legacy code DELETED, not merely receiving zero traffic.
class StranglerFacade {
private migratedRoutes = new Set<string>(['/orders', '/products']); // routes fully migrated
async handleRequest(path: string, request: Request): Promise<Response> {
if (this.migratedRoutes.has(path)) {
return this.newSystem.handle(path, request); // new system handles migrated routes
}
return this.legacySystem.handle(path, request); // legacy system handles remaining routes
}
// DECOMMISSION CRITERIA: migration is complete when:
// 1. migratedRoutes === ALL routes (no legacy calls remain)
// 2. legacySystem code is DELETED from codebase (not just idle)
// 3. Legacy database schema is migrated or dropped
// 4. Legacy CI/CD pipeline and cloud resources are deprovisioned
// "Bypassed is not done" — legacy must be deleted, not merely bypassed.
}
// Note: production implementations use API Gateway routing rules (AWS ALB, nginx upstream,
// Kong route configuration) rather than application-level dispatch code.Java Example
// Strangler Fig Pattern — Java (routing facade)
// Source: Fowler, "StranglerFigApplication", martinfowler.com, 2004
// "Done" = legacy code DELETED — bypassed is not done.
@Component
public class StranglerFacade {
private final Set<String> migratedPaths = Set.of("/orders", "/products");
public ResponseEntity<?> handleRequest(String path, HttpServletRequest request) {
if (migratedPaths.contains(path)) {
return newSystemClient.forward(path, request); // new system
}
return legacySystemClient.forward(path, request); // legacy system
}
// DECOMMISSION CRITERIA: migration is complete when:
// 1. migratedPaths contains ALL routes (no legacy calls remain)
// 2. legacySystemClient and all legacy code are DELETED from codebase
// 3. Legacy database schema is migrated or dropped
// 4. Legacy CI/CD pipeline and cloud resources are deprovisioned
}
// Note: production implementations use Spring Cloud Gateway, AWS ALB, or nginx upstream
// routing rather than application-level dispatch.Lineage Backward
- Facade-Pattern (GoF structural ancestor) — The routing facade deployed in the Strangler Fig pattern IS a GoF Facade. It provides a simplified interface (route dispatch) that hides the complexity of the dual-system topology from clients. Clients call the facade; the facade decides which backend to invoke. When migration is complete, the Facade either becomes a permanent API Gateway or is removed.
- Anti-Corruption-Layer-Pattern (isolation companion during migration) — The ACL prevents legacy data model concepts from contaminating the new domain during the coexistence period. Both patterns are used together for clean migrations. The ACL is removed when migration is complete.
- Bounded-Context (migration is a context boundary strategy) — Strangler Fig migration is a context boundary migration strategy. Each feature extracted from the monolith into the new system defines a new bounded context with its own domain model and ubiquitous language. The ACL enforces the boundary between the old context (legacy) and the new context (new system) during the transition.
Lineage Forward
- API-Gateway-Pattern — In practice, the Strangler Facade is often implemented as an API Gateway (AWS API Gateway, Kong, nginx, Spring Cloud Gateway). The gateway's routing rules are updated as features are migrated. After migration is complete, the gateway may be retained as a permanent API Gateway handling north-south (client-to-service) traffic — no longer routing to a legacy system, but serving as the standard entry point to the new microservices.
Related Concepts
| Pattern | Relationship |
|---|---|
| Facade-Pattern | GoF structural ancestor — the Strangler Facade IS a GoF Facade providing route dispatch that hides backend complexity from clients. |
| Anti-Corruption-Layer-Pattern | Isolation companion during the coexistence period — prevents legacy model contamination. Removed when migration is complete. |
| Bounded-Context | Strangler Fig migration is a bounded context migration strategy — each migrated feature gains its own bounded context and domain model. |
| API-Gateway-Pattern | The gateway often serves as the Strangler Facade in practice. After migration, the facade may be retained as a permanent API Gateway. |
| Deployment-Patterns-MOC | This note is part of the Phase 14 deployment patterns collection. |
Related Architecture Patterns
- Modular-Monolith — A well-structured Modular Monolith provides natural extraction seams for the Strangler Fig migration; each module's defined public API becomes the interface the Strangler Facade routes to.
Sources
- Fowler, Martin. "StranglerFigApplication", martinfowler.com/bliki/StranglerFigApplication.html, 2004
- Newman, Sam. Building Microservices, 2nd ed., O'Reilly, 2021, Ch. 3
- Richardson, Chris. Microservices Patterns, Manning, 2018