Seems like everyone is writing microservices these days and that might be a good thing, if they solve the problems they are intended to solve. If you read our last post, though, you’ll know that we developers often like to take certain patterns and apply them universally. That lack of context – and not knowing when and why to do something – combined with an architectural pattern like microservices, can yield some very unexpected results.
You’ve got your API in My Microservice
Because I’m a fan of microservices, I’ll generally ask people in interviews to tell me what the biggest advantages of using them are. One of the most common answers is that they are small, targeted APIs (often RESTful) exposing functionality that can be instrumented together to support broader business functionality. Good answer. Speaks to freedom on the client side (and potentially on the server side, too) to be language agnostic so long as it can make a request under whatever protocol is being used (often candidates mention HTTP). Touches on the ability to refactor behind a well-defined interface. Great answer about the advantages of an API, but it doesn’t actually answer my question about microservices specifically.
What Are Microservices Anyway?
To answer the question, you need to know what a microservice architecture is, and unfortunately, that definition often depends on who you ask.
Martin Fowler sums it up this way: “In short, the microservice architectural style is 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.”
Based on this and the evolution of microservices, people have gone on to extend the definition of microservices to include having dedicated per service code bases, dedicated databases, providing resiliency to systemwide problems caused by an issue in a single service, and being easier to scale.
To me, rather than getting pulled into a microservice definition debate, I’d rather focus on the main problems that they are intended to solve:
- Reliable deployability
- Frictionless extension
- Outage resiliency
There’s a lot that’s been written about continuous deployment (as well as continuous integration and continuous delivery, for that matter), and it all comes down to releasing code in an automated, tested, and reliable way. It’s no wonder that continuous deployment and microservices go hand in hand. The decoupled nature of microservices really enables independent services to be deployed seamlessly and easily.
Compare making a one-line change to a monolith versus a microservice. Because deploying a monolith means you are deploying everything, a small change might have a big impact. Even if that is a one-liner, if that was a change to some underlying base code, that may cause a huge ripple effect across the entire application resulting in a substantial test surface area.
No one said you can’t achieve reliable deployability with a monolithic architecture, but the risk is simply much higher. Microservices allow you to reduce that risk. Instead of deploying everything, you simply deploy the service or services that need that change. Even if that’s in some shared code (and you can debate whether you should have shared code across your service offering – I’ll say you can because of the isolated nature of service deployment), you don’t need to take that change across your system all at once.
Tangential to the idea of reliable deployability is a concept I’ll call “frictionless extension”. Because microservices are so targeted, capabilities can be brought online easily by creating new services and deploying them.
The logic follows a similar route as the last point on reliable deployability in that compared to monoliths, the danger of introducing some kind of breaking change becomes very small.
There are some merits of isolating services to their own databases so that you can ensure that a new feature did not inadvertently impact existing database schema that another service depends.
One of the most interesting features of a microservice architecture is outage resiliency – the capability for the system as a whole to continue to function, even if one or more of the services is offline. There are a number of ways to achieve this: some argue that asynchronous communication is central to this and that synchronous RESTful communication leads to what amounts to a distributed monolith and you lose the capability for the system to function. However, don’t assume that just because HTTP is a synchronous protocol that you can’t simply handle that in a non-blocking way (in C# by way of async/await).
Ever heard of Netflix’s Chaos Monkey? It’s designed to systematically go in and terminate a process to test the resiliency of the broader system. Failures do happen, and if you build your services with that in mind, something like a Chaos Monkey running at specific intervals with the proper support allow you to refine your systems and incrementally improve that resiliency, rather than find by your alerts waking you up at 2am.
Everyone Is Doing It
Microservices have been gaining popularity over the past several years. In fact, most of our clients have some sort of microservice initiative going on. Fortunately, they aren’t just adopting them blindly. If you don’t know whether your development initiative needs a microservice strategy, let us know, we can help!