Hey all,
Welcome back - it’s another Monday and I hope you’re all getting your week started off right!
Summer of Shipping met last Thursday (YouTube) with a short presentation on Git and some more demos. Projects are beginning to form and it’s exciting to see people starting to make progress!
This newsletter continues to grow - so thank you to everyone for all of your support and sharing the word with those who might benefit from reading 🙏
Today’s topic is the Strangler Pattern - an important idea to have in your toolkit when rewriting systems.
We’ll start with a story.
Storytime
In early 2017 and I was working on a rewrite of my company’s “Cart” system. There’s a long backstory, and one day I’ll tell it in more detail, but the gist of it was we had been working on the project for a long time and were just entering QA testing.
In the weeks before testing, we integrated the rewritten Cart module back into the greater system. There were numerous touch points and each one felt extremely risky. I felt a sense of absolute dread sending my work over for testing.
It came as no surprise to me when our QA team returned the bloodiest bug report I had ever seen from a regression test. It was clear that we had our work cut out for us.
At this time, I wish I could say I found a way to cut scope of our project, deliver 50% of the promised business value quickly, and put a plan in place to deliver the other 50% over time.
Sadly, that wouldn’t happen until much later - I hadn’t yet internalized the ideas of the “Strangler Fig Application” and the “Strangler Pattern”.
Enter the Strangler Fig
Martin Fowler wrote about a concept he called the Strangler Fig Application. In that post, he has this to say about rewrites:
Much of my career has involved rewrites of critical systems. You would think such a thing as easy - just make the new one do what the old one did. Yet they are always much more complex than they seem, and overflowing with risk.
He’s absolutely right. A simple “rewrite” is never so simple. Especially, if you try to do it all in one go. Instead, he presents an alternative strategy, one that’s based on the analogy of a Strangler Fig
Strangler Figs are beautiful and often massive trees that grow via rather nefarious means. They start by sprouting on a host tree, sending their root systems down along the trunk of their victim. Over time, the strangler fig envelops the old tree, choking it off from the real world. The strangler stands magnificent, in the place of where its host once was.
Martin Fowler describes this because it’s a metaphor for an alternative approach to rewriting legacy systems. Instead of rewriting the system to be a perfect replacement, identify new business value that the legacy system just can’t deliver. Maybe its architecturally cramped and can’t integrate with something important. Maybe it’s slow and everyone is too terrified to tweak something to speed it up.
Find this business justification, and deliver it rapidly in a new, adjacent system - one that has no interaction with the legacy system. As a greenfield project that has access to the most productive technologies your company allows, it should be able to materialize quickly.
What next? Well, this new Strangler Fig Application has essentially sprouted on its host. It has delivered value, the business is excited, and slowly, it will be fed additional resources to expand its responsibilities. In time, it’ll capture all of the business use cases of the system it was designed to replace.
The Strangler Pattern
The metaphor of a Strangler Fig is vivid, but how do we apply it? Sadly, Martin Fowler describes a few tactical steps to apply the idea, but those examples make the Strangler Fig seem like an esoteric strategy.
Thankfully, Azure has put out a great description of the Strangler Pattern - something that’s a lot more general. Here’s their illustration of how to use it:
The key point to note is that in this case, the Strangler is a Facade, a wrapper around both the legacy and “modern” systems. This differs slightly from Martin Fowler’s formulation. For Fowler, there isn’t necessarily a Strangler Facade, just a legacy and modern system that take different amounts of traffic.
But the idea of throw up a facade to route between the legacy and modern systems is subtle genius.
Let’s make this concrete and look at how a Strangler Facade can help us.
A tiny Strangler Facade
Imagine we have a legacy system and a modern system. They both perform a function called foo
// OLD
legacySystem.foo(input)
// NEW
modernSystem.foo(inputV2) //different input
Then adding a Facade might look like this:
// StranglerFacade
stranglerFacade.foo(input)
// Implementation of stranglerFacade.foo
function foo(input) {
if (isV2Enabled) {
const inputV2 = mapToV2(input) // this mapper is crucial
return modernSystem.foo(inputV2)
} else {
return modernSystem.foo(input)
}
}
The reason this is so important, is that it reduced the number of changes we had to make. Imagine if foo
was being called 10 times. Well, now, we only have to make the change in one place, not 10.
And from behind this facade, we could gradually start pointing more methods on the facade to use the new system. It reduces the surface area of changes drastically. Instead of making maybe 100 changes (the number of calls into the system), you’d only have to change 20 (the number of methods on the system).
(For implementation details in a statically typed language, see the Appendix)
How the Strangler Pattern would have saved Cart
The biggest problem with the Cart project was that the integration points between our module and the system we were merging into were numerous and complex.
We had to go find and replace every call against the old Cart system and call the new one. This was not a straightforward process, the old system had all sorts of weird flags and parameters that just weren’t intuitive.
// old system
cartManager.addToCart(productId, quantity, true, false, 0)
And there were dozens of these methods, each being called a handful of times. Translating each call to the new system proved to be extremely buggy, as our QA team found.
The Strangler Fig mentality encourages you to find business value you can deliver quickly, and do a rewrite of something impactful, but impossible in the old system.
The Strangler Pattern instructs you how to do it: throw your system behind a Strangler Facade.
Had I really taken those two lessons to heart, I would have:
Noticed that 50% of the project’s business value came from rewriting one massive function. Deliver that business value quickly by rewriting only that function. Let’s call it
doX
(as in “Do X”).Put that
doX
function behind a Strangler Facade.
Doing both of these would have A) reduced the time till delivery of business value B) given us a platform to build additional functionality.
The Strangler Facade was the crucial implementation idea that we were missing in Cart. It would have crystalized in my mind that Stranglers are not something to be used in rare cases, but something every rewrite should be equipped with.
Conclusion
Performing rewrites of software systems is hard. But it’s also extremely common. There’s a very good chance you’ll see one early in your career!
When you do see one, you need to have the Strangler Pattern in your toolbox. Take Martin Fowler’s Strangler Fig mentality and ask yourself “what value can we deliver quickly if we write something from scratch along the edges of the system?”. A good candidate is something small (less than 3 months) or something stateless (i.e. a calculation of sorts).
Then ask yourself “Can we reduce the size of surgery if we add a Strangler Facade?”.
The Strangler Pattern might not fully click until you’ve struggled with a rewrite gone wrong, but I want to create a mental marker for you to come back to this idea when you need it. The next time someone on your team is clamoring for an upheaval of the system, remember the Strangler!
Until next time!
Phil
Appendix: A Note on implementing Stranglers with Interfaces
If you’re using a language that has interfaces, you could do this even better by extracting an interface from your legacy system. (If you’re using Java, IntelliJ can do this automatically for you) By doing this, you would get:
// LegacySystem
class LegacySystem implements LegacyInterface { ... }
// StranglerFacade
class StranglerFacade implements LegacyInterface { ... }
Using this LegacyInterface means you can trivially swap the LegacySystem for the StranglerFacade - as all callers expect a LegacyInterface.
This is an example of using (abusing?) the Liskov Substitution Principle (L from SOLID).