Challenges and red flags
We've discussed a lot of advantages that the microservice architecture has over a monolith, but migrating is a massive undertaking that should not be underestimated.
Systems get started as monoliths, as it is simpler and allows for quicker iteration in a small code base. In any new company, pivoting and changing the code, searching for a successful business model is critical. This takes preference over clear structures and architecture separations—it is the way it should be.
However, once the system matures, the company grows. As more and more developers get involved, the advantages of a monolith start to become less evident, and the need for long-term strategy and structure becomes more important. More structure doesn't necessarily mean moving toward a microservice architecture. A great deal can be achieved with a well-architected monolith.
Moving to microservices also has its own problems. Some of them are the following:
- Migrating to microservices requires a lot of effort, actively changing the way an organization operates, and a big investment until it starts to pay off. The transition will probably be painful, as a pragmatic approach is required and compromises will need to be made. It will also involve a lot of designing documents and meetings to plan the migration—all while the business continues to operate. This requires full commitment and an understanding of what's involved.
- Do not underestimate the cultural change—organizations are made of people, and people do not like change. A lot of the changes in microservices are related to different ways of operating and doing things in different ways. While this empowers different teams, it also forces them to clarify their interfaces and APIs and to formalize communication and boundaries. This can lead to frustration and resistance by members of the teams.
- There's also a learning curve in learning the tools and procedures. Managing clusters is done differently than a single monolith, and developers will need to understand how to interoperate different services for testing locally. In the same way, that deployment will be different from traditional, local development as well. In particular, learning Docker takes some time to adapt. Plan accordingly and give support and training to everyone involved.
- Debugging a request that moves across services is more difficult than a monolithic system. Monitoring the life cycle of a request is important and some subtle bugs can be difficult to replicate and fix in development.
- Splitting a monolith into different services requires careful consideration. A bad division line can make two services tightly coupled, not allowing independent deployment. A red flag in that means almost any change to one service requires a change in the other, even if, normally, it could be done independently. This creates duplication of work, as routinely working on a single feature requires changing and deploying multiple microservices. Microservices can be mutated later and boundaries redefined, but there's a cost associated with that. The same care should be taken when adding new services.
- There's an overhead in creating microservices, as there's some work that gets replicated on each service. That overhead gets compensated by allowing independent and parallel development. But, to fully take advantage of that, you need numbers. A small development team of up to 10 people can coordinate and handle a monolith very efficiently. It's only when the size grows and independent teams are formed that migrating to microservices starts to make sense. The bigger the company, the more it makes sense.
- A balance between freedom and allowing each team to make their own decisions and standardize some common elements and decisions is necessary. If teams have too little direction, they'll keep reinventing the wheel over and over. They'll also end up creating knowledge silos where the knowledge in a section of the company is totally nontransferable to another team, making it difficult to learn lessons collectively. Solid communication between teams is required to allow consensus and the reuse of common solutions. Allow controlled experimentation, label it as such, and get the lessons learned across the board so that the rest of the teams benefit. There will be tension between shared and reusable ideas and independent, multiple-implementation ideas.
- Following the Agile principles, we know that working software is more important than extensive documentation. However, in microservices, it's important to maximize the usability of each individual microservice to reduce the amount of support between teams. That involves some degree of documentation. The best approach is to create self-documenting services. We'll look at some examples later in the book on how to use tools to allow documenting how to use a service with minimal effort.
- Each call to another service, such as internal microservices calling each other, can increase the delay of responses, as multiple layers will have to be involved. This can produce latency problems, with external responses taking longer. They will also be affected by the performance and capacity of the internal network connecting the microservices.
A move to microservices should be taken with care and by carefully analyzing its pros and cons. It is possible that it will take years to complete the migration in a mature system. But for a big system, the resulting system will be much more agile and easy to change, allowing you to tackle technical debt effectively and to empower developers to take full ownership and innovate, structuring communication and delivering a high quality, reliable service.