There are many reasons you may want to split up a monolith. I’m not going to go into too much detail, but some of the more common reasons include wanting to be able to scale functionality separately, modularizing the codebase, and avoiding the noisy neighbor problem.
No matter your reason or reasons for wanting to do so, the project is risky. You’re attempting to either lift and shift existing code or rewrite existing code (or a little bit of both). In either case, the goal is little or no change in functionality for your users. To make it harder, it’s often the case that at least some of that code is old and current developers retain little or no context on the original use cases or intent. With all of that work, you want to ensure that you don’t end up in a situation that’s still as entangled as before, but now with network delays for calls between services (a distributed monolith).
Before you get started on the journey of splitting your monolith, I recommend spending time thinking about your service architecture and what your domains are. This is worth careful consideration to help with team allocation and ownership, as well as helping to prevent that distributed monolith we just discussed.
The next step is identifying whether you want to use a Big Bang or Strangler Fig approach. Without going into too much detail, the Big Bang approach replaces everything at once. The main advantages to this approach are that you have more flexibility to rewrite everything, think about things holistically, and even remove features entirely. It also takes less time from start to finish since it’s your sole focus for that time.
Meanwhile, the Strangler Fig approach starts with things least tightly coupled and removes them piece by piece until you are fully split. This approach has the advantages that it’s much faster to see the first benefit, it’s typically lower risk (since, at any given time, there’s less in flux), and it’s usually easier to convince upper management because it allows the team to continue working on new features while simultaneously doing this in the background. I’ve talked to people using both approaches, but most companies pick Strangler Fig.
Assuming you pick Strangler Fig, the next choice is which services you will start with. The best candidates are going to be services that are less tightly coupled to other things, services that you might be changing anyway, net new functionality, and foundational services that other services may need to build on or leverage (for example, Authorization, Authentication, an API Gateway, etc.). Other great candidates include any functionality that needs to scale at a different rate from everything else and anything already causing problems. If a particular service checks multiple of these boxes, even better, pick that one first.
Minimizing Your Move
First of all, before you try to lift and shift or rewrite your code, it’s worth ensuring you’re not moving anything you don’t need (There are more details in the first use-case in this article). But, at a high level, use feature flags to wrap log lines to check if a given code block is still in use. The feature flags help limit how many logs are generated while the logs identify which lines of code are still in use.
Splitting Out a Service
Now that you’ve identified a service to start with and you’ve removed any dead code, one of the common ways to start the whole process is by creating the interface on top of your existing service that matches what you want your new service’s interface to be. It may be that you already have great APIs, in which case you can use what you already have, but there’s a decent chance that you may find yourself wanting to update a few things, and before getting started is the perfect time to do so.
Next, you want to build out your new service while keeping that interface exactly the same. This is very important because it will allow you to route traffic to either your existing monolith or to the new service seamlessly and with no client changes.
Test Your New Service
While having this interface is important for the transition anyway, the nice thing about it is that it also sets you up to leverage it for parity, load, and stress testing. Load testing is a type of testing to ensure that a new system can handle the expected load. Stress testing, meanwhile, is meant to find the breaking point of a system or, at the very least, ensure that it can handle up to a point higher than the expected load. Parity testing, meanwhile, compares results from two systems to ensure that the new system has been implemented to match the old system and no features have been omitted. All three test types are vital to lowering risk as you move to a new service and can be painlessly done with feature flags. (Check out our other blog post for more details about how to set this up.)
Turn It On!
Finally, after you’ve leveraged the flag directing between your old and new systems for testing, you can use that same flag to start to flip traffic over to the new system fully. Using the flag allows you to slowly ramp up that traffic and quickly turn it off if any problems arise. Now it’s time to start on the next service!
Get Split Certified
Split Arcade includes product explainer videos, clickable product tutorials, manipulatable code examples, and interactive challenges.
Switch It On With Split
Split gives product development teams the confidence to release features that matter faster. It’s the only feature management and experimentation platform that automatically attributes data-driven insight to every feature that’s released—all while enabling astoundingly easy deployment, profound risk reduction, and better visibility across teams. Split offers more than a platform: It offers partnership. By sticking with customers every step of the way, Split illuminates the path toward continuous improvement and timely innovation. Switch on a trial account, schedule a demo, or contact us for further questions.