Module 1: Why Not Rewrite

The strangler fig metaphor

Overview

The previous lesson justified incremental with numbers: more accumulated value, less risk per step. This lesson gives it a name and an image —an image so good it became the technical name of the strategy—. In 2004, Martin Fowler traveled to Queensland, in the northeast of Australia, and was struck by some plants called strangler figs. These figs grow in a peculiar way: their seed germinates high up in another tree —on a branch, in a crack of the bark— and from there launches roots toward the ground that go on enveloping the host tree's trunk. Over the years, the fig grows around the tree, covers it, and eventually replaces it entirely: the original tree dies and decomposes, and in its place remains the fig, with a hollow trunk where the other one used to be. Fowler saw in that the perfect metaphor for modernizing a legacy system, and he called it Strangler Fig Application.

Notice what the metaphor captures, because every detail matters. The fig doesn't fell the host tree and then plant a new one in the gap —that would be the big rewrite: cut everything down and spend two years with a stump—. The fig grows around the living tree, taking its place gradually, and at no point is there a clearing without a tree: the host stays alive, giving shade and sustaining the ecosystem, until the fig can hold it up on its own. That's the principle of modernizing in place: you build the new around the old, divert responsibilities little by little, and the system never stops working during the transition. This lesson installs the metaphor and its why —why gradual replacement with the old one always alive avoids the big-bang cutover blackout—. The mechanics of how that diversion is done (the router, the facade, the step-by-step traffic split) is module 3; here, the image and the reason.

Connection with the module. Lesson 5 measured why incremental wins; this one gives it the canonical metaphor that names the whole technique of the guide and that you'll find in every module that follows. The strangler fig is the concrete answer to the question "how do I modernize without shutting down the business?": envelop, divert, replace, retire. Here we work the metaphor and the why; the how —the strangler_router that splits traffic_percent between legacy and modern with fallback— is the heart of module 3. The boundary is strict: if this lesson leaves you itching to see the router working, that's exactly the sign that you're ready for module 3.

An analogy: replacing the bridge without closing the river

A town has an old bridge over the river, the only crossing, over which everything passes: people going to work, cargo trucks, ambulances. The bridge is deteriorated and has to be replaced. There are two ways to do it.

Way 1 — demolish and rebuild. You close the bridge, demolish it, and build a new one in the same place. During the two years of work, there's no crossing: people take an hour-long detour, the trucks don't pass, the ambulances don't either. The town is paralyzed waiting for the new bridge. It's the big rewrite: you turn off the old, and until the new is ready, there's no service.

Way 2 — build the new one next to it and move the traffic little by little. You build the new bridge next to the old one, without touching the old, which keeps carrying all the traffic meanwhile. When the new one is ready, you don't close the old one all at once: first you divert only the pedestrians over the new one, and you observe —does it hold?, does anything fail?—. If all goes well, you divert the light cars too. Then the trucks. And if at any moment the new bridge gives problems, you return that traffic to the old one, which is still there, in an instant. When 100% of the traffic crosses over the new one without problems for a while, then you retire the old one. At no point was the town left without a crossing.

Way 2 is the strangler fig, and it's the pattern that names this guide. The old bridge is your legacy system; the new one, the modern; the traffic you move little by little is the traffic_percent that in module 3 you'll split with a router. The two bridges coexist during the transition —it costs a bit more to have two bridges than one, yes—, but in exchange the river is never closed: the town crosses every day, and the risk of each step is bounded to those you diverted, with the old bridge always ready as backup. This lesson measures exactly that difference: the town that was never left without a crossing against the town paralyzed for two years.

Worked example: the always-alive system vs the big-bang cutover

We're going to model two things. First, the gradual diversion of traffic from the legacy to the modern —the traffic_percent that rises from 0 to 100— and verify the key property: at each step, the system stays alive. Second, compare what happens when a serious incident occurs (a latent bug in the new code) in each strategy: the big-bang exposes it to 100% of the users with no net; the strangler exposes it to a small fraction, with the old one as an instant fallback. We measure the expected impact of that incident:

