When we say legacy code, what’s the first thing that comes to your mind? It might be an outdated, old system that went through tens, or even hundreds or thousands of developers. It could also be a database in which the lines of code pile up, the methods are barely hanging on to each other and maintenance is a nightmare. Either way, most of us think of it as something bad, which exists mostly in longstanding companies.
However, a complex codebase is something that’s bound to happen in every product, whether it’s a 12 years old company or a 5 years old AI-Machine-Learning-Blockchain startup. Developers come and go, teams rise and fall and every person within the company brings his or her own lines of code into the mix. And before you know it, you have a product that slows down due to its legacy code.
This is when the Strangler Pattern becomes useful.
— OverOps (@overopshq) April 26, 2018
So… What’s does it mean?
The best and most known analogy for the Strangler Pattern was written by Martin Fowler back at 2004:
“One of the natural wonders of this area [Australia] is 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.”
The Strangler Pattern got its name from the same tree, the Strangler Fig, since it shares the same behavior. Similar to the way this tree’s fresh and new vines are replacing the original base of the tree, so does the new code that enters the application.
As our product moves forward, complexity in the form of features and fixes is added on top of the existing code. We need to find a way to make the application live on, without overloading the original structure, or tree trunk. The Strangler Application concept talks just about that – creating the new vines that in return will help the application grow bigger and better.
To put it in other words, our legacy code is harder to maintain, and it’s time to understand how to improve it. Replacing every single element that’s not working as it should is a difficult and sometimes dangerous task, that could lead to the application not behaving as it should or worse, breaking. We need to replace the old with the new, but we need to do it the smart way.
The good news is that it’s possible, through a graduate process in which we rewrite the old, run the new code in parallel to our older one, test the environment and make sure everything is working as it should. Or in other words…
Microservices to the rescue?
Microservices offer a light architecture that promotes the “Separation of Concerns” principle. This way, we can encapsulate different sections of the application, each with its own logic, data, structure and so on and take care of each part on its own.
This “buzzword” has been around for a while, and companies are adopting and adapting to the new elements it brings with it. It’s no wonder, since it might be easier to handle than the monolithic architecture, and it brings a lot of benefits into our workflow and build methods.
Unlike a monolithic architecture, microservices make it easier for us to understand, develop and test different elements of the main product, and it helps make each part much more resilient to architecture erosion.
Microservices allows us to redesign and rewrite critical elements within a codebase in parallel to the old methods and code that’s currently in use. In return, it’s easier to control the gradual transition from the old to the new, making sure the application is still up and running as expected.
There are a few essential benefits microservices introduce into our workflow. The first one is the ability to prevent our monolith structure from becoming even more unmanageable. The second benefit is helping dev and Ops teams to move forward with certain elements within the application or product faster, without relying on other teams or areas to finish or deploy their changes.
Microservices can also improve the build-test-deploy-monitor cycle. They allow teams to complete this cycle for individual elements within the application much faster, without having to wait for testing or deploying other features. You can iterate faster.
However, it’s not all sunshine and rainbows in the microservice architecture, and there are a few downsides that come when switching to this method. One of the most critical elements we need to remember when moving towards a microservice architecture is:
With microservices comes scattered monitoring responsibility
Going back to Martin Fowler, in his microservices prerequisites post he mentioned that
“It’s important to ensure you can react quickly when your monitoring indicates a problem. In particular, any incident management needs to involve the development team and operations, both in fixing the immediate problem and the root-cause analysis to ensure the underlying problems are fixed.”
And indeed, there are some ways in which we could fudge up our microservices, and we now have responsibility for an entire application that lives on “scattered” services. Each service can include its own language, technology, live on a different machine or have a different version control, and it’s up to our teams to handle the load.
The permutations we need to take into account for each service usually translates to higher operations costs both in time and money, when these teams try to predict, understand and even prevent issues before they impact the users.
We can’t deny it, whenever an error surfaces, the top priority on everyone’s mind is finding its root cause and solving it. This becomes an even bigger challenge when we need to understand in which machine, microservices or server this issue had happened, and having the answers to where the code break can give us an extra set of eyes into our application.
While it might sound like a knock against microservices, it doesn’t mean that we shouldn’t move to this architecture. It means that we should be aware of these challenges and that we need to know what we’re looking for in the first place when monitoring our environment.
The move towards a Strangler Pattern, or more accurately, a Strangler Application is something that you should adopt early on if you want to keep your application up and running as optimal as you can.
Microservices can help us to lighten the application, and we don’t have to wait for the Strangler Pattern to appear to start switching towards it. We just need to make sure we know what we’re doing, and know that we’re taking both pros and cons into account.