🏗️ Section 17 · Question #24

What is Strangler Fig pattern

The Strangler Fig Pattern (coined by Martin Fowler) is an evolutionary architectural migration strategy that incrementally replaces specific features of a legacy monolithic appl...


🟢 Junior Level

The Strangler Fig Pattern (coined by Martin Fowler) is an evolutionary architectural migration strategy that incrementally replaces specific features of a legacy monolithic application with microservices over time, until the legacy monolith is either completely phased out or reduced to a minimal core, avoiding the catastrophic business risks of a “Big Bang Rewrite”:

Phase 0 (Legacy Monolith):   [═════════════════ Monolith ═════════════════]

Phase 1 (First Step):        [══════════════ Monolith ══════════════] + [User Service]

Phase 2 (Expansion):         [═══════════ Monolith ═══════════] + [User Service] + [Order Service]

Phase 3 (Final Cutover):     [User Service] + [Order Service] + [Payment Service] + [Catalog Service]
                             (The old monolith is safely decommissioned and powered down! 🎉)

The Botanical Metaphor

In nature, the seeds of the strangler fig tree germinate in the upper branches of a host tree. The fig gradually sends roots down to the soil, thickens its lattice around the host trunk, and eventually envelops the original tree completely, leaving a hollow, independent fig tree while the host tree rots away naturally.

Why It Eliminates the “Big Bang Rewrite” Disaster

  • The Big Bang Rewrite Anti-Pattern: A company freezes new product development for 18–24 months to rebuild the entire application from scratch. In 90% of cases, this fails catastrophically: the business outgrows the initial spec, hidden edge cases in the monolith are forgotten, and attempting an all-at-once cutover produces fatal outages.
  • Strangler Fig Strategy: Value is delivered to production every 2–4 weeks. A single bounded context is carved out and deployed with Zero Downtime, while the rest of the business continues operating uninterrupted.

🟡 Middle Level

1. The Four-Phase Migration Lifecycle

                       ┌───────────────────────────────┐
                       │     API Gateway / Ingress     │
                       └───────┬───────────────┬───────┘
            /api/v1/orders     │               │  /api/v1/users (Strangled Route!)
                               ▼               ▼
                      ┌────────────────┐ ┌─────────────────┐
                      │    Monolith    │ │  User Service   │
                      │  (Deprecating) │ │ (New Container) │
                      └────────────────┘ └─────────────────┘
  1. Intercept: Deploy an API Gateway (Spring Cloud Gateway, Envoy, Kong) in front of the monolith. All incoming external traffic passes through this edge facade.
  2. Transform: Design and implement a new, cleanly bounded microservice (e.g., User Service) from scratch using modern best practices and an isolated database (Database per Service).
  3. Coexist: The API Gateway updates its routing rules: /api/v1/users traffic is forwarded to the new microservice, while all remaining routes continue proxying to the monolith. Data is synchronized between systems.
  4. Eliminate: The legacy user code, controllers, and deprecated database tables are permanently deleted from the monolith.

2. The Anti-Corruption Layer (ACL)

When carving out a new service, developers must prevent legacy domain models, confusing table schemas, and deprecated database column naming from polluting the clean architecture of the new microservice:

[Clean Domain Model (New Service)] ◄──► [Anti-Corruption Layer (ACL)] ◄──► [Monolith (Legacy Schema/API)]

An Anti-Corruption Layer acts as a bidirectional translation adapter:

  • Translates legacy monolith data structures and cryptic column abbreviations into clean Domain-Driven Design (DDD) domain entities.
  • Ensures the new microservice’s domain remains pristine and decoupled from legacy tech debt.

🔴 Senior Level

1. Bidirectional Data Synchronization: Why Dual-Writing Fails & CDC Wins

During the extended Coexistence phase, data mutations made in the new microservice often need to be reflected in the monolith’s legacy database (and vice versa):

CDC Synchronization Pipeline:
[Monolith PostgreSQL] ──(WAL Binary Stream)──► [Debezium CDC] ──► [Kafka] ──► [New Service PostgreSQL]
         ▲                                                                               │
         └─────────────────(Debezium CDC / Outbox Pipeline)──────────────────────────────┘
Strategy Architecture Operational Trade-offs
Application Dual-Writing The monolith writes to its local DB and synchronously sends an HTTP/Kafka write to the new service. 🔴 High Failure Rate: Network timeouts and app crashes cause permanent data divergence; lacks two-phase commit atomicity.
Change Data Capture (CDC) Debezium + Kafka Connect tails the low-level Write-Ahead Log (WAL) of the database engines. 🏆 Industry Standard: Asynchronous, sub-second latency, zero application code overhead, zero data loss.

Preventing Infinite Replication Loops:

If Database A replicates changes to Database B via CDC, and Database B replicates changes back to Database A, an infinite replication cycle will corrupt data.

  • Solution: Add an origin tracking column (source_system = 'NEW_SERVICE') or embed origin metadata inside Kafka message headers. Debezium and consumer ingest handlers filter out any CDC records originating from their own system.

2. Traffic Shadowing (Dark Launching) & Semantic Diffing

