There is a feeling that microservices are often marketed as a “silver bullet” to replace a monolith. But not everyone likes this approach. In fact, it is sometimes used incorrectly or inappropriately. Below are examples of problems that I was "lucky" to face when using microservices in different companies and which I don't want to repeat in the future.
Phantom Scalability
The bold plus of the microservice approach is scalability. An infinitely large flow of users and a high load are considered inevitable. Because of this, a lot of time is spent on preliminary optimization, and not on useful business features. In fact, a high load is not always present.
The first victim of pre-optimization is a linear business process. It is decomposed into many microservices. A sort of reserve for the future, for reasons of division of responsibilities or illusory scaling. As a result, it becomes harder for business to navigate in the IT landscape and speak the same language as IT, not to mention the problems of navigating the source code for the developers themselves.
Service Interaction Issues
The received services are scattered from monorepa on separate repositories. The services themselves are becoming harder to connect. In this case, the version of the microservice API may begin to diverge from the version of the same API in microservice clients. Then the good old JSON is replaced with Protobuf, and HTTP with gRPC in order to get performance, typing and convenient API versioning.
Unfortunately, solutions like gRPC + Protobuf are not bug-free. Services may fall sequentially due to propagation of errors and known mismatch of the service input-output pair. The codebase is getting bigger, debugging services is much more difficult than with plaintext data, bringing with it a whole new bunch of problems.
This problem should be resolved by a normal testing process. In particular, integration tests. But integration tests need to be written and run for each microservice and their group as part of the business process. This is a rather complicated task, for which they usually do not want to devote extra time. In this case, the microservice integration test can be reduced to the form of a unit test for a monolith.
Old restrictions and habits
With all this, the usual limitations for a monolith wander into microservices. Vivid examples: one programming language and one database for all microservices. In the first case, this is the former limitation of the monolith, in the second, it is a legacy striving to become the “bottleneck” of the entire system. Due to the rejection by the developers and management of the possibility of the existence of a heterogeneous system built in different programming languages, the possibility of choosing appropriate tools for solving urgent problems is lost.
In addition to the above, individual microservices may not have developers responsible for them. Everybody starts to support everything, nobody is responsible for anything. In this case, no one has knowledge about the operation of individual services, except for the last person who changed their code. There is a desynchronization of developers, a loss of understanding of the essence of the work of microservices in relation to the tasks solved within the subject area.
Infrastructure bureaucracy
Microservices are harder to maintain than a monolith. The infrastructure that used to be a pair of servers becomes a small private cloud. It takes a lot of time for developers to support such solutions and creates problems for management in the future. A far-fetched need for additional bureaucracy appears. Individual employees are hired, separate processes are created.
In such cases, rule sets for working with microservices can be exposed to maintain the integrity of the microservices loop. The worst case is to deny even the possibility of running a one-time script or migration, preventing direct access to the path. The bottom line is that the script is styled as a full-fledged service, adding one more line to the long list of microservices.
Future
The result is a system that has many times more noise than a useful signal. Everywhere - from infrastructure to the code of a particular service. From the developer's understanding of the service interaction scheme to the company's management. Programmers unwittingly become master at solving puzzles.
Of course, the classic monolith is no better. It is slow, stateful, hard to recycle, not covered by unit or integration tests, etc. But we can do better! Thanks to the mod for microservices, among many other advantages, we saw the rise of CI / CD and worked on testing. We can now apply them to other approaches as well, not just microservices.
The next time you develop a new system or recycle an old one, no matter how large it is, think about it. Does the company need scalability? Do you need the ability to handle high loads? Are you truly ready for microservices yourself? Or is it better to start with more conservative architectures?
Maybe not build a rocket, but build a simple scooter, because you only need to get to the end of the street?