When to split up your monolith
I decided against calling this "When to use microservices" because the point isn't about microservices. It is about breaking up a monolith, which is not necessarily the same thing. You could go as far as breaking up your monolith into proper microservices. But, this could also be overkill for your use case.
From my experience there are a couple of indicators that your monolith is too complex:
- You have multiple teams working on distinct areas of the product
- You have areas of the application which would benefit from a different release schedule
- You have different areas of the application which are stressing the system in different ways which have become observable
- You have "cross-cutting concerns" which span multiple teams and are large and/or complex
1 - Multiple Teams and Multiple Areas
There are some applications which may have multiple areas, but no fixed teams addressing them. Team members may have specializations, but may also work in other areas. But, when the complexity reaches a certain point, you also start dedicating resources to particular areas of the application. And, at this point I feel it starts making sense to consider breaking the code base out or at least deploying as individual services as well.
In my experience, it complicates on-boarding and maintaining clean task lists when there is no clear division in the code between teams.
This can create complexity and problems depending on how the lines are drawn. But, in most cases, this can actually be a good thing. As long as the architecture remains solid, it means you are addressing natural problems occurring due to the growth of your application. And the problems faced now should help mitigate or eliminate similar problems in the future as your application (hopefully) continues to grow.
2 - Different Ideal Release Schedules
As an application grows in scale and complexity you will often see certain areas of the application emerge which are more critical or fault prone and need to be addressed more or less frequently. If the code exists in a monolith, then those areas of the code are bound to a release cycle which may not align with the needs of the broader application.
This is actually the one I've personally seen the most often. Especially when a system runs a bunch of background processes. Those processes may run at different frequencies and that may impact the best time to have a release. For instance, a background service which only runs monthly doesn't gain a ton from weekly releases. In fact, it gains a lot of overhead from more frequent releases and can lead to trade-offs in the release cycle regarding what will or won't be present the next time the service runs.
However, if that part of the application is split out, it can plan releases around when the service actually runs.
3 - Resource Loads
This would be the second most frequent issue I see. Applications tend to grow over time. Early on, discrepancies in the resource needs of the varying parts of the system tend not to matter as much.
If you only have a few thousand users of your web application or the overall footprint of the application is just a few hundred MBs of RAM and it never utilizes more than 2 cores for example, it may not matter if 90% of the CPU is taken up by one feature and 2/3 or the RAM is taken by another.
But, as you start hitting the point where you need to start scaling hardware it starts to matter a lot. For instance, if 90% of your RAM usage is locked in Cached objects, spinning up more instances just means duplicating that usage across each instance. And as the cache size increases the only way to address it is by adding more RAM to all of your nodes.
In the cache example, you could split out the cache to a single distributed cache service. That takes that 90% of RAM usage away from the rest of the application. And now you may not need as many nodes for the remainder of the application, or you should at least be able to scale down the resources allocated.
And the same can be done with CPU resources for particularly CPU intensive tasks.
PS - I realize caching is a lame example. But, I have also seen applications cache in-memory and then scale out without a distributed cache. And it is a clear example of the benefits. It is also a lame example because most people would address it by switching to something like Redis. But, that is arguably doing what I'm suggesting. Just via a 3rd party service rather than in-house.
4 - Cross-Cutting Concerns
This is one which I think really only goes hand-in-hand with the others. The caching was an example of this actually. But more broadly speaking, if you have started breaking your monolith into smaller pieces you'll likely also need to replicate a lot of functionality into the newer services. And those shared/common features are often GREAT targets for separating out. Things like authentication or localization for example.
When Not To Bother
If you started with a monolithic architecture and you're not pushing the limits of your infrastructure it may not matter. While it is never a bad idea to get ahead of potential future problems, I would suggest that it makes more sense to focus on monitoring and measuring than to proactively re-architect a solution.
One of the biggest problems and one of the major reasons (in my opinion) that microservices get a bad reputation is due to people doing it prematurely or incorrectly. Making a shift when it isn't obvious it is ever going to be needed means introducing complexity and likely problems with no apparent gains. And making architecture changes without knowing where the pain points are can lead to making things worse.
Knowing when you're likely to start seeing your application performance degrade, which resources will most likely be stressed when it does and which parts of the application contribute the most to that will help you make the right decisions and validate whether they are delivering the gains you hoped for.
For instance, if your application is simple (or just really lucky) the load may be rather uniform. In that case even when you start hitting your infrastructure limits it may make sense to simply add more nodes or scale up rather than change the architecture of the application at all.
As I've said before, what kills most architecture upgrades is a lack of understanding and preparedness. And while you lose some of that if you start off with a monolith, it is quite remarkable what the right measurements can do to a company's willingness to tackle risk.
Comments
Post a Comment