Before switching live user traffic over to the newly strangled microservice, senior engineers validate correctness and performance under real-world production load using Traffic Shadowing:

                  ┌───────────────────────────────┐
                  │    API Gateway / Envoy Proxy  │
                  └───────┬───────────────┬───────┘
                          │ (Live Route)  │ (Asynchronous 100% Shadow Copy)
                          ▼               ▼
                     [ Monolith ]   [ New Service ] ──► [ Shadow Database ]
                          │               │
                          └───────┬───────┘
                                  ▼
                         [ Semantic Diff Engine ]
  1. Traffic Duplication: Envoy or the API Gateway synchronously forwards the live user request to the Monolith (returning the official response to the user). Simultaneously, it clones the request payload and sends it asynchronously to the new microservice.
  2. Semantic Diffing (e.g., Twitter Diffy): A diff engine compares the JSON responses, headers, and status codes between both systems across hundreds of thousands of live requests.
  3. Canary Migration: Once response parity reaches 100%, real traffic is cut over incrementally via Feature Flags ($1\% \to 10\% \to 50\% \to 100\%$).

4 Tricky Questions

1. How do you safely test mutating operations (POST, PUT, DELETE) using Traffic Shadowing without causing duplicate credit card charges or sending duplicate emails?

Answer: Shadowing read-only GET queries is harmless, but shadowing mutating write requests requires strict isolation:

  1. Isolated Shadow Database: The shadowed microservice connects to a dedicated, ephemeral Shadow Database populated with an anonymized snapshot of production data. Mutations in the shadow service never touch production data stores.
  2. Mocking External Side-Effects: At the network layer or via Spring profiles (@Profile("shadow")), all outbound HTTP and SMTP clients (e.g., Stripe, SendGrid, Twilio) are swapped with mock No-Op adapters or WireMock servers.
  3. Event Assertion Verification: Rather than checking physical side-effects, automated verification engines compare generated Kafka domain events and final entity states in the Shadow DB against the expected production output.

2. Which bounded context should an engineering team strangle first, and why?

Answer: An engineering team should never begin by strangling the core transactional domain (e.g., core billing or checkout). The Optimal Candidate:

  1. Low Coupling: A component with minimal internal foreign keys and dependencies on other parts of the monolith.
  2. Low Business Risk: A failure in this service will not halt mission-critical company operations.
  3. Prime Examples: Outbound notifications (Email/SMS dispatch), PDF document generation, static catalog browsing, or promotional discount calculations. Starting with a peripheral service allows the team to establish CI/CD pipelines, Kubernetes manifests, observability dashboards, and deployment automation with minimal operational risk before taking on complex transactional domains.

3. How do you handle cross-system distributed transactions between the new microservice and the legacy monolith during migration?

Answer: Two-Phase Commit (2PC / XA) is completely infeasible between modern microservices and legacy monolithic frameworks. The Enterprise Solution: Implement an Asynchronous Saga with Monolith Integration:

  1. Adapt the monolith to expose explicit compensating REST endpoints (e.g., POST /api/v1/legacy-orders/{id}/cancel).
  2. The new microservice acts as the Saga Orchestrator, coordinating state mutations across both systems via Kafka command topics.
  3. If an operation fails in either system, the orchestrator triggers compensating endpoints in reverse order, ensuring eventual consistency.

4. Must an engineering organization always strangle a monolith down to the very last line of code (The 80/20 Rule)?

Answer: No. Decomposing a monolith is a business investment driven by ROI, not an ideological purity contest. In practice, the Pareto Principle (80/20 Rule) applies:

  • Migrating the first 80% (frequently modified, high-traffic product features like search, cart, and recommendations) delivers 95% of microservice benefits (deployment agility, independent autoscaling).
  • The remaining 20% often consists of stable, rarely modified legacy code (e.g., quarterly financial tax reporting, legacy batch export routines).
  • Rewriting this legacy tail provides negative ROI. The remaining monolith is simply retained as a stabilized, downscaled internal service (“Legacy Core Service”) sitting behind the API Gateway indefinitely.

🎯 Interview Cheat Sheet

Must Know:

  • Definition: An evolutionary migration pattern (coined by Martin Fowler) that incrementally replaces monolith features with microservices behind an API Gateway.
  • Why It Wins: Eliminates the extreme failure and cancellation rates of multi-year “Big Bang Rewrites”; ensures continuous business delivery.
  • The 4 Steps: Intercept (API Gateway facade) $\to$ Transform (Build modern service) $\to$ Coexist (Dual running & route cutover) $\to$ Eliminate (Delete dead monolith code).
  • Anti-Corruption Layer (ACL): Protects new domain models from being contaminated by legacy monolith database schemas.
  • Data Sync: Use Change Data Capture (Debezium + Kafka) rather than application-level dual-writes; prevent infinite loops with source tracking tags.
  • Verification: Validate stability using Traffic Shadowing (Dark Launching) and semantic diff engines prior to canary release.

Common Follow-up Questions:

  • “What is the main challenge of the Coexist phase?” → Maintaining bidirectional data consistency between two active databases without causing deadlocks or replication loops.
  • “How fast is a rollback if a new strangled service fails?” → Under 1 second: toggle the API Gateway route or Feature Flag to route 100% of traffic back to the monolith endpoint.

Red Flags (DO NOT Say):

  • ❌ “The best way to modernize is to halt feature development for 18 months and rewrite the entire monolith from scratch.” (The classic Big Bang anti-pattern that leads to project cancellation).
  • ❌ “We synchronize data by executing two @Transactional database writes simultaneously in our Java code.” (Dual-writing lacks distributed atomicity and causes silent data corruption).
  • ❌ “The first module to carve out should be the core billing and transaction ledger.” (Start with low-risk, loosely coupled perimeter services like notifications or reports).