Proposing the Strangler Fig Solution: Part 3

Hey everyone, Jamie here.

This is the final part of my series on joining a new company with an established team and a legacy application.

In Part 1, I focused on the human element: the critical importance of listening, building trust, and respecting the team's deep-seated knowledge.

In Part 2, I dove into the code itself, treating the decades-old monolith not as a mess, but as a historical artifact—a “technical archaeology” project to map its structure and understand why it was built the way it was.

Now, I've arrived at the most delicate moment of all. I have the team's trust, and I have a functional map of the system. The business wants to build new, modern features. We, the dev team, want to use modern tools and practices. But the monolith is brittle, slow to change, and has no test coverage.

What do we do?


The Fantasy vs. The Reality

Every developer, upon seeing a legacy system, has the same fantasy: “Let's just rewrite the whole thing!”

We dream of wiping the slate clean, spinning up a fresh Laravel 11 project, and rebuilding everything “the right way” over six months. We present this to the business, they get excited, and we get to work.

This fantasy is a trap. It is, in my opinion, almost always the wrong answer.

The “Big Bang Rewrite” is a recipe for professional disaster. It dramatically underestimates the millions of tiny business rules hidden in the legacy code (the very rules I was discovering in Part 2). The project invariably takes three times as long and delivers half the functionality. By the time it's “finished,” the business has been treading water for two years and the new system is already out of date.

You can't just stop the business. The monolith is a critical, revenue-generating asset. You can't turn it off.

So, you don't rewrite it. You evolve it.

The Strategy: The Strangler Fig Pattern

The solution I've started to discuss with my new team is a well-known strategy, most famously named by Martin Fowler: The Strangler Fig Pattern.

The metaphor is simple and brilliant. A strangler fig starts as a small vine on an old, established tree. It grows, wrapping itself around the host. It gets stronger, puts down its own roots, and thickens. Eventually, after many years, the old tree inside rots away completely, leaving a new, healthy, hollow tree in the shape of the original.

This is our technical roadmap. We don't touch the old monolith. We build around it, piece by piece, until it's no longer needed.

Here’s the pragmatic, step-by-step plan I've proposed.

Step 1: Build the “Proxy” (The Trellis)

First, we need to control the flow of traffic. Right now, all traffic hits the old monolith directly. We need to put a “facade” or “proxy” in front of it.

This is the only piece of infrastructure we need to add. All user traffic will now hit this proxy first. Initially, its only job is to pass 100% of the traffic straight to the monolith. For the user, absolutely nothing has changed.

But for us, everything has changed. We now have a central control point.

(You all know my love for Caddy. It's the perfect tool for this. We can configure it to act as a reverse proxy, forwarding all requests to our monolith. It's clean, simple, and handles the automatic HTTPS we need.)

Step 2: Pick Our First “Vine” (A New Feature)

The business wants to build a new feature. Let's say it's a new, modern “User Dashboard.”

Great. We do not build this inside the monolith.

We spin up a brand-new, clean, modern Laravel 11 application. This is our first “vine.” We build this new dashboard the “right way”—with proper tests, DTOs, modern PHP, and a clean architecture. It's a self-contained application. It might even have its own database, or (more likely) it will safely read data from the monolith's database (but not write to it!).

This is a huge win. The team gets to work in a modern environment, the feature gets built quickly, and it's fully tested.

Step 3: Update the Proxy (The First “Strangle”)

Once the new feature is ready, we make one tiny change to our proxy's configuration. We add a rule that says 'any traffic for the new dashboard's URL should be routed to our new, modern Laravel app.' Then, we add a second rule that says 'all other traffic should continue to go to the old monolith, just as it always has.'

And just like that, we've started the process.

The user has no idea. They go to an old page and get the monolith. They click “Dashboard” and are seamlessly routed to our brand-new Laravel app. The two systems are living side-by-side, appearing as one.


The Long-Term Payoff: How We Win

This is where the magic happens. We got a quick win by shipping a new feature. Now, we repeat the process.

We continue this, one piece at a time. We “strangle” the old application, one route at a time. We can prioritise based on business value. The most critical, complex parts (like the order processing I found) can be left for last, once we've built our confidence and our new app is stable.

This approach is pragmatic and respectful. * It reduces risk. We're only changing one small thing at a time. If the new “Contact Us” form has a bug, we just remove that one routing rule from the proxy to instantly roll back. * It delivers value now. The business doesn't have to wait two years for a rewrite. They get their new dashboard next month. * It's great for the team. We get to build new, greenfield code immediately, which boosts morale. New hires can be pointed to the new, clean Laravel app, making onboarding a thousand times easier. * It's safe. The old monolith, the heart of the business, keeps running, untouched and stable, for as long as it needs to.

This is the long game. It's not a quick, dramatic victory. It's a slow, steady, and safe evolution. It’s the only way to turn a 20-year-old application into a modern one without stopping the very business it exists to serve.

It’s the pragmatic way.

Cheers, Jamie C