The phrase microservices is known to everyone, even to someone uninitiated in software engineering, but what do microservices actually mean, and why are they suddenly in such demand? Let’s delve deeper.
Microservices Link to heading
- Microservices divide software components into different, loosely coupled, individually deployable, and runnable services. Since the ownership of individual services can be divided amongst other teams, these services are also split into different version-controlled repositories.
- With the growth and adoption of REST API and frameworks that support creating a service based on REST API like Spring boot, Gin, Flask, Ruby on Rails, etc., it has become much easier to develop microservices at rapid speeds.
- Microservices make following Conway’s Law easier, states organizations design systems that mirror their communication structure.
Monoliths Link to heading
For the longest time, we have been using Monoliths to develop software. The monolith is commonly part of a single repository that can be run and deployed. A monolith holds all the software modules into one parent module. As the monolith grows, the modules in the monolith can have their ownership divided amongst teams.
Pros and Cons Link to heading
Monoliths Link to heading
Pros Link to heading
- Cross-cutting changes are easier to make and test.
- Complete software can be tested and released all at once.
- No network latencies amongst modules since every module can be method/function calls.
Cons Link to heading
- With time, as monolith grows, the size of monolith deployment also increases, making deployments take much longer. This problem can be alleviated to an extent by dividing modules into services and libraries, but that also makes the deployment pipeline complex.
- Code reviews in a cross-cutting change in a monolith become more challenging to review as the monolith grows.
- Monolith requires a lot of dedicated tooling like:
- Modularized CI pipeline. Suppose there is a slight change in a specific module. In that case, the whole monolith must be built and run to test changes, which causes the CI feedback loop to be longer and makes development more complicated. The CI pipeline needs to be intelligent enough to detect modules with code changes and build only the specific modules.
- Bugs can be introduced quickly because of cross-cutting changes.
- Monitoring and alerting solutions for a monolith are complicated since the monolith is an entire system owned by the organization.
- Monoliths can make it difficult for new engineers to get onboarded, as it takes time to understand the system.
Microservices Link to heading
Pros Link to heading
- Microservices are individually runnable and deployable, and therefore it is much easier to create and maintain their CI/CD pipelines
- The team which owns the service can dictate the deployment cadence of microservices.
- Monitoring and alerting of the microservices are simplified because of independent units.
Cons Link to heading
- As the number of microservices grows, handling authentication and authorization across all services can become complicated.
- The performance of features spanning multiple microservices can be hampered by network latency.
- Testing patterns for microservices can become complicated because a change in microservice API can break flows that depend on the changed microservice.
- Since microservices are independently deployable services, they can DoS one another.
- The organization needs to define when it is reasonable to create a new microservice or when to add a functionality to an existing microservice.
- Microservices can be expensive as each microservice can have its tech stack with its infrastructure.
So, who wins? Link to heading
Short answer; it depends.
Long answer; there is no right or wrong solution here. There’s a case to make for each implementation. Recently, there have been a lot of discussions regarding how monoliths will be the future. Although they are known to be challenging to scale, there are solutions like Google’s Service Weaver that can help to scale monoliths.
On the other hand, design patterns like service templates, app of apps deployment patterns, and tools like service mesh make scaling a fleet of microservices a lot easier.
Although there is a middle ground, macro-services. A macro-service contains modules that can be tightly coupled within the whole macro-service (modules and service) and is deployable as a single package which reduces the complexity that comes with the increased number of microservices since a macro-service can be big enough to handle complex features within itself and is small enough that the issues of a monolith do not creep in.