STEPS = 10  # traffic to the new service rises 10% per step

print("Gradual traffic diversion (legacy -> modern), the system NEVER goes down:")
print(f"{'week':>7}{'legacy%':>9}{'modern%':>9}{'system alive?':>15}")
print("-" * 40)
for step in range(0, STEPS + 1):
    modern = step * 100 // STEPS
    legacy = 100 - modern
    print(f"{step:>7}{legacy:>9}{modern:>9}{'yes':>15}")

# A serious incident during the migration: same latent bug in the new code.
P_FAIL = 0.30            # probability that the new code hides a serious bug
MTTR_BIGBANG = 8.0       # hours to revert a total cutover (the old one is already off)
MTTR_STRANGLER = 0.25    # hours: the fallback to the old one is almost instant
FIRST_STEP_TRAFFIC = 0.10  # with strangler the bug shows at 10% and you catch it there

# "user-hours of impact" = probability * duration * fraction of traffic hit
bigbang_impact = P_FAIL * MTTR_BIGBANG * 1.00
strangler_impact = P_FAIL * MTTR_STRANGLER * FIRST_STEP_TRAFFIC

print("\nA serious incident during the migration:")
print(f"{'strategy':<14}{'traffic affected':>18}{'forced downtime':>18}"
      f"{'user-hours':>16}")
print("-" * 66)
print(f"{'big-bang':<14}{'100%':>18}{'yes (cutover)':>18}{bigbang_impact:>16.3f}")
print(f"{'strangler':<14}{'10% (+ pauses)':>18}{'no (fallback)':>18}"
      f"{strangler_impact:>16.3f}")

print(f"\n  The big-bang exposes 100% of the users in a single moment.")
print(f"  The strangler exposes {FIRST_STEP_TRAFFIC:.0%}, falls back to the old one instantly and "
      f"pauses the advance.")
print(f"  Expected impact {bigbang_impact / strangler_impact:.0f}x lower with the "
      f"gradual replacement - and zero blackouts.")

What to expect. When you run the file, the output is exactly this:

Gradual traffic diversion (legacy -> modern), the system NEVER goes down:
   week  legacy%  modern%  system alive?
----------------------------------------
      0      100        0            yes
      1       90       10            yes
      2       80       20            yes
      3       70       30            yes
      4       60       40            yes
      5       50       50            yes
      6       40       60            yes
      7       30       70            yes
      8       20       80            yes
      9       10       90            yes
     10        0      100            yes

A serious incident during the migration:
strategy        traffic affected   forced downtime      user-hours
------------------------------------------------------------------
big-bang                    100%     yes (cutover)           2.400
strangler         10% (+ pauses)     no (fallback)           0.007

  The big-bang exposes 100% of the users in a single moment.
  The strangler exposes 10%, falls back to the old one instantly and pauses the advance.
  Expected impact 320x lower with the gradual replacement - and zero blackouts.

Read the top table first, the gradual diversion one, because its most important column is the last.

From week 0 to 10, the traffic moves from the legacy to the modern by 10% per step: 100/0, 90/10, 80/20... up to 0/100. It's the fig growing around the tree, or the bridge's traffic moving little by little. But look at the system alive column: in every row it says "yes." At no point —not in week 0, not in 5, not in 10— was the system off. That's the property the metaphor promises and that the big-bang can't give: the transition happens with the system in production the whole time. There's no Saturday at midnight with everything off; there are ten weeks of gradual split with the business running every day. The river was never closed.

Now the second table, the incident one, which measures why that matters when something goes wrong —and in a new system, something always goes wrong—. Suppose the modern code hides a serious bug (probability 0.30, the same for both strategies: the new code is equally immature in both cases).

In the big-bang, when that bug manifests, it hits 100% of the traffic —because at the cutover everything moved to the new one at once—, and reverting takes 8 hours, because the old one is already off and going back means turning it on again and reconciling what was processed. The expected impact: 0.30 × 8 × 1.00 = 2.4 user-hours, with forced downtime and 100% of the users affected. It's the whole town without a crossing while they fix the new bridge that failed.

