Some time ago Ackee CTO Dominik Vesely provided an intro to microservices in agency development (original post here), where he made a brief introduction and explained the pitfalls and advantages of the architecture. Let me retrospect on the topic after several years of hands-on experience.
Core technologies
TypeScript code compiled to JavaScript to run on Node.js, that runs in Docker running in a Kubernetes pod on Google Cloud Platform. So as you can see, pretty similar to the 3y old article.
Nowadays we use serverless technologies provided by GCP a little bit more. Namely Cloud Run, a serverless container on a Google managed platform, and Cloud Functions, functions as a service. Mostly because choosing serverless can save 70% of the budget for small to medium sized projects.
Project start – microservices yes or no?
When starting a project, you rarely know to what scale it will grow to. I mean you can guess based on the current state, the customer's wish and the business plan promising that in 3 months from now the app will have a few hundreds of users, next year thousands and the year after a few millions.
From my experience, the chances are that it will not get that much traction as the product owner initially planned, or at least not so quickly. With that in mind, making the wrong architecture decision at the beginning may easily result in making the development far more complicated, slower, more expensive and less flexible in the crucial stage of the project.
We are far more cautious when choosing microservices, than we used to be. We switched to the opposite mode I would say – if we don't have to, we don't use them at all.
I think the best choice is to push this decision further, as far as possible. Start with one service that can later become a part of other microservices or stay a monolith. Afterall, services are easier separated than merged.
The benefits of microservices
I stated that "if we don't have to, we don't use microservices at all" – bold statement, nothing forces us of course, we just wait until we can benefit from the architecture. From my experience, these have proven to be the most beneficial to us:
- Independent deployment
- Independent scalability
- Fault isolation
Independent deployment allows us to deploy any service at any time, regardless of the other services. This becomes particularly useful when your app has "foreground" and "background" business processes. An example of a foreground process might be the need of responding to user requests in order for client apps (mobile and web) to work correctly, whereas a background process might be a content generation mechanism (crawling, media generation, data sync to other systems...), which – when stopped for a while – won't drastically affect the end-users. This means that the background processes apps can be upgraded with almost no impact on the users, regardless of the deployment strategy you use. Bear in mind that if designed incorrectly, you may find yourself in a position when you are forced to schedule deployment of multiple services at a time, which isn't as fun as it may seem, because in order to make it right, the release process will get much more complex and will be very hard to automate – we tried that, but that's a story for another day.
Independent scalability helps a great deal when the requirements for the services differ. Let's say you are doing some video generation or a text analysis. This task can take a while and is probably more demanding for computing resources. It makes sense to scale these services a bit differently than others, balancing the total costs and overall application performance. Independently scalable apps are great candidates for new services.
Fault isolation will contain the issues in one of the services without affecting the others directly. If something bad happens to the background apps and they keep restarting due to let’s say out of memory issues, the end-users will probably see a bit of stale data, but other app functionalities will remain intact. Don't take the fault isolation for granted in microservices, depending on the implementation, you can easily let these errors bubble throughout the system, making the user app look broken as a whole.
One other beneficial property, linked to independence, is a technology independence. Your services will probably be communicating through some language-agnostic interface, whether it is some messaging system or Web API protocol, which means you can easily make even the most drastic changes to the application, without affecting the rest. For example, easily upgrade your old JavaScript app to TypeScript, or rewrite the app from TypeScript to Go etc.
I would like to mention one microservices property that is mostly not beneficial to us as an agency – individual ownership (of the service, e.g. by a team). This sounds nice if you have smaller teams and you have enough work for all of them, but in my experience, some parts of the system would require more work than others, or the work is not evenly distributed in time and one sprint you need more work on one service, and then another. We want to keep our developers 100% effective so that when someone is available, they go help with another service or a completely different project. Taking into account programmers’ well being – not forcing them to switch contexts often – makes stable ownership harder to keep.
The cost of microservices
Nothing comes for free, and from my experience the cost of microservices is pretty high. You are taking care of multiple services now – not just one – but all being part of the same product. You’ll spend more time on deployments, monitoring, service configuration, technical design, sharing the data, restoring the data, data inconsistency, code sharing, keeping all the dependencies up to date and vulnerability-free, debugging, local development and more.
The costs for running the infrastructure will probably be a bit higher – more services to host, more databases to provide, inter-service communication – in cloud, both traffic and message queues are paid.
Now this sounds scary, but it may be a price you will gladly pay in order to get the benefits if they are crucial for your product.
Summary: monolith or microservices?
Monolith or microservices are both good and you shouldn’t choose one over the other unless you weigh their pros and cons for your product and development style. Microservices are not a silver bullet – you can tame monolith apps to scale up better than poorly implemented microservices, or both may work fine for you.
Well written microservices for the right use case may bring you a bunch of useful benefits, but from what I can tell, it’ll cost you. The development, infrastructure and maintenance will get more complex, which you can sooth a bit with experience and skill, but the point still remains.
When in doubt, start with a single app, try to push the decision whether monolith or microservices to tomorrow, you may end up being fine with one service after all.