AlgoMaster Logo

Strangler Fig Pattern

Last Updated: January 8, 2026

Ashish

Ashish Pratap Singh

Throughout this section on microservices, we have covered patterns for building distributed systems from scratch: service discovery, API gateways, sidecars, service meshes, circuit breakers, and bulkheads.

But what if you are not starting from scratch?

Many organizations have existing monolithic applications, sometimes built over decades, that they want to modernize. The temptation is to stop everything, rewrite the system in a modern architecture, and launch the new version when it is ready. This approach, the "big-bang rewrite," fails more often than it succeeds.

The Strangler Fig pattern offers an alternative. Named after strangler fig trees that gradually envelop and replace their host trees, this pattern enables incremental migration from a legacy system to a new architecture. You replace functionality piece by piece, keeping the old system running until the new one is ready.

In this chapter, you will learn:

  • Why big-bang rewrites typically fail
  • How the Strangler Fig pattern works
  • Implementation strategies for routing and data migration
  • Real-world examples and lessons learned
  • When to use this pattern and when to consider alternatives

The Strangler Fig pattern is one of the most practical approaches for modernizing legacy systems safely.

The Problem: Big-Bang Rewrites Fail

The idea is tempting. Your legacy system is a mess: hard to understand, impossible to test, built on outdated technology. You imagine starting fresh with clean code, modern frameworks, and proper architecture.

The Reality

Here is what actually happens:

Why Rewrites Fail

ProblemDescription
Hidden complexityLegacy systems contain years of edge cases, bug fixes, and business rules that are not documented
Moving targetBusiness does not stop; requirements change while you rewrite
No value deliveryCustomers get no new features during the rewrite
All-or-nothing riskYou only discover if it works when you flip the switch
Knowledge lossPeople who understood the old system may leave during multi-year rewrite
Testing gapNew system has not been battle-tested in production

Joel Spolsky famously called rewriting code from scratch "the single worst strategic mistake that any software company can make." Netscape's rewrite nearly killed the company. Many others have shared the same fate.

The Strangler Fig Pattern

The Strangler Fig pattern gradually migrates a legacy system to a new architecture. Instead of replacing everything at once, you intercept requests to the old system and incrementally route them to new components.

The Metaphor

The pattern is named after strangler fig trees in tropical rainforests. These trees start as seeds dropped by birds onto host trees. The fig grows roots down to the ground while wrapping around the host. Over time, the fig completely envelops the host tree. Eventually, the host dies and rots away, leaving the fig standing independently.

Applied to Software

In software, the legacy system is the host tree. You build new components around it, gradually replacing functionality until the legacy system can be removed entirely.

How It Works

Step 1: Introduce the Strangler Facade

First, identify a point where you can intercept requests before they reach the legacy system. This is your strangler facade. It could be:

  • An API gateway
  • A reverse proxy (Nginx, HAProxy)
  • A load balancer
  • A new routing layer

Initially, the facade passes all requests through to the legacy system. It is transparent to clients.

Step 2: Identify Migration Candidates

Analyze your legacy system to find good starting points. Look for functionality that is:

  • Well-defined and bounded
  • Has clear inputs and outputs
  • Causes the most pain (slow, buggy, needs frequent changes)
  • Has low coupling with other parts

Good First Candidates

  • Authentication: Clear boundary, standardized protocols
  • Email/Notifications: Isolated, rarely changes
  • File upload/storage: Well-defined interface
  • Search functionality: Can use modern search engines
  • Reporting/Analytics Often read-only, low risk

Poor First Candidates

  • Core business logic: Too intertwined with everything
  • Database schema: Everything depends on it
  • Shared libraries: Used everywhere
  • Tightly coupled modules: Hard to extract cleanly

Step 3: Build the New Component

Create the new service that will replace the legacy functionality:

The new component should:

  • Implement the same business logic as legacy
  • Have its own data store if needed
  • Be independently deployable
  • Have comprehensive tests

Step 4: Route Traffic to New Component

Update the strangler facade to route relevant requests to the new component:

Start with a small percentage of traffic to validate the new system works correctly.

Step 5: Verify and Iterate

Monitor the new component carefully:

  • Compare responses with the legacy system
  • Watch for errors and performance issues
  • Gather metrics and logs
  • Get feedback from users

Once confident, route 100% of traffic. Then repeat for the next functionality.

Step 6: Decommission Legacy Components

When functionality is fully replaced:

  1. Stop routing any traffic to it
  2. Keep it running (unused) for a rollback period
  3. Remove references in the codebase
  4. Archive data
  5. Delete the code and infrastructure

Implementation Strategies

URL/Path-Based Routing

Route requests based on the URL path. It’s straightforward and works especially well for REST APIs.

