This is the first in a series of posts by Richard Rodger on the subject of the microservices architecture. The series will run over the next few months.
The enterprise software industry has a problem. I’m going to point out the elephant in the room. In fact, I’m going to walk right over and give its trunk a good, hard tug.
Most enterprise software projects are still delivered late and over budget.
Let’s think for a moment about what the software that we build is supposed to do: deliver business logic. Yep, the grubby business of helping our clients to sell stuff and make money.
Not too elevated a goal, is it? Not exactly up there with the ceiling of the Sistine Chapel, or the software system that took the space shuttle into orbit.
Yet our behavior often suggests that we – the developers and architects of enterprise software – think of ourselves as creators of great art, or that we are writing the world’s most expensive code. We strive for perfection. We think big. We tend to rely on intuition (take as an example unit testing, which feels like a good idea).
Let’s bring ourselves down to earth. Our code has business problems to solve, users to serve, content to deliver. As our first step back on terra firma, let’s re-acquaint ourselves with our old friends, science and engineering, as embodied in components.
Hello components, my old friend…
Components are the big gun in the software architecture arsenal. They have been the core principle of many software approaches in recent years: OO, Web services, structured programming, to name a few. Components are a force multiplier, letting you build big things out of small things. With components, you only have to solve a problem once. Yet, we know that our efforts still result in delays and legacy messes. So why haven’t components delivered?
To usefully answer this question, we need to re-phrase it to better reflect the balance of responsibility: why don’t we – developers and architects – deliver more effectively with components?
As I see it, we haven’t been using them right, or thinking about them right, for a long time. We haven’t become adept at writing reusable components that deliver business logic. Reusability is rarely achieved. Each new project writes new business logic, with new bugs, and new technical debt.
Sadly, it is precisely this reusability – or composability – that would really speed up development.
The basic principle of composability is that you combine parts to create something new. Composition is powerful, and works because it only does one thing: adding components together. You don’t modify components; you write new ones to handle special cases.
This approach is based on the principle of additivity (more on this below). Additivity gives you speed; you can code faster because you never have to modify old code, and technical debt inside a component does not grow. That is the promise of components.
Monoliths betray the promise of components
The monolith, with its object-oriented architecture, betrays this promise. Reusability is hard in monolithic systems because of the very nature of those systems. Large, cumbersome units of code are difficult to combine usefully or efficiently. Monolithic systems are layered and complex, with business logic distributed everywhere from the front end to the database. They are riddled with dependencies. New versions affect many parts of the system, causing the inevitable regressions.
This is where microservices-based architectures come in. In a network of microservices, the default operational action is to add or remove a single microservice instance, and then to verify that the system is still healthy. This is a very low-risk procedure compared to big-bang monolith deployments.
A new unit of software construction
The heart of the microservice idea is the concept of composable components as the units of software construction. In other words, in microservices architectures, reusability is designed in from the start.
Another core concept of microservices architectures is that composition works well only when the means of communication between components is sufficiently uniform. Messages are the most uniform and least debt-prone method of communication. The protocols and formats don’t matter; messages are transport-independent. From the perspective of an individual microservice instance, and from the perspective of the developer writing that microservice, there are only messages arriving, and messages to send.
Microservices key concepts
The table below summarizes the key concepts of the microservices architecture:
When it comes to specification, microservices architecture design takes a ‘messages first’ approach. Ironically, you do not start by asking what services to build; you start by asking what messages the services will exchange. Once you have the messages, natural groupings suggest what services you need to build. This gives you a direct route from business requirements to implementation. (In my book, I argue that a design approach that accommodates under-specification is the winning strategy.)
The deployment of many hundreds or thousands of individual services in production is not a trivial task. We can turn this perceived weakness to our advantage: it forces you to automate the management of production systems. Automation is good because it’s less error-prone. It’s just about possible to get by with manual management in monolithic systems. The result is that the manual approach is used more often in those systems, with the associated increased likelihood of errors.
I started this post talking about people – us, the developers and architects who create enterprise software. Many of us are familiar with the old saying ‘No battle plan survives contact with the enemy’. As I indicated earlier, ‘the enemy’ can often be ourselves. This is the case when, with the best of intentions, we work against our own interests and those of our clients.
Microservices help us to become our own ally again. By removing the expectation of perfection in enterprise software systems, they make it safe for everyone in the development team to be more honest. They render unnecessary certain behaviors associated with perfection-seeking in the monolithic world, such as daily stand-ups.
Microservices are disposable. This is perhaps one of the greatest benefits of the architecture from a people perspective. Because each unit of code is small, and there are so many of them, and they can be replaced or rendered obsolete at any time, developers do not need to feel that their career progression or their ego is bound up with any one piece of code that they’ve written. That’s the kind of environment I like to work in.
In my next post in this series, I’ll be identifying the current problems with custom software development as I see them, and I’ll be looking more closely at the reasons behind these problems.
For more detail on any of the issues raised here, and a lot more as well, the first five chapters of my new book, The Tao of Microservices, are available now (the first one is free!).
Contact Richard on Twitter.