Software architecture has come a long way since punched cards and monolithic data centers, and the evolution has spawned terms faster than many organizations can learn them. With the cloud now driving software change, and becoming an almost-universal part of enterprise IT plans, we need to understand how cloud software is structured. That means learning its confusing terminology, a process hampered by the lack of clear and accepted definitions.
Nowhere is that more obvious than in the way we refer to the autonomous components of distributed applications. We have “services,” a term that’s almost a decade old but still in use. We have “microservices,” which surely have to be more than tiny little services; and we have “functions,” or “lambdas,” which are often used in “serverless” cloud computing. But what’s the difference, and why should we care?
Building software from components has three goals, ones we could call the “three Rs.” There’s “reuse,” meaning that common functions can be implemented once and used where needed. There’s “redeployment,” meaning that changes to an application can be rolled out with less disruption, and there’s “repair,” meaning that fixing problems is easier because of availability and performance. As it happens, our three critical terms differ in at least one of these areas.
From One End to the Next
“Services,” the original componentization strategy, are still in use today. By convention, a service is a functional component of an application that does some specific business thing. A service-oriented application might have services like “Add Employee,” “Pay Employees,” “Change Employee Information,” and so forth. Business activities, business processes, and business transactions determine the componentization.
Obviously, these are all components of a single application (payroll/personnel, in my example), but while they’re smaller than the application overall, they’re not really very small and they’re also not particularly easy to reuse. You can’t fit “Pay Employees” easily into a customer resource management application or into check processing.
They’re also generally implemented in a specific way. They’re persistent, meaning that they’re presumed to be available all the time, and stateful, meaning that they store information between transactions. The former condition means that they can waste resources in the cloud by being loaded and paid for when not in use, and the latter that you can’t scale performance by creating multiple copies to share the load, because the same information wouldn’t be stored in the copies as in the original.
“Functions” are at the other end of the scale. A function is a software component whose outputs are totally dependent on the inputs; nothing internal is stored or used. Output = 2xInput1 + Input2 is an example of a very simple function. Functions are obviously small and generalized, so you can reuse them easily, and because the output is a function of the inputs alone, you can give an input to any copy of a function and get the same result. That makes them fully scalable and resilient. Because serverless computing loads function only when they’re used, these properties are critical to the serverless cloud. On the other hand, they’re not as obviously useful in the data center, where having a component stay resident can offer performance benefits without creating incremental cloud service charges.
Not All Beers & Roses
Which leaves us with “microservices,” perhaps the most confusing of all our new terms for application components in the cloud age. Google tends to use the term to mean much the same thing as a function, but among developers the term seems to have taken on a different meaning. To them, a microservice is a small component that shares the stateless property of functions, but is like services in that they’re persistent. Functions come and go as they’re used and the use ends, but microservices tend to deploy and stay in place for successive uses. That makes them a kind of waystation between services and functions, and that may be why microservices seem to dominate application planning these days. They’re easy to come to terms with, and they’re useful both in the cloud and in the data center.
Microservices aren’t all beer and roses, though. Any strategy to break applications into distributable components has its own risks. The most obvious is that separate components mean moving work across a network from one to the other, something that takes significant time and creates risks that loss of connectivity will cause an application failure.
If microservices are smaller than services, it follows that there would be more of them, and thus more accumulated network delay and risk. Both are exacerbated if the application isn’t carefully designed. For example, a microservice that’s invoked a dozen times in connection with a single transaction multiplies the delay and risk by a dozen times too. Better to avoid using microservices where that kind of iteration is likely.
Functions can also be misused. You pay only for what you run in a serverless cloud application, but that’s most valuable when you use functions for something you don’t run often. If a function is invoked hundreds of times per hour, it will surely cost you more than it would if it remained resident—i.e., became a microservice.
Componentization offers benefits both in development and in running applications, but even though it’s been around for decades, we’re still grappling with the downside of those benefits. Services, microservices, and functions are all ways to build agile component-based applications, and the fact that even the terms are confusing to many means that special care is needed to build applications in the era of the cloud.