Posted on 2021-01-04 byVlad Călin
If you are here to get a short yes/no answer, then the answer is a big no. Microservices are not worthy investment of time and effort for an early stage startup. I'll tell you why.
But first, how did I come to this conclusion?
Well, I started various side projects and startup attempts, while still being in the "the code is the only thing that matters" stage of my development career. I researched the microservice architecture styles and particularities because it was the new hype everybody was talking about.
I had multiple attempts to build microservices from the get go for my wanna-be startup attempts, but with no exception I had to dump them and try again, this time as monolithic apps with boring technologies.
The issues that kept me from getting to a correct and functional microservice based architecture were the following:
It's no secret that having multiple microservices that talk with each other leads to more complexity. Once you split the project into multiple units, you basically end up with multiple projects and code bases.
All the microservices eventually need to share code, so you'll need to extract all the common functionality in some libraries that need to be distributed to all microservices. That adds complexity for the deployment and dependency management workflows.
When you detect an issue, or you need to implement a certain feature in a shared library, you'll need to do it very carefully so that you don't break all the microservices that use it.
The worst part about having to share code is when the need for an emergency fix occurs. Suddenly, you need to launch the patched version for the library, then re-deploy all the microservices that depend on it.
The other type of complexity it has is the development complexity, thus leading to slower iterations. Each microservice abstracts its business logic and exposes external APIs that can be used by other microservices (either HTTP based API for synchronous communication or message based APIs for asynchronous communication) so debugging becomes harder.
Iterating on adding/removing functionality becomes more difficult because to change an API from a microservies must be done in more steps.
Because in a microservice based architecture the contracts between them must be very well-defined and tested thoroughly, it is much harder to iterate on them and fix past bad decisions.
Other type of complexity they introduce is tooling complexity. A microservice based architecture require a lot auxiliary systems such as logging, monitoring, tracing, individual continuous integration and deployment strategies, individual testing strategies, etc.
Each one of them adds more and more operational overhead and one more tool you either need to learn, to maintain and pay for.
This is a problem very specific to startups or very young projects. You don't know off-the-bat what the requirements are and things change very rapidly without enough time to properly plan and know beforehand all the data models, fields, relations you're going to need.
Especially for startups, requirements change very often, pivots might need to be made, feature planing occurs in short feedback cycles.
For a microservice architecture to work, you need to make the separation into microservices based on the data domains you have. You need the data that is closely coupled to live in the same microservice so you'll avoid unnecessary network calls or deal with data inconsistencies.
Initially, going for a monolithic architecture is more suitable because you keep everything together and for an early stage startup it is crucial (and happens very frequently) to be able to add, remove or change data models quite often.
In my opinion, when you start a new project, you need to build it as a monolithic application and then later on, after you reach a certain scale, reach product-market-fit and your data domains are well defined and don't need to make changes frequently, you can start splitting frequently used and performance critical functionality in separate microservices.
But until then, the most important step (and the most difficult) for a startup is to define how their data looks and settle down to a suitable data configuration and build the microservices starting from there. But more than often, startups by definition need to try things out and iterate, thus being incapable of figuring out from the get go how their models will look like. It's an iterative model that takes time and effort.
This point was slightly touched in the Increased complexity that leads to slow iterations section, but I'll expand on it more.
Microservices by nature need a lot of auxiliary technology to function properly, such as discovery, load balancing, circuit breaker implementation, reporting, monitoring, observability and tracing. All these things are somewhat necessary in a proper microservice architecture and they need to be set up and integrated with all present microservices.
Once you do that, migrating tools will become a lot harder.
More than that, communication between microservices is contract based, meaning that each one of them must describe and expose a set of APIs that can't change. Changing anything in their contracts require a long migration path which needs special attention and very comprehensive testing strategies.
Removing something from a microservice would require a deprecation period, in which the existing dependent microservices need to be migrated away from that functionality.
Replacing an API will require creating the version, deprecating the old one and maintaining both until it is safe to remove the old one permanently.
And each such migration needs to be tested properly with a very comprehensive testing suite, otherwise you might end up in a situation where you need to re-introduce a removed API just to keep your services live.
This problem is much more critical in a fast moving startup environment, where you are sometimes forced to cut corners due to various constraints (and the most commonly cut corners are tests), the risk of breaking some microservice interaction is even higher.
Microservices appeared as a response to the need to scale horizontally a product, once you reach the cap of vertical scaling, or the need to move a lot of the number crunching in the background while keeping the main client facing services responsive.
But these kind of problems are not the kind of problems startups face. If you suddenly need to scale your applications to handle millions of active users, then I suppose your business can not be considered a startup anymore, and you can afford dedicated teams of highly skilled people to build and maintain several microservices.
But as a startup, your biggest concern is figuring out the business model that works and finding the product market fit. These struggles are not tech related, but product and business related.
So, in my opinion, as a startup, to go for a microservice architecture requires too much overhead and has too many disadvantages to make it worthwhile.
Having and maintaining a successful microservice fleet requires significant manpower and resources, things that startups often don't have, or if they do, they would be better used developing features and capturing market share.
For a startup, starting as a monolithic application with boring (and battle tested) technologies is more than enough. Even though building is important, it's all about building the product that people want to pay for, it's not about the code.