In the strangler, the same bug shows in the first step, when the new one carries only 10% of the traffic. You detect it fast (the small volume makes it easy to notice the anomaly), you return that 10% to the old one —which is still alive— in 0.25 hours, and you pause the advance until you understand what happened. The expected impact: 0.30 × 0.25 × 0.10 = 0.007 user-hours, with no forced downtime, with only 10% of the users grazed. The result: 320 times less impact, and zero blackouts. It's not that the strangler has fewer bugs —it has the same ones—; it's that when a bug appears, it finds it early, with few people exposed and an emergency exit (the old one) always available.

Put the two tables together and you have the essence of the metaphor: the system never goes down (first table) and when something fails, it fails small and reversible (second table). The big-bang bets that the cutover comes out perfect on the first try over 100% of the system; the strangler doesn't need anything to come out perfect, because each step is small, observable, and reversible. That's the difference between replacing the bridge with the river open and closing it for two years.

Deep dive: why the old one as fallback changes everything

The detail of the metaphor most people overlook is that the host tree stays alive while the fig grows. Translated to software: during the whole strangler migration, the legacy system keeps working and serving the traffic you haven't diverted. It's not a dead system you're replacing; it's a live system that keeps being your safety net. And that net completely changes the risk profile, for three reasons.

First, the fallback is instant and cheap. If the modern one fails in the 10% you gave it, returning that traffic to the old one is changing a number in the router (the traffic_percent back to 0) —it's not rebuilding anything—. In the big-bang there's no fallback: you turned off the old one, so "going back" means turning it on again and dealing with the data already written to the new one. That's why the example's MTTR is 0.25 hours against 8: it's not that the strangler's team is faster at fixing; it's that its reversal is trivial and the big-bang's is a major operation.

Second, you discover the problems with few people exposed. The tacit-knowledge bugs (lesson 4) aren't seen in the code: they're seen when a real customer does something weird. With the strangler, those weird cases hit the 10% of the traffic first, where you detect and correct them before going up to 20%. With the big-bang, all the weird cases of the 100% of the traffic arrive at once, the first day, with no margin to learn from one before the next.

Third, the advance is reversible at every point. You can go up to 30%, see a problem, go down to 20%, correct, and go back up. The migration isn't a one-way arrow toward an irreversible cutover; it's a dial you turn in both directions according to what you measure. That reversibility is exactly what a critical system needs, and exactly what the big-bang cutover doesn't have.

flowchart LR
    subgraph Strangler["Strangler fig: the old one alive as a net"]
      C["client"] --> R["router<br/>(traffic_percent)"]
      R -->|"90%"| L["legacy (alive)"]
      R -->|"10%"| M["modern (new)"]
      M -. "if it fails, back to" .-> L
    end

A boundary clarification, so as not to get ahead of ourselves: everything this diagram shows —the router, the percentage split, the fallback— is the mechanics of module 3. This lesson doesn't ask you to build it; it asks you to understand why it works: because it keeps the old one alive as a net while the new one grows. The full technical name (facade, traffic_percent, branch_by_abstraction, retiring the legacy at 100%) and its executed implementation arrive in the following modules. Here you keep the image —the fig, the bridge— and the reason —the system always alive, the fallback always ready—.

Common mistakes

Confusing the metaphor with a slow big-bang. What happens: someone says "yes, we do strangler" but actually builds the whole new system for months and then turns it on all at once —only calling it "gradual migration"—. Why it happens: the word "strangler" sounds good and is adopted without its essence. How to spot it: ask "at what moment does the new system start serving real production traffic?". If the answer is "at the end, when it's complete," it's not strangler: it's a big-bang under another name, and it inherits all the risk of the single cutover. How to fix it: the essence of the strangler is that the new one serves traffic from early and little by little, with the old one alive beside it. If there's no gradual split of real traffic, with fallback to the old one, you're not strangling: you're rewriting with a more modern speech. The first slice has to go to production with real traffic soon, even if at 5%.

