Strangler Fig Migration Pattern in Python

Gradually reroute calls from a legacy service to a modern replacement using a runtime switch and feature detection.

Easy Python 3.10+ Aug 9, 2026 Microservices patterns 14 views 0 copies

Python code

31 lines
Python 3.10+
from dataclasses import dataclass

@dataclass
class PaymentService:
    def process(self, amount: float) -> str:
        return f"Legacy processed ${amount:.2f}"

class StranglerFig:
    def __init__(self):
        self._new_service = None

    def attach_new(self, service):
        self._new_service = service

    def process_payment(self, amount: float) -> str:
        if self._new_service and self._new_service.is_available():
            return self._new_service.process(amount)
        return PaymentService().process(amount)

class ModernPayment:
    def is_available(self) -> bool:
        return True

    def process(self, amount: float) -> str:
        return f"Modern processed ${amount:.2f}"

if __name__ == "__main__":
    fig = StranglerFig()
    print(fig.process_payment(100.0))
    fig.attach_new(ModernPayment())
    print(fig.process_payment(150.0))

Output

stdout
Legacy processed $100.00
Modern processed $150.00

How it works

This pattern wraps the legacy and modern services behind a facade. The facade checks whether the modern service is available for each call; if it is, the call is forwarded there, otherwise it falls back to the legacy implementation. This allows you to incrementally migrate traffic without a big-bang rewrite. It's a classic strangler fig approach: the modern system gradually 'strangles' the old one while the interface stays stable for callers.

Common mistakes

  • Forgetting to check availability before calling the modern service, which can crash when it's not deployed.
  • Hard-coding the fallback logic in every caller instead of centralizing it in the facade.
  • Not planning for a rollback path if the modern service misbehaves.

Variations

  1. Use a percentage-based reweighting (e.g., route 10% of traffic to the new service and ramp up over time).
  2. Wrap both services behind a common protocol/interface so the facade works with any implementation.

Real-world use cases

  • Migrating a payments microservice to a new provider while keeping the old API intact for customers.
  • Gradually rolling out a rewritten recommendation engine to a subset of users before full cutover.
  • Replacing an in-house auth service with a third-party identity provider without changing downstream callers.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Microservices patterns

Related tutorials and quizzes for this topic.