Example: Nginx configuration

Percentage-Based Routing

Route a percentage of traffic to the new system. Great for gradual rollouts.

Feature Flag-Based Routing

Use a feature flag to decide which implementation handles a request.

  • use_new_auth = true → New Auth
  • use_new_auth = false → Legacy Auth

Feature flags enable:

  • Per-user or per-tenant rollouts
  • Instant rollback by flipping a flag
  • A/B testing of implementations
  • Geographic or segment-based routing

Branch by Abstraction

For internal code migrations, introduce an interface and swap implementations behind it.

Pattern:

  • Application calls IPaymentProcessor
  • A factory selects LegacyProcessor or NewProcessor based on configuration

Handling Data Migration

Routing is only half the battle. Data is often the hardest part especially when the new service needs its own database.

Shared Database (Temporary)

Both systems use the same database initially. Migrate data storage later.

Pros

  • Simplest approach
  • No sync needed

Cons

  • Tight coupling
  • Cannot evolve schema independently

Data Synchronization

New service has its own database, kept in sync with legacy:

Pros

  • Services are decoupled

Cons

  • Sync adds complexity and latency

Event-Driven Synchronization

Use events to keep data in sync:

Pros

  • Loosely coupled
  • Async and scalable

Cons

  • Eventual consistency
  • Complex debugging

Best Practices

1. Start Small and Learn

Pick a simple, low-risk component for your first migration. The goal is to establish patterns, not solve the hardest problem first.

2. Invest in Observability

You are running two systems in parallel. You need visibility into both.

3. Use Shadow Testing

Route copies of requests to the new system without using the response. Compare results to validate correctness.

4. Plan for Rollback

Things will go wrong. Ensure you can quickly revert to legacy.

  • Keep legacy running even after migration
  • Feature flags for instant rollback
  • Reversible database migrations
  • Documented rollback procedures

5. Actually Decommission Legacy

Do not leave old code running "just in case" indefinitely. Set firm dates.

Decommission Timeline (Example ):

  • Week 1-2: Route 0% traffic, monitor
  • Week 3-4: Remove from codebase
  • Week 5-6: Archive data
  • Week 7-8: Shutdown infrastructure

6. Communicate Progress

Set expectations that migration takes time. Share progress regularly.

Migration Dashboard:

Common Pitfalls

Pitfall 1: Migrating Too Much at Once

The point is incremental migration. Migrating 10 services simultaneously is back to big-bang territory.

Fix: One service at a time. Get it to production. Learn. Repeat.

Pitfall 2: Not Decommissioning Legacy

Teams migrate functionality but leave old code running. This wastes resources and creates confusion.

Fix: Set firm decommission dates. After X weeks with 0 traffic, delete.

Pitfall 3: Ignoring Data Consistency

During migration, data might exist in two places. If not careful, they drift apart.

Fix: Choose a source of truth. Build reconciliation jobs. Alert on inconsistencies.

Pitfall 4: Breaking Client Contracts

External clients depend on your API. Changing it during migration breaks them.

Fix: Maintain backward compatibility. Version APIs. Give clients migration paths.

Pitfall 5: Underestimating Hidden Dependencies

Legacy systems have dependencies you do not know about. That "unused" endpoint? Some batch job calls it at 3 AM.

Fix: Log all requests to legacy. Wait weeks before deciding something is unused.

When to Use the Strangler Fig Pattern

The Strangler Fig pattern is ideal when you need to modernize a system without betting the business on a single, risky cutover.

Good Fit

ScenarioWhy It Fits
Legacy system too large to rewriteIncremental progress is achievable
System must remain availableNo downtime required
Business continues evolvingCan deliver features while migrating
Risk tolerance is lowSmall, reversible changes
Migration will take months/yearsSustainable pace

Consider Alternatives

ScenarioAlternative
System small enough to rewrite quicklyJust rewrite it
No clear API surfaceBranch by abstraction first
Team lacks experienceStart with new feature, not migration
Data model must fundamentally changeDatabase-first migration

Summary

The Strangler Fig pattern enables safe, incremental migration from legacy systems to modern architectures:

  • The problem: Big-bang rewrites typically fail due to hidden complexity, moving requirements, and all-or-nothing risk.
  • The solution: Incrementally replace functionality by routing through a facade, migrating piece by piece.
  • Key steps: Introduce facade, identify candidates, build new components, route traffic, verify, decommission legacy.
  • Routing strategies: URL-based, percentage-based, feature flags, or branch by abstraction.
  • Data handling: Shared database, synchronization, or event-driven approaches.
  • Best practices: Start small, invest in observability, use shadow testing, plan rollbacks, actually decommission.