Turning off the old one too early. What happens: as soon as the modern one's traffic reaches a high percentage (say 80%), the team decides "it's almost there" and turns off the legacy to save the cost of maintaining two systems. Why it happens: keeping the old one alive costs, and the temptation to "close the chapter" is strong. How to spot it: if it's proposed to retire the legacy before the modern one has served 100% of the traffic stably for a while, the net is being removed too early. How to fix it: the old one is retired only when the new one has carried 100% of the traffic without problems for an observation period —that's the fig's last step, when the host tree can finally die because the fig holds itself up—. Turning it off at 80% is leaving yourself without a fallback right in the 20% of cases you haven't validated yet, which —per lesson 4— tend to be the weird and dangerous ones. The cost of keeping the old one a bit longer is the premium that buys reversibility until the end.

Treating the traffic percentage as a one-way arrow. What happens: the team raises the traffic_percent and, faced with a problem, insists on "pushing forward so as not to lose the progress" instead of lowering it. Why it happens: lowering the percentage feels like going backward, like admitting failure. How to spot it: if faced with an incident in the modern one the reaction is "let's hold on and fix in the heat of the moment" instead of "let's return the traffic to the old one and diagnose calmly," the pattern's greatest advantage is being wasted. How to fix it: the traffic split is a dial, not a one-way lever. Its power is precisely that you can lower it: faced with a problem, returning traffic to the old one (which is still alive) is the safe play —the users go back to a system that works while the team investigates without pressure—. Turning the dial backward isn't failing; it's using the net the pattern gave you. Module 3 and module 7 (measuring progress) show how to distinguish a healthy step back from a stall.

Exercises

Exercise 1 — Take apart the metaphor. The strangler fig captures modernization in place with several precise details. For each of these elements of the metaphor, say what it represents in the migration of a legacy system: (a) the host tree that stays alive; (b) the fig's roots that come down enveloping the trunk; (c) the moment when the host tree finally dies; (d) that the fig never leaves a clearing without a tree.

See solution
  • (a) The living host tree represents the legacy system in production, which keeps working and serving traffic throughout the migration. It's not a dead system you replace; it's your active safety net, the fallback always available while the new one grows.
  • (b) The roots that come down enveloping the trunk represent the modern system taking on responsibilities little by little: each new root is a slice of functionality (or a percentage of traffic) diverted from the old to the new. The fig doesn't replace everything at once; it envelops gradually, like the traffic_percent that rises step by step.
  • (c) The moment the host dies represents the final retirement of the legacy, which happens only when the modern one already carries 100% of the traffic stably —the last step, when the fig holds itself up and the old one is no longer needed—. It's the turning off of the old system, and it comes at the end, not at the beginning.
  • (d) That there's never a clearing without a tree represents the central property: the system never goes down during the transition. At no point is the business left without service, unlike the big-bang (demolish and wait two years with a stump). It's the "system alive: yes" column in all the rows of the first table.

Exercise 2 — Compute the incident's impact. Using the example's formula (impact = probability × duration × fraction of traffic affected), compare two migrations faced with an incident of probability 0.20. In the first (big-bang), the incident affects 100% of the traffic and takes 6 hours to revert. In the second (strangler), it affects 5% of the traffic and reverts in 0.2 hours with fallback to the old one. Compute the impact of each and the reduction factor.

See solution
  • Big-bang: impact = 0.20 × 6 × 1.00 = 1.2 user-hours, with 100% of the users affected and forced downtime (the old one is already off).
  • Strangler: impact = 0.20 × 0.2 × 0.05 = 0.002 user-hours, with only 5% of the users grazed and no forced downtime (the old one is still alive as a fallback).
  • Reduction factor: 1.2 / 0.002 = 600x less impact with the strangler.

Note that the bug's probability (0.20) is the same for both: the new code is equally immature in the two strategies. The 600x reduction doesn't come from having fewer bugs, but from the other two factors: the strangler affects far fewer people (5% vs 100%) and reverts much faster (0.2 h vs 6 h, because the fallback to the old one is trivial). The pattern doesn't promise flawless code; it promises that, when a flaw appears, it's small and reversible instead of total and irreversible.

