The microservice architecture is meant to simplify and accelerate development, but only when done correctly
A key goal of the microservice architecture is to accelerate development by improving the developer experience. For example, let’s imagine that you are a member of the Delivery Management team, responsible for developing the delivery management subdomain of the FTGO application. Your focus is on developing the very best courier scheduling algorithm so that your company can maximize consumer satisfaction and beat its competitors in a very cut throat market. Anything that slows down development could jeopardize the very existence of the company. The challenge, of course, is that scheduling deliveries is a complex subdomain. There’s no right answer and the only way to make progress is to constantly iterate and experiment. To do that, you need to be able to frequently make changes, deploy them into production and get feedback.
In this article, I describe why using the microservice architecture can accelerate software delivery. You will learn about two common architectural smells that can have the opposite effect and slow down development. I’ll also describe how it’s insufficient to simply adopt microservices. You also need to adopt the right development practices and team structure.
Let’s start by looking why the monolithic architecture is not always the best choice.
A monolithic architecture can sometimes slow you down
There’s nothing inherently wrong with the monolithic architecture. But for a company such as FTGO, which has many teams working on a very large application using practices such as DevOps and continuous deployment, a monolithic architecture can be an obstacle to the rapid development. In particular, it can be challenging to resolve the dark energy forces, which can result in various problems including:
- Lack of team autonomy - Team autonomy can suffer because teams regularly have to coordinate their changes.
- Slow deployment pipeline - Implementing a fast deployment pipeline that can support a high rate of pushes/PRs (e.g. at least once per developer per day) without becoming a bottleneck can be challenging, if not impossible, due to the application’s size and complexity
- Difficult to change the technology stack - Changing or upgrading the application’s technology stack is especially difficult because it requires all teams to upgrade at the same time.
These issues are especially problematic if you are developing a subdomain, such as delivery management, that requires frequent experimentation.
Using microservices should accelerate development
It often makes sense, therefore, to implement subdomains, such as Delivery Management, that need rapid iteration, as separate services.
For the Delivery Management team, there are several advantages to having a
Delivery Service including:
- Improved team autonomy - the Delivery Management team can develop, build, test and deploy the service independently of what other teams are doing
- Faster deployment pipeline - the
Delivery Serviceis significantly smaller than the overall application and the rate of commits is much lower so its unlikely to be a bottleneck.
- Easier technology stack management - the Delivery Management team can make decisions about technologies, e.g. library versions, without having to coordinate with the rest of the organization.
Delivery Service enables the Delivery Management team to focus on the courier scheduling algorithm and frequently run experiments to improve it.
The team will have less cognitive load, more autonomy and get faster feedback from production.
Or, at least it should but ….
But you need a well-designed microservice architecture
Sadly, using “a bunch of services”, which superficially resembles a microservice architecture isn’t guaranteed to accelerate development. The architecture needs to be well designed. There are two common architecture smells that can hinder development:
- Tightly coupled services
- Excessively fine-grained architecture
Let’s look at each smell in turn.
Tight design-time coupling will kill productivity
One common mistake is to ignore the essential concept of design-time coupling and create services that are tightly coupled to each other.
If, for example, the
Delivery Service is tightly coupled to other services then the productivity of the Delivery Management team will suffer since they will frequently need to coordinate with other teams.
Either the Delivery Management team will constantly be blocked waiting for other teams to make the necessary changes to their services.
Or, they will be frequently changing their service to support changes in other services.
Analyze Git commits to detect tight design-time coupling
One way to detect tight design-time coupling is to analyze Git commits to identify services that frequently change in lock step. If you regularly see JIRA (for example) issues that are implemented by commits for the same group of services, then that’s an indication of tight design-time coupling. For example, the CodeScene documentation describes how to detect design-time coupling (what it calls change coupling) in more detail.
Refactor to eliminate tight design-time coupling
Let’s imagine, for example, that the
Delivery Service and the
Order Service, which is owned by a different team, regularly change together.
One solution would be to merge the two services, which simplifies development although might not address team autonomy.
Another option, would be to reassign responsibilities and redefine APIs to reduce the degree of coupling.
For example, in QConPlus 2021: Takeout burritos and minimizing design-time coupling in a microservice architecture, I described how moving the calculation of an order’s subtotal from the
Order Service to the
Restaurant Service reduced design-time coupling between the two services.
An excessively fine-grained architecture can also slow you down
Tight design-time coupling isn’t the only architectural smell. Another issue an excessively fine-grained architecture, the More the Merrier anti-pattern. That’s because the more fine-grained an architecture, the more complexity it has in the interactions between services. When done right it can, as I described earlier, simplify development since the services are simpler. But if you take it too far, then instead of being able to focus on business logic you will find yourself spending too much on writing collaboration logic.
At most one service-per-team is a reasonable goal
But one rough rule of thumb is that a Service per Team is a reasonable goal. And that the, all too common, one service per developer architecture is probably too fine-grained.
Design a better microservice architecture using Assemblage
Designing a better microservice architecture is difficult and involves numerous trade-offs. A good approach is to use the Assemblage architecture definition process that uses the dark energy and dark matter forces to shape the architecture.
You also need the rest of the success triangle
Simply adopting the microservice architecture isn’t enough to accelerate development. You also need to adopt the other two elements of the success triangle: DevOps as defined by the DevOps handbook; and an organization structure consisting of small, loosely coupled teams, as described by Team Topologies.
Ignoring those two elements is a common anti-pattern of microservice adoption.
Is your microservice architecture helping or hindering development?
There are a couple of ways to answer this question:
- Ask the developers
- Gather the DORA metrics
Survey the developers
A good way to determine whether a microservice architecture is helping or hindering development is to ask the developers using a survey. For example, the DORA DevOps survey includes questions, such as “Complete their work without communicating and coordinating with people outside their team”, which is a characteristic of high-performance teams. You can also ask questions about ease of making changes; and how much time is spent on business logic vs. collaboration logic.
Gather the DORA metrics
It’s also important to gather the DORA metrics including lead time and deployment frequency.
Take the DORA DevOps Quick Check
The DORA DevOps Quick Check is a simple five question assessment of how well you are delivering software.
Improve your DORA metrics
If you are using microservices but your lead time is high and your deployment frequency is low, then that’s a sign that something is wrong. Perhaps, software delivery is slowed down by the architectural smells I described earlier. Or perhaps, there are problems with your process, such as not using DevOps as defined by the Devops handbook. Or with your organization, such as having silo’d teams rather than a set of loosely coupled teams as described by Team Topologies. The DORA website hassome followup assessments that can help you identify what needs to be improved.
- One of the main goals of microservices is to accelerate development
- But achieving that goal requires a well-designed architecture. In particular, you must avoid:
- Tight design-time coupled services - otherwise, you will be constantly coordinating with other teams
- An excessively fine-grained architecture - otherwise, you will spend too much time on collaboration logic
- Determine whether microservices are helping or hindering development by surveying the developers and gathering the DORA metrics