The Strangler pattern in practice

In this post I’ll discuss my experiences with the strangler pattern and how it can be applied when rewriting a large, legacy code base.

Introduction

In one of my recent projects, I helped a development team that was struggling with maintaining a large application. One that handled a lot of (web) traffic, but suffered from the usual problems:

  • Complicated, legacy code
  • Fragmented business rules
  • Monolithic design
  • Hard to bring under test
  • Lots of technical debt
  • Painful deployment process with lots of manual steps

The problems were so great that the team did not have much confidence in modifying the existing code, resulting in low velocity and increased lead or cycle times.

Refactor or rebuild?

Initially, the team tried to refactor the existing code to make it more manageable. They enthusiastically embraced the challenge. The Facade pattern was tried. A lot of tests were added. Unfortunately, the code proved to be a tough companion. A few months of hard work netted only 2.5% code coverage and the most complicated parts in the core of the application were still untouched.

Another strategy, rebuilding the entire system from scratch, was also discussed. Such an approach has a number of difficulties. With a straight-up rewrite you may end up rebuilding existing bugs and potentially obsolete features. After all those years, the “why” behind a lot of the changes is hard, if not impossible, to reconstruct. Additionally, it’s often hard to determine where to begin, what the return on investment could be and when the new application is ready for production.

Clearly, another approach was needed. An approach that allowed the team to, over time, replace the existing monolithic application with a modern, service-oriented stack, while still being able to add new features and innovate.

Strangulation

Martin Fowler describes the Strangler Application:

One of the natural wonders of this area are the huge strangler vines. They seed in the upper branches of a fig tree and gradually work their way down the tree until they root in the soil. Over many years they grow into fantastic and beautiful shapes, meanwhile strangling and killing the tree that was their host.

In this project the strangler pattern was applied on a page-by-page basis. Newly implemented pages are handled by new code, old pages by the legacy application.

To get there, the following steps were followed:

  1. First, add a proxy, which sits between the legacy application and the user. Initially, this proxy doesn’t do anything but pass all traffic, unmodified, to the application.
  2. Then, add new service (with its own database(s) and other supporting infrastructure) and link it to the proxy. Implement the first new page in this service. Then allow the proxy to serve traffic to that page (see below).
  3. Add more pages, more functionality and potentially more services. Open up the proxy to the new pages and services. Repeat until all required functionality is handled by the new stack.
  4. The monolith no longer serves traffic and can be switched off.

Strangler Pattern

The proxy

The proxy itself can be implemented in any number of ways. In this particular project we used Apache with mod_proxy, but nginx or haproxy work just as well.

As mentioned before, the proxy operates on the page level. Some pages are handled by the legacy application, other pages by a new service. This can be based on a number of variables:

  • IP address (such that users within the company see a new feature first)
  • cookies (feature toggle)
  • user agent

A possible version of a proxy (using Apache + mod_rewrite) could look like this:

RewriteEngine On
# Serve feature from new service for internal network
RewriteCond expr "%{HTTP:X-FORWARDED-FOR} -ipmatch '192.168.0.1/24'"
RewriteRule ^/feature/(.*)$ ${NEW_SERVICE_URL}/$1 [P,L]
# Proxy everything else to legacy application
RewriteRule ^/(.*) ${LEGACY_URL}/$1 [P]
ProxyPassReverse / ${LEGACY_URL}/

Final thoughts

The strangler pattern is a very useful tool to gradually replace a legacy, monolithic application with a modern service-oriented architecture. The benefits, compared to a traditional rewrite, are clear: less risk, more frequent releases, better return on investment and room to deliver value. Additionally, because all functionality is implemented in new services, a high standard of quality can be applied from the first day:

  • Test-driven and behavior-driven development
  • Fast and comprehensive tests
  • Lots of code coverage (this particular project had a rule of 100% code coverage at all times, more on that in a later post)
  • Continuous Delivery / Continuous Deployment

As always, there are some caveats. Depending on the amount of functionality, strangulating a large application is a process that may take quite a while. It’s very important to keep focus on actually getting rid of the old application. Business involvement & alignment is key to make this work.

Michiel Rook

Michiel Rook is an experienced, passionate & pragmatic freelance coach, developer & speaker from the Netherlands. He loves helping teams and companies to develop better software and significantly improve their delivery process. When he’s not thinking about continuous deployment, DevOps or event sourcing he enjoys music, cars, sports and movies.

9 thoughts on “The Strangler pattern in practice

Leave a Reply

Your email address will not be published. Required fields are marked *