Exercise 3 — The cost of maintaining two systems. A manager objects: "the strangler forces me to keep the old system and the new one running at the same time throughout the migration; that's more expensive than only maintaining one. Why is that overhead worth it?". Answer using the metaphor and the lesson's measurements, and say when that overhead would not be worth it.

See solution

The manager is right about the fact: maintaining the old and the new in parallel (the two bridges over the river) costs more than maintaining just one. That overhead is real and must be acknowledged, not denied.

Why it's worth it, with the metaphor and the figures:

  1. The river is never closed. The first table showed "system alive: yes" in all the rows: the business runs every day of the migration. The alternative (a single system) is only cheaper if you accept the cutover's blackout —the two years with the town without a crossing—. For a system with active customers like Mercado, that blackout isn't an option, so the honest comparison isn't "one system vs two," but "two systems with the business alive vs a cutover that risks shutting it down."
  2. The fallback reduces the impact of incidents 320x. The living old one is the safety net that makes an incident affect 10% instead of 100%, and revert in minutes instead of hours. The overhead of maintaining the old one is, literally, the cost of that net. The second table measured it: without the old one as fallback (big-bang), the expected impact of an incident is 2.4 user-hours; with it (strangler), 0.007. That overhead buys a huge risk reduction.

When it would not be worth it: if the system were small, without users in production, or if a blackout were acceptable (a stump for a few days breaks nothing). In that case, maintaining two systems is paying for a net you don't need, and a direct replacement would be simpler. That's exactly the narrow region that lesson 7 identifies —and Mercado, with customers buying now and a critical system, isn't in it—. The strangler's overhead is an insurance premium: wise when the risk is high, waste when the risk is low.

Summary and next step

In this lesson you installed the metaphor that names the whole technique of the guide: the strangler fig. You saw how the fig grows enveloping the host tree and replaces it without felling it, with the tree alive the whole time, and how that translates to modernizing in place: build the new around the old, divert traffic little by little, and retire the legacy only at the end. And you measured it: the gradual diversion keeps the system alive at every step (the "yes" column in all the rows), and faced with a serious incident, the strangler has an expected impact 320x lower than the big-bang —not from having fewer bugs, but because it finds them early, with few people exposed and the old one always ready as a fallback—. The new bridge is built next to it without closing the river.

Before moving on you should be able to: explain each detail of the metaphor (the living host, the roots, the final retirement, the clearing that never stays empty); argue why the old one as fallback changes the risk profile; compute the impact of an incident in both strategies; and distinguish a real strangler from a disguised big-bang.

Lesson 7 closes the module by consolidating the thesis without falling into dogma: modernize in place vs start from scratch. Because rewriting isn't always bad —there's a narrow region where it is the right option—, and you'll score several systems on five axes to see exactly where that region falls, and why Mercado is in the opposite corner. With that you'll be ready for the mini-project, where you'll build the complete defense against Mercado's rewrite.

Resources

  • Martin Fowler, "StranglerFigApplication" (2004) — martinfowler.com/bliki/StranglerFigApplication.html. The original source of the metaphor, with the story of the Queensland figs that inspired the name. Required reading for this lesson. In English.
  • Sam Newman, Monolith to Microservices (O'Reilly, 2019), ch. 3, section "Strangler Fig Pattern" — the mechanics of the pattern (the proxy, the traffic diversion, the retirement of the legacy) that module 3 of this guide develops. In English.
  • Paul Hammant, "Legacy Application Strangulation: Case Studies" — paulhammant.com/2013/07/14/legacy-application-strangulation-case-studies. Real cases of strangling legacy systems, with the traffic-diversion patterns in practice. In English.
  • microservices.io, "Pattern: Strangler Application" — microservices.io/patterns/refactoring/strangler-application.html. The pattern's card with its context, forces, and consequences. Quick reference for module 3. In English.