The Strengths and Weaknesses of Microservices
There has been significant buzz around microservices lately, enough to generate some hype. After implementing heavy and cumbersome SOA solutions for more than a decade, are microservices the solution the industry has been waiting for? Or, are microservices simpler than monolithic solutions?
Before discussing these issues, it would be a good idea to include a definition. In their article entitled Microservices, James Lewis and Martin Fowler define the microservices architectural style as an approach to
developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies.
Some of the benefits of microservices are pretty obvious:
- Each microservice is quite simple being focused on one business capability
- Microservices can be developed independently by different teams
- Microservices are loosely coupled
- Microservices can be developed using different programming languages and tools
Those benefits make it look like microservices are the perfect solution, but aren’t there some drawbacks?
Benjamin Wootton, CTO of Contino, is currently architecting a system based on microservices and has encountered a number of difficulties detailed in an article called Microservices - Not A Free Lunch!. Here are a digest of those.
Major Operations Overhead
20 services can become 40-60 processes after failover and resilience are introduced into the equation. The number of processes grows when load balancing and messaging middleware are added. Operating and orchestrating all these services can be a “daunting task”, according to Wootton:
Productizing all of this needs high quality monitoring and operations infrastructure. Keeping an application server running can be a full time job, but we now have to ensure that tens or even hundreds of processes stay up, don't run out of disk space, don't deadlock, stay performant. It's a daunting task.
The operations processes need to be automated, but because “there is not much in terms of frameworks and open source tooling to support this” it results that “a team rolling out Microservices will need to a make significant investment in custom scripting or development to manage these processes before they write a line of code that delivers business value.”
DevOps Is a Must
Wootton believes that an organization implementing microservices needs DevOps because
You simply can't throw applications built in this style over the wall to an operations team. The development team need to be very operationally focused and production aware, as a Microservices based application is very tightly integrated into it's environmental context.
Since many of the services will probably need their own data stores, the developers will also need to have a “good understanding of how to deploy, run, optimize and support a handful of NoSQL products.”
Services rely on the interfaces between them to communicate. Changing one service’s interface implies changing other services, observes Wootton:
Change syntax or semantics on one side of the contract and all other services need to understand that change. In a Microservices environment, this might mean that simple cross cutting changes end up requiring changes to many different components, all needing to be released in co-ordinated ways.
Sure, we can avoid some of these changes with backwards compatibility approaches, but you often find that a business driven requirements prohibit staged releases anyway. …
If we let collaborating services move ahead and become out of sync, perhaps in a canary releasing style, the effects of changing message formats can become very hard to visualize.
To avoid introducing “synchronous coupling into the system,” Wootton believes that sometimes is useful to add certain code to different services, leading to code duplication which is a “bad idea as every instance of the code will need to be tested and maintained going forward.” A solution would be to use a shared library between services but it “won't always work in a polyglot environment and introduces coupling which may mean that services have to be released in parallel to maintain the implicit interface between them.”
The Complexity of a Distributed System
As a distributed system microservices introduce a level of complexity and several issues to take care of, such as “network latency, fault tolerance, message serialization, unreliable networks, asynchronicity, versioning, varying loads within our application tiers etc.”
Wootton considers that microservices usually make use of asynchronous programming, messaging and parallelism, which can be complex when “things have to happen synchronously or transactionally,” requiring to “manage correlation IDs and distributed transactions to tie various actions together.”
Testing is another issue to consider when doing a microservices architecture since it may be “difficult to recreate environments in a consistent way for either manual or automated testing,” wrote Wootton. Also,
When we add in asynchronicity and dynamic message loads, it becomes much harder to test systems built in this style and gain confidence in the set of services that we are about to release into production.
We can test the individual service, but in this dynamic environment, very subtle behaviors can emerge from the interactions of the services which are hard to visualize and speculate on, let alone comprehensively test for.
Brady, a reader commenting on Wootton’s article, added from his experience attempting to transition from a monolithic app to microservices:
I was working on a project moving towards micro-services from a monolithic application and we ran into a lot of the hurdles mentioned in your post. We ended up having a lot of code duplication (since the services were built on different languages and frameworks) - lots of little "implicit contracts", for example mapping User data from one service to another (one service not necessarily needing all the same data as another). While there are some clear benefits of the approach, probably not something you want to jump into without some careful planning. Our approach in the end was to modularize the monolithic application (so we can share code repository, deployments, and code between modules - but still have nice, loosely coupled components) and pull out the modules into their own independent micro-services that can be deployed/managed independently only if really necessary.
Dennis Ehle, another reader, shared his experience with microservices, concluding that indeed microservices come with a cost:
We're currently implementing CD pipeline automation framework for a client that has over 450 developers working across 50 services (or microservices). To me one the most fascinating aspects of this architecture is none of those 450 developers will ever write a single line of code to support a customer facing user interface. All customer facing UX work is performed by a different group entirely.
While the level overall flexibility, risk reduction and cost savings this client currently enjoys is significant and a direct result of moving away from a monolithic architecture, there is no doubt a very real "microservice tax" paid due to many of the factors you very articulately outline in your post!
On the other hand, Steve Willcox, yet another reader, sees opportunities in challenges introduced by microservices:
Being one of the tech leads on transforming a monolithic Java application to a SOA implementation, I've come across everyone of the issues you raise but instead of seeing those as problems I see them as opportunities to build software better. …
You say "Substantial DevOps Skills Required". I see that as a good thing. It gives the people writing the code the responsibility of how it runs in production. Going to a SOA implementation almost forces you to a de-centralized DevOps where the service team developers do the DevOps as compared to the old school "throwing it over the wall" to the centralized operations team. It's a big positive to have the dev team be responsible for the operations of their code. …
Yes, there are more services compared to a monolithic application to build, test, and deploy but in today's world those things should all be automated. Having two vs. twenty that follow the same automated patterns should not be that much more work. …
Regarding code duplication Willcox said that it may not be that bad:
I used to be a purest in this area in that any and all code duplication is bad but then realized this purity sometimes resulted in much more costly and complex solutions and no one won but idealism. I'm now more practical in this area and simplicity has to be a part of the equation as well. I really like what Richard Gabriel wrote in The Rise of Worse is Better about this.
In conclusion, we could say that the microservices architecture has a number of appealing benefits, but one must be aware of its challenges before embarking on such a journey.