Chapter 1. The Microservice Revolution
These days, you can’t swing a dry-erase marker without hitting someone talking about microservices. Developers are studying Eric Evans’s prescient book Domain-Driven Design (Addison-Wesley). Teams are refactoring monolithic apps, looking for bounded contexts and defining a ubiquitous language. And while there have been countless books, videos, and talks to help you convert to microservices, few have spent any appreciable time asking if a given application should be a microservice.
There are many good reasons to use a microservices architecture, but there are no free lunches. The positives of microservices come with added complexity. Teams should happily take on that complexity, provided the application in question benefits.
This report will give you a set of principles you can use to help focus your efforts and avoid wasting your time. As you read through the following pages, ask if your application benefits from a given principle. If you answer “yes” for one or more of the following principles, the feature is a good candidate to be a microservice. If you answer “no” for every principle, you are likely introducing accidental complexity into your system.1 But why are so many companies adopting microservices in the first place? What even is a microservice?
How Did We Get Here?
Odds are you’ve noticed a major shift in how your organization approaches infrastructure. Servers were once homegrown—a bespoke artisanal approach. And while you may enjoy the idiosyncratic when it comes to your morning coffee or your favorite food truck, unnecessary variables in your infrastructure lead to sleepless nights. During this “Paleolithic” era of software, servers were a very expensive resource, forcing developers to deploy as many apps to the same hardware as possible. Doing so may have placated the accountants, but it introduced its own set of problems.
With shared resources, one application’s bug could impact every application on a given box. Upgrades to common libraries were constrained by the slowest-moving system in the environment, making currency projects a frustrating series of freezes and testing. Organizations often kicked the can down the street rather than deal with vital (but not flashy) currency projects. Afraid of breaking anything, many companies poured proverbial concrete over their infrastructure, allowing fear to lead them down a dark path. Who knew there was such a thing as #YodaOps?
Fear is the path to the dark side. Fear leads to anger. Anger leads to hate. Hate leads to suffering.
Yoda, Star Wars Episode I: The Phantom Menace
Delivering code to production was its own source of frustration as well. As an application moved from dev to test and beyond, things that worked in one region were just as likely to stop working in a different region. You could spend days pounding your head against the wall trying to determine what, exactly, was amiss, wasting countless hours that could have been better spent delivering features and functionality.
The software industry doesn’t stand still; in fact, it seems to be in constant flux.2 Infrastructure is a different game today, and servers are commodities. Rather than spend countless hours troubleshooting a bad server, it is faster to just destroy the instance and spin up a fresh one. With public cloud providers, containers, and app platforms, you now bundle your application with everything it needs and move that abstraction from server to server. In truth, you probably aren’t moving anything, you’re just updating a routing table.
With these higher-level abstractions, if it works in dev, it will work in test because you are working with the exact same thing, eliminating an entire class of bugs from the procedure. It also liberates development teams—they are no longer subject to the tyranny of the slowest-moving application. If your application needs a spiffy new library version, go ahead, you aren’t affecting anyone else! You can focus your attention on solving problems for your customers, not undifferentiated heavy lifting.
Your teams can deliver in days or weeks instead of months or years, allowing you to be far more responsive to business changes. You can run A/B tests and perform hypothesis-driven development instead of hazarding guesses and arguing in the project room. Disruption affects every industry, and you can no longer afford to rely on “We’ve always done it that way.” You must evolve just as one large bank did (described in Figure 1-1). If an organization in a heavily regulated field such as banking can adapt, so can you.
None of these benefits magically happen; they are the culmination of cloud environments, cloud native architectures, DevOps, and the cultural shift inherent in any transformative technology. The transition takes time, but the results speak for themselves, allowing you to deliver business critical software consistently and repeatedly.
Microservices are ultimately a reaction to plodding monoliths and heavyweight services, as well as modern cloud environments. Monoliths suffer from a lengthy list of problems, starting with long ramp-up times for new developers, all the way to build times measured in phases of the moon. With years (or decades) of technical debt, modularity breaks down over time, making it very difficult to refactor and add vital new features. Scaling typically means adding capacity for the entire application, not just the pieces that needed it, leading to single-digit resource utilization. Out of this frustration was born the microservice.
What Is a Microservice?
There are nearly as many definitions of a microservice as there are developers touting them as miracle cures. Before delving further, the key definition is the one inside the walls of your organization. Whether it adheres to the Platonic ideal form of a microservice isn’t nearly as important as getting everyone on the same page. There is a reason why a glossary is often one of the most important artifacts in any project room.
Ultimately, microservices are a reaction to monoliths and heavyweight service oriented architectures (SOA), as well as the capabilities of cloud environments.3 The issues with poorly structured monolithic architectures are legion, from low developer productivity caused by massive codebases to the inability to target compute resources to the bits that need more performance, there are no shortage of headaches. Software is not immune to the second law of thermodynamics; over time, the modularity of the monolith breaks down and it takes longer and longer to add new features and functionality.
Some are partial to defining a microservice as any service built and maintained by a two-pizza team. Personally, I am a fan of defining them as something that can be rewritten in two weeks or less, since that reminds us that microservices should be, well, small. Of course, there is no stock answer to the question of how microservices should be defined—it depends on the volatility of the services in question. While I support two-pizza teams, that definition won’t help you determine just how many services said team can support. If the microservices are stable, a two-pizza team might be able to support ten or twenty of them. However, if the services are constantly changing, the exact same team might struggle with more than five!
Rather than debate designations, think in terms of characteristics. Microservices are suites of small, focused services that embody the Unix ethos of small, focused tools that do one thing and do it well.4 Microservices should be independently deployable, independently scalable, and free to evolve at different rates. Developers are free to choose the best technology to build services around business capabilities. In a nutshell, microservices are an example of what I refer to as the zeroth law of computer science—high cohesion, low coupling—applied to services.
Microservices Are a Tool
At the end of the day, microservices are a tool and it is up to you to properly apply them. It is just another approach. An architectural style. A pattern. It is not Mjölnir, and it will not solve every problem you’ve ever had on a given project.5 If you’ve ever been to a home improvement store, you might have noticed there is an entire aisle full of hammers. Some are smaller, some are larger; some have smooth faces, some knurled. Some include a handy hook to help you pull out the nail you inevitably bent, while others will help you tear down a wall with minimal fuss.
The expert knows when to pick up which hammer, while the novice often falls in love with the first hammer they ever used. If you are demolishing a shed, a sledgehammer is your friend. While it will help you get rid of that eyesore, using it to put up some beautiful maple trim will probably result in a trip to urgent care. You face a similar choice with your applications. Are microservices useful? Absolutely. Are they right for every situation? Of course not.
Microservices really do offer some impressive benefits. But they come at a price. Don’t pay the complexity tax unless you get something in return. In other words, no, not everything should be a microservice! Use them where they make sense. Use them where they add value. If you need one (or more) of the principles that follow, go forth and prosper! If not…well, there’s always serverless.
5 In case your Norse mythology is a little rusty, Mjölnir is Thor’s hammer. You may recall that it was destroyed by Hela (in the Marvel Cinematic Universe, at least).