To be a microservice: How smaller parts of bigger applications could remake IT
If your organization could deploy its applications in the cloud the way Netflix does, could it reap the same kinds of benefits that Netflix does? Perhaps, but its business model and maybe even its philosophy might have to be completely reformed -- not unlike jumping the chasm from movies-by-mail to streaming content.
The idea that big things may be comprised of thoughtful combinations of lots of small things, may be the least new technological innovation since the wheel. But microservices architecture for data center software is not really about subdividing code into smaller, bite-size chunks. Rather, it's about a complete reconsideration of the question of what servers do -- not only the role they play on behalf of their software's users, but the way they are tended to by IT personnel.
A microservice is its own entity
A server's principal function is to deliver applications to users. Conventionally, those applications have been installed directly onto servers. Virtualization added one layer of abstraction to that model, enabling a virtual server to move to the physical server that best suits it.
Up to now, service-oriented architecture (SOA) has been about how an application is developed, not how it's deployed. A "service" in the context of an application is a single, discrete function that has well-defined inputs and outputs (its "interface"), and which may (or should) have code that is portable between applications. It may be part of a reusable library, or it may be part of an application's core structure. But a user doesn't (and shouldn't) perceive several services -- just one application.
More than simply portable code, a microservice is an independent component -- ideally, one that is contextually separated, or decoupled, from any application in which it plays a role. It is deployable independently. This way, if a plurality of users require more of certain specific services, the server cluster's orchestrator can respond by scaling up (multiplying) just the services without replicating the entire application, and scaling them back down when they're no longer necessary.
Many so-called containerized environments (for instance, Kubernetes) do this anyway, including for software that wasn't designed this way to begin with. Microservice architecture is, by definition, designing software this way to begin with.
What makes microservices worth considering
It has been said that a picture is worth a thousand words. Yet no picture may be worth fewer words than the above, which is an actual capture by Netflix engineer Bruce Wong of the microservices running in Netflix, circa 2014. By now, a similar image might look like a solid blue wall.
If you, like a growing number of people, have read thousands of pages of literature, listened to hours of lectures, participated in dozens of discussions, and still find yourself asking, "But what the Sam Hill are microservices?" here at last is a response that will, at least, satisfy everyone if only to a limited extent.
A microservice is a software component:
Designed intentionally to be executed over a network;
Contacted and triggered through that network exclusively by way of an API;
Designed with the interest of being operated independently of any specific application (even if that's what ends up happening anyway);
Intended to be executable in parallel along with multiple other instances of itself.
"If you're trying to think about your software system, and what its architecture is," explained Martin Fowler, the software design consultant credited with creating the majority of the microservices ideal, "the first thing you have to do is figure out, 'What is important? What do we, as the technical leadership of a project, consider to be the most important things in that? . . . What is the thing in the code base that I have to keep at the top of my head when I'm working on it?'"
Fowler, speaking to an O'Reilly conference in 2015, was defining the "architecture" part of "microservices architecture" in an effort to make it easier for himself to define the front part of that phrase. Even for its creator, that's still the hard part.
What is top-of-mind for microservices architects?
There are numerous interpretations of microservices, just as there are many directions that "impressionism" can take in painting. What these interpretations have in common is a comprehension of software as interchangeable parts rather than assembled wholes. If you were to accept such a comprehension yourself, in any form that it might take, you might find yourself thinking in one of the following ways:
Rather than continuing to build your core IT functions as colossal applications that you have to upgrade every few years in massive, exodus-like upheavals (big enough to justify your effort, small enough to remain feasible), you think of your company's software in terms of its disassembled parts.
A small team may be responsible for the upkeep, maintenance, and evolution of one or more parts.
The production cycle for upgrading or, when necessary, replacing each part may be much shorter and easier to expedite.
Yet the evolution of the application they jointly constitute may actually proceed at the same rate -- not necessarily faster, just in smaller steps that are achievable with less effort, and (hopefully) less expensively.
Why even bother with microservices?
The benefits of microservices architecture are not self-explanatory. Perhaps you've had a family member who has craved the ability, wherewithal, or time to construct an entire landscape out of Legos, and please, oh please, won't you let her? Besides having fun, are there other benefits to microservices in a real-world, "production" IT environment, perhaps your own? For now, the answer -- like the response to the plea for a billion Legos -- sounds like a parent stalling her child for time: "Maybe. We'll see."
The reason microservices architecture should be of interest to you anyway, even if you're not a software developer or IT operator, is because it resides at the epicenter of the most important global debate affecting IT departments today: What's the most cost-efficient and practical way for an organization to deploy its applications to its users?
You've seen where this debate started. You know how public cloud infrastructure works, at least generally speaking. You may have leased a virtual server yourself from a public cloud provider such as Microsoft, Amazon, or Google. That may still be sensible for running a website on an Apache or NGINX server installed on a virtual machine (VM). But if your job includes managing a software platform for your manufacturing, design, research, or marketing division, you know it needs more than one processor. Leasing as many VMs as you need processors, is not cost-effective.
Containerization provides a solution to this dilemma, but you've seen that already too. You know it's feasible to remake an application so that it runs on a cluster of servers. And you might have known, or just learned about, serverless deployment models. Serverless makes it feasible for your organization to pay for the service your application provides, rather than the infrastructure that supports it.
But at some point down the road, be it today or three years from now, it will come time for your organization to design new and highly functional applications specifically for, and probably within, these new operating models. What is the design pattern that application should take, and should your IT workforce be trained to work with it right now?
Microservices architecture may not quite be the complete answer just yet. But it's asking all the right questions, at the right time.
What does a microservice do?
Software development topics are often abstract, which makes it difficult to sell to a general audience without first concocting an attractive metaphor. A popular book on microservices architecture from a major publisher treated readers to a 62-page prelude before approaching the simple question of what a microservice is. And then, the book proceeded with almost an apology for why it's so hard to explain.
Rather than apologize here, let's give it a shot.
Do one thing well
Suppose you have an e-commerce application. One function within that application might be to examine the items in a customer's shopping cart, and update their profiles with current prices and shipping data, especially for those customers who tend to stockpile items in their carts and make purchases whenever they feel like it. An update routine such as this would probably already have been expressed as a reusable code module.
A microservice that performs the function of this reusable module may be a separate program, written in the same language as the rest of the application or a completely different one. It is not compiled as part of the application, nor installed with it in the same server or virtual machine. Instead, it is maintained as a separate unit (for Docker, a separate container) and triggered through some network-driven mechanism, such as ordinary HTTP.
This way, the microservice becomes a kind of pattern for functionality. The orchestrator may not even call one instance of this pattern into existence until it's absolutely required, although such a strategy might introduce latency. Preferably, a standard number of instances are set to go on demand. Whenever that user demand level rises beyond a specified level, the orchestrator may choose to replicate the microservices, calling many more instances into being.
Toward a more substantive definition for microservices
That's the unofficial, tech journalist definition of "microservices." The most trusted formal definition among software developers actually states, quite explicitly, that it should not be treated as a formal definition. Martin Fowler and James Lewis advanced the notion that microservices:
Represent business processes individually
Are intended to run together as a suite
Are designed to be deployed automatically
Communicate with one another with a "lightweight mechanism" such as HTTP for synchronous operations (that take place in sequence), or a reasonably lightweight message queue such as APMQ for asynchronous operations (that may take place in parallel)
May not necessarily utilize the same programming language or frameworks as one another, in order to work together
Josh Evans was a senior engineer with Netflix, the organization that pioneered the design and implementation of microservices in production. As a witness to the birth of the model, and now a senior director of data service engineering at GitHub, Evans offered some additional elements to Fowler and Lewis' model, during a presentation to a developers' conference in late 2016:
A microservice requires minimal, and preferably no, coordination with other code components in order to be completely functional. That is, it should be self-driving, capable of determining its purpose the moment it's deployed ("instantiated").
Rather than replicate an entire server to add power to an application, an orchestrator should be capable of instantiating as many copies of a microservice as the application may require ("scaling out"). What's more, several concurrently running instances of the same microservice may have different functions depending on the data they were given or pointed to, or the databases to which they were connected.
As little manual oversight should be invested in microservices environments as possible, especially since the orchestrator may be given liberties as to how to scale instances out and where, resulting in a system whose shape and scope at any one time may be somewhat unfamiliar to human observers.
"Going back to that theme of biology, you can think of microservices as organs in an organ system," explained Evans. "These systems come together to form the overall organism" To help make his point, he showed his audience an animated diagram of Netflix' microservices organism, which incorporates the company's earlier and more conventional design, but was essentially designed from the ground up for microservices. A service proxy acts as the front door for the application. It dispatches requests through a routing layer (which Netflix calls "Zuul") that doesn't just plan a single destination for the request, but the entire chain of services necessary to fulfill that request. The routing layer knows which services, and in what sequence, will fulfill the demand, and not every request will need to traverse the full depth of the system.
The twist at the center of this diagram, like a moth having wandered into a spider web, represents the API -- the common component with which communication takes place. Notice the microservices don't each have their own APIs; instead, they rely on another service to provide communication.
This is what an intentional microservices architecture looks like. It might appear to include the various types of esoterics with which software developers and political philosophers love to play. Yet here's why all of this matters: When an organization commits to a full microservices architecture for its IT, as Netflix did, it no longer constructs its application in versions or staged releases or semi-annual updates. It evolves more continuously, organically, and in Netflix' case in particular, more easily.
Perhaps no other enterprise in the world has the mandate of Netflix: To deliver millions of simultaneous, high-definition, high-fidelity video streams through the most fickle system humanity has ever devised, the internet. So Netflix experiences simply will not translate to most organizations, unless they'd care to transition away from, say, financial services or pharmaceuticals and get into streaming media delivery. Netflix' development patterns may only ever apply to Netflix.
Yet in the search for commonality -- for any fragment of the Netflix experience that may apply to enterprises at large -- there are certain aspects of the more complex, more formal definition of microservices that appear to be less esoteric and more fundamental:
In a distributed software architecture that encompasses any number of servers, or even platforms, simultaneously, the location of a function is denoted by some kind of address that can be resolved through a network. A web service, with which you may be familiar, is such a service that uses HTTP for resolving that address. A microservice is one stage further along in that train of evolution. By design, it is not joined with the other reusable components it will be making use of ("consuming," in some developers' lingo) in the same body of code. So it contacts the other components in its network or in its domain (which, if they're designed as intended, will also be microservices) using the same method.
What's more, each microservice should be capable of making itself discoverable by the process responsible for coordinating, scheduling, and/or orchestrating microservices. By "discoverable," I mean that the orchestrator has a means available to it, to resolve the location of a requested microservice and to make contact with it. In the past, services had hard locations, and HTTP addresses pointed to them. Today, hard-wiring a service to an address works against the whole principle of distributed services. So now, an orchestrator such as Kubernetes may use a reverse proxy such as NGINX to resolve the current whereabouts of a service that's anywhere in the network, given a more general identifier such as the HTTP address (the URI) of the origin of that component in the repository that originally distributed it. Here, NGINX still negotiates with a domain name service (DNS) to look up that current location, but this way, it can link a known component using a permanent identifier, with a temporary or ephemeral address that's likely to change in a few seconds' time.
This is the service discovery principle -- the guarantee that your program can find something that moves around a lot.
Being locatable through service discovery enables a microservice to be decoupled from applications, and therefore independently manageable. Reusability through incorporation is not at all new to code; modern applications in Windows, Mac OS X, or desktop Linux would be impossible without it. But decoupling makes it feasible for the bindings between independent components of code to be mapped out just before their code is actually executed, which is entirely different from the concept with which we all deal today, of "installing" applications in systems.
Fathoming a transition to microservices
To be a microservice is to function as a very different type of program than an application. In the early days of computing, software was a product you could shrink-wrap in a box full of disks and put on a shelf with a price tag. In those days, an application was a class of program with a myriad of interrelated functions -- a word processor, a business process modeler, a presentation graphics toolset.
SOA was originally created to facilitate the software equivalent of such purpose-built components -- things with one function, which adapt themselves to any condition within a given range. Microservices is indeed a form of SOA (though some will disagree even with that). The "micro-" part is on account of an early effort to envision each of these components like ants in a colony. It helped people visualize the concept better if the metaphor likened these parts to something small. In practice, however, there is no size constraint to a microservice. You might think it's against the whole point to have a big microservice, and you'd have several prominent architects agreeing with you.
It was object-oriented programming, particularly by way of the C++ programming language, that first brought SOA into prominence, years before Java. One organization recognized as an authority in SOA, the Open Group, defines a "service" in this context as "a logical representation of a repeatable business activity that has a specified outcome." Note how thoughtfully this definition was crafted: It's not the business activity itself that constitutes the service, but its representation as code. This way, another representation using a different code would be -- rightfully -- a separate service.
Recipes are made to be repeatable: Assemble the exact proportion of ingredients, prepare them and combine them in the specific order, and you're certain to reproduce what the chef hand in mind. Repeatable code is a different beast. It depends on elements capable of changing or redirecting the work flow, such as:
Variables capable of altering the intent of the instructions;
A database whose contents represent, among other things, the state of the work product (e.g., the house being built, the automobile being designed, the shipping routes being plotted);
Decoupling of the repeated code's context from that of the code that calls it, or passes control to it, ensuring that the function it performs can be performed, perhaps in a different way, on something else.
In practice, functional independence is the most difficult aspect of the microservices ideal to achieve. It means devising an independent component that, with minimal instruction, can essentially educate itself as to its own purpose, execute a task pursuant to that purpose flawlessly, and then terminate itself. Such a component would be relatively easy to automate, in the context of a system were every other component worked exactly the same way.
Think of a robot whose only purpose is to hammer a nail. You give it the angle of the armature, the length of the nail, the percentage of that length extending above the base board, and the relative level of the base board where it can stop hammering. It learns its operating conditions at "birth." It performs its sole function, and then stops.
A functional assembly of such robots would be like most every sci-fi movie ever made: whisking you away to a utopic world of future social orders, without paying any mind to the doubtful likelihood of the sequence of events that would bring such an order about. Maybe that's not important for a movie, but no business has ever successfully changed its operating principles without a plan.
Every technology since the first upgrade to the wheel has required its adopter to plan a period of transition. No organization will be able to adopt a microservices-oriented strategy without plotting a course for how its IT departments -- both its code and its people -- can make the methodological transition there from wherever they are now.
What microservices would change
If any enterprise software platform purports to enable microservices, its job must be to maximize the amount of leverage that may be gained through the reuse and redistribution of small chunks of code. Making these modules as functional as they can possibly be, directly impacts three parts of the broader IT ecosystem:
The work patterns of software developers, enabling them to more rapidly build modules and applications that utilize work that they and others have already done;
The management strategies of IT operators, who may now deploy services across multiple platforms while keeping applications integrated between those platforms, maximizing utilization and conceivably driving down real, physical costs -- including cooling.
The economics of cloud-based deployment, enabling organizations to focus on smaller, more flexible, more manageable billing components that drive up efficiencies while driving down operating expenses.
Yet there's an underlying assumption with respect to any organization that would adopt a microservices model, either in one lump sum or in stages: That its developers and its IT personnel will be capable of envisioning, planning, and then producing services as components of applications to come -- as parts of a later machine. Put another way, microservice developers need to perceive their work independently of the context in which it would later be put to use.
This is not a natural way of thinking -- a bit like an artist producing a designated segment of a painting without regard to the rest of the canvas. And this is where the microservices debate stands now: How can an organization teach its people to think abstractly, in service of a goal that can only be explained abstractly and realized in the abstract, while at the same time promising benefits that are both measurable and concrete?