The Strangler Fig Pattern

📘 Chapter 10: Microservices & Service Architecture ⏱️ 8 min read 🏗️ Lesson 044

Named after strangler fig trees that grow around a host tree and eventually replace it, this pattern lets you incrementally migrate from a monolith to microservices — without a risky "big bang" rewrite.

The Core Idea

Place a proxy in front of the monolith. Gradually route traffic for specific features to new services. The monolith shrinks over time until it's gone (or small enough to maintain).

  • Each migration step is small and reversible
  • The old and new systems run simultaneously
  • Users never notice the transition

The Four Phases

Strangler Fig Migration Phases Phase 1: All Monolith Proxy Monolith A B C D E 100% traffic Phase 2: Some Migrated Proxy Monolith B C D E Svc A Phase 3: Most Migrated Proxy Mono D E A B C Phase 4: Monolith Gone API Gateway A B C D E Time (months to years) → Each phase: deploy, validate, rollback if needed, then proceed
Figure 1: The proxy gradually shifts traffic from monolith to new services. At each phase, rollback is possible by re-routing traffic back.

Key Techniques

The Strangler Proxy

A reverse proxy (Nginx, Envoy, API Gateway) that routes by URL path or header:

  • /api/orders/* → new Orders service
  • /api/payments/* → still monolith (not migrated yet)
  • Routing changes are config-only — no code deploys needed to shift traffic

Data Migration Strategies

  • Dual-write: New service writes to both old and new DB during transition. Risk: inconsistency if one write fails.
  • Change Data Capture (CDC): Stream database changes from monolith DB to new service's DB via Debezium/Kafka. Safer than dual-write.
  • Database-per-service: Target state — each service owns its data. Migrate gradually using CDC, then cut over.

Feature Flags for Granular Control

  • Route 1% of traffic to new service, monitor errors, ramp up
  • Instant rollback: flip the flag, 100% goes back to monolith
  • Canary by user segment: internal users first, then beta, then everyone

What to Migrate First?

Prioritize modules that are:

  • High change frequency: Extracted service lets the team ship faster without monolith deploys
  • Low coupling: Minimal data sharing with rest of monolith — cleaner extraction
  • Clear boundary: Well-defined API surface — easier to put behind a proxy
  • Different scaling needs: Immediate operational benefit from independent scaling

Avoid starting with: core domain (too coupled), shared auth (too cross-cutting), or anything without clear ownership.

Real-World Examples

🏢 Stripe — Ruby Monolith Migration

  • Stripe's core was a large Ruby monolith processing billions in payments
  • Couldn't do a big-bang rewrite — any downtime = money lost
  • Used strangler pattern: new payment flows routed to new services, legacy flows remained in monolith
  • Key insight: They kept the monolith for years alongside new services. "Done" doesn't mean monolith is gone — it means it's stable and shrinking.

🏢 Amazon — From Bookstore Monolith to 1000+ Services

  • Early 2000s: Amazon was a monolithic C++ application
  • CEO mandate: all teams must communicate via service interfaces (the famous Bezos API memo)
  • Migration took years — each team extracted their domain into a service at their own pace
  • No central "rewrite project" — organic, team-by-team extraction driven by pain and need
  • Result: enabled AWS (internal services became external products)

Interactive: Plan Your Migration Order

You have a monolith with 5 modules. Rank them by migration priority based on their characteristics: