Microservices: Where Anything is Possible
How awesome was 1999?
We were pre-tech bubble. We were pre-pendulum swinging too far towards governance and compliance and corrupting the tooling that was advancing us. We were pre-closing the doors to innovation in the name of safety. And, the best part, The Matrix was playing in theaters.
I promise, I don’t date myself with that movie reference for no reason. I bring up The Matrix because at the end of the movie, Neo (the main character for those still in grade school when it came out) described a world without constraints. “… [I’m going to show you] a world without rules and controls, without borders or boundaries. A world where anything is possible.” This is the tech world today. The promise of The Matrix is being realized by the microservices movement.
At this point, the analogy is probably not lost on anyone that, in the purest sense, microservices are built without borders or boundaries. Because of that, microservices are fundamentally (and literally) changing how we view the world around us. Today, the future is being built with more veracity and velocity than ever and the impossible becomes possible if we just say “yes, if”.
And, that’s the hard part. “Yes, if” we change our approach to technology. “Yes, if” we challenge what we view as important attributes of an architecture. “Yes, if” we can change our mindset.
Microservices: the who
I’m not an authority on microservices; but, I do have the very fortunate benefit of working with the smartest, most innovative and creative people in the world whose common goal is to do amazing things for their brands with technology. They have shaped my perspective and fundamentally changed my view of what IT is all about.
Who are they? They are from every industry, of every size and they are of different hierarchical levels. Regardless of those differences, however, they all have one thing in common. The thing about these people is not that they are creating a new paradigm of IT because it’s cool, they are doing it because they are part of the creative consumer phenomenon brought to traditional corporate environments to use technology as a competitive differentiator.
In 1986 Eric von Hippel termed these people Lead Users, and defined them as consumers of products or services that have current needs generally not provided by commercial market offerings. For that, they innovate and iterate existing products to meet their emerging needs. Put control in the hands of innovators, you get something special. Take existing technology, iterate it to meet the demands of a modern software factory, and you get microservices.
So, that’s the secret. If you want to innovate faster, to get to market faster, to create competitive differentiation through technology, you need to allow the innovators to innovate. You must remove the friction slowing down that innovation. That’s the entire secret to building a successful microservices strategy.
Microservices: the what
But, take a step back for a second, there’s some housekeeping we must do before we get there.
I should acknowledge the technical tenants of what a microservice is, because, if I don’t, I think they may take my half-nerd membership card away. I assume that you already have a good idea of the academic definition of a microservice after you’ve (already most likely) read Martin Fowler, but, at the risk of oversimplifying, a microservice service can be defined as something that is independently deployable; has bounded contexts; that are exposed through APIs; with minimal dependencies; that are assembled to form applications (probably in a devops-ie kind of way); and are distinguished from monolithic applications. Further, you probably already have a good understanding that an implementation of this architecture (technically) includes microservice services and microservice patterns, and it’s those patterns that allow services to, well, service clients or other services.
Ok, so all that is crazy interesting, yes, but that’s not the most interesting part to me. The most interesting part to me is “why” do they need to be designed like this and what exactly does the patterns, especially, do for me to support the service?
Microservices: the why
To answer the question about “why” is a microservice those things, I think we have to go back to 1967 (ok, I’m not quite that old, and I don’t have another movie reference) to Melvin Conway. He said “any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization’s communication structure”. When you have a network team concerned with the network, and a database team concerned with the database, and a Windows team and a Linux team and a middleware team and, and, and… we create artificial barriers in our system that is a replica of our organization. Melvin Conway is describing the journey to the Monolith.
With that, Melvin Conway is also teaching us that true innovation happens when small teams (2 pizza teams?) control their own innovation destiny (cradle to grave). To do that, we must remove inter-dependencies created by organizational design and constraints.
Microservices: the when
Ok, so that’s the service. But the other half of the implementation is the pattern. In my opinion, it’s the pattern that has created the inflection point in the way we design modern systems. The pattern speaks directly to the “when”.
The journey from monolith to microservice isn’t fast, easy, or fun. An organization looking to take this journey has to be able to be built to change. Netflix, a great example of this journey, deconstructed their monolith over 7 years. That’s 7 years of multi-master replication. 7 years of multiple technology stacks. 7 years of multiple architecture investments. And 7 years of learning how to pick themselves back up after failing. But, what their 7 years tells me, is that anything worth doing in this space will require that we live in several worlds before we’re done (if we’re ever done). So, the wrong time to take on a microservices initiative, for example, would be when we’re trying to reduce costs; or when we’re trying to simplify our architecture; or when we’re unwilling to assume risk. When we begin to accept those fundamental truths of balance, it’s then that might be the right time to start exploring microservices.
Matt McLarty et. al in a book I consider to be the standard on the topic of balance in the new application economy discusses “speed and safety at scale in harmony” as the tenants of modernization initiatives. The book describes how technology has to marry with people, and microservices initiatives can’t be done without cultural, organizational and technical shifts.
With my experience, I offer an addendum to that idea and suggest that the harmony the @apiacademy seeks to explain can be further deconstructed with the simple representation of a 3-legged stool supported (in order of importance) by tenants of #1 velocity, #2 stability and #3 efficiency.
Microservices: the where
When I talk about this stool to those seeking the holy grail of the microservices promise (but maybe still approach it like they did in SOA architectures and haven’t really accepted the complete shift in microservices mindset), I always add a dramatic pause to my speaking rhythm and wait for someone to cock their head to the side, or squint at my deck with a skeptical eye, or to raise a hand to suggest that stability, as number 2 priority, is antithetical to modern IT demands and just not acceptable with an oppressively demanding corporate expectation.
I LOVE when some go even further and describe that these ideas are disconnected from IT reality and I haven’t considered their specific requirements of twenty-seven 9’s of availability with full change management processes, required security development frameworks and yada yada yada!
…and I totally get it, I really do. And I purposely pause so that they can completely take me down… because, once it’s out, we can start with some objection handling by acknowledging that there are right places (and wrong), for where microservices initiatives should begin. Microservices aren’t silver bullets, and just because you can, doesn’t mean you should. So, the real question is, where do we get the most benefits from the promise of a microservice initiative, and how do we apply that innovation energy?
Microservices: the how
In my mind, microservices are all about velocity, full stop. If an organization is looking to increase velocity, they may want to look at microservices. The IT archetypes of stability and efficiency are important, but secondary to velocity in microservices. The three pillars need to exist in harmony, and it’s the microservices patterns that bring that balance.
So, the real question about velocity is, exactly how?
I talk a lot about removing friction. Removing friction in the development process. Removing friction that delays the introduction of new services and capabilities. Removing the friction of innovation. There’s lots of things that cause friction; governance, compliance, change management, release processes, performance testing, landscape promotion, disaster recovery (and it goes on and on and on). But, what if we could just let developers do what they do? What if we gave them control (remember Conway from above)? What if we let them choose the right tool for the job, and to deploy and deprecate services at will?
Ok, I’ve made some extraordinary statements and promises and maybe even a few IT sacrilegious claims, but, you’ve stuck with me, and for that I thank you. But, entertain me a bit more, because I’d like to suggest that maybe we can.
Polyglotism in a microservices context describes using the right tool for the right job. If a developer thinks that a certain database is correct for their service but operations doesn’t know how to support it, microservices purists say, sure, let them use it anyway. Is that middleware approved? Who cares! What OS, network, virtualization tech, protocol, tech stack have you chosen? Not our concern. It’s up to you because you own it cradle to grave! We have some rules (see Fowler above), but, outside of that, go crazy with your domain driven design.
“Wait! I can’t let my developers be all cowboy and running amuck! Can I?”
I realize it’s not that simple, but I’m trying to get away from “no, because” to “yes, if”. Once we start thinking about “yes, if”, we can start to apply the technology and thought patterns that can minimize disruption caused by the creative process. The question is, of course, how do we describe the “if” part? It’s the “if” that is holding us up now.
When we consider microservices and how they are designed, and we consider how the patterns apply the protection (security, scale, performance, stability, etc) an organization demands, maybe we’re still talking about the same thing, just with a slightly cocked head.
So, the patterns are the secret. When you focus on velocity, the secret lies in how minimize disruption caused by inherent risks in the architecture; risks such as in stability, or efficiency or security or all the other things that the tradition IT paradigms require.
Let’s talk stability
In my mind, stability can be broken down into failure, capacity and integrity concerns. The most interesting and worrisome is, of course, risk of complete service failure.
Microservices are not designed to never fail. Building for stability is expensive to create, expensive to replicate, expensive to change, enhance, replace, deprecate, etc, etc, etc. Rather than build for stability (to never fail) of a service, microservices patterns help us build for the (inevitable in any system) failure event and to offer a recovery.
Keep calm and failover
I exemplify this build to fail idea by talking about the amazon.com recommendations service.
First, let’s describe the consumer experience on amazon.com today. I use Amazon almost every day. I buy lots of things, and Amazon knows a lot about me. So, when I login to amazon.com, the platform offers me a recommendation of things I might like based on my past experiences with them. I’ll over-simplify for the sake of explanation, but I’m sure we can all imagine that this could be a very simple choreography between 2 API’s that are independently deployable, have bounded contexts, and all those other microservices attributes we would expect. Let’s imagine that the 2 microservice API’s we’re dealing with are API1: books I’ve ordered API2: a list of all possible books, and one-to-many relationship to similar books that might be interesting to a customer.
From a transaction perspective, the choreography might be 1) I login 2) the client (or an intermediary) asks API1 to gather the contents of all of my previous orders 3) the client (or an intermediary) asks API2 for a list of recommended books based on all of the books previously ordered as captured by the call to API1 4) return a payload to the caller of an aggregation of API1 and API2.
So, let’s talk about building to fail. Let’s assume API1 (the orders I’ve made in the past) is a mission critical API, and there’s no failure tolerance. In that case, maybe that particular service is NOT a candidate to be a microservice (maybe). However, do we know exactly what it means to our service, application, consumers, the business, if API2 (the recommendations API) were down?
At the highest level, if API2 were down, it would mean the application couldn’t personalize a recommendation to my caller. From a user experience perspective, that personalization doesn’t necessarily create a dependency on the actual reason I’m using the platform (probably to buy something), does it? Although personalization would be nice, and it might create an opportunity for increased revenue to Amazon, I’m going to suggest that the personalization of the page does not create a high enough transaction value to fail the progression of other experiences (ordering a book) that may be in progress.
In traditional architecture design, if the recommendations API were down, the system is essentially down. Traditional design dictated that we made sure the recommendations API was up at all times. Microservices adopters look at it slightly differently, and ask how we can build the application so that, if non-critical services fail, how might we continue the actual important transaction flows.
In this case, we can apply an example of the circuit breaker pattern. The pattern talks about what to do in the event of failure conditions, and there are lots of ways to apply it. In this case, our application of it might be that 1) the circuit is shut down the path to the recommendations API until it’s in a healthy state 2) it serves up cache if available 3) even more interestingly, if it can’t serve up cache for whatever reason, when the circuit is tripped, we can apply patterns such as server side discovery and dynamic routing to have an alternate response in its back pocket (technically speaking), to say, obtain a list of the New York Times best sellers as a generic response.
Again, the point is, maybe the result isn’t personalized and it’s not ideal; but, the system didn’t fail. And, we’ve got all the benefits of an independently deployable microservice that I can iterate on and innovate for without impact to the whole. Without saying, guaranteeing that the circuits are being tripped appropriately as new services are introduced and deprecated should be done by automating destructive testing in production.
Microservices patterns minimize capacity waste or risk because they are, by the very nature of how they were designed (independently deployable, bounded contexts, etc etc etc), virtually infinitely scalable. Devops toolchains help a microservice developer guarantee that when capacity is needed, the supporting dependencies will be available for the service to be scaled.
Take, for example, the ephemeral nature of microservices themselves, scaling is dealt with by (and even introduction of a net new service at scale=1) patterns such as server side discovery and dynamic routing which are leveraged to offer the system the insights it needs to react to a change to state, health status, or service performance. Furthermore, as services scale horizontally, we risk service chattiness, but microservices patterns borrow from other technologies to reduce that impact by caching at all levels of the system (the protocol, the service, and at intermediaries such API gateways). Adding that caching layer all but guarantees that the distribution of services doesn’t materially affect the system as a whole, specifically by reducing any additional latency introduced by the distribution.
Finally, we can’t conclude our discussion on stability without addressing loss of data. Again, it’s the patterns that help minimize that risk. Microservices need to be designed in such a way where they are stateless, immutable and idempotent. What if we were able to retry requests if we were not worried about immediate consistency, for example, using event sourcing or CQRS? Or what if we can replay all transactions if we have a mechanism for structured logs such as with Kafka? The point is, microservices architectures may offer solutions to traditionally difficult problems to solve if the system designer begins to think about systems in a ‘microservices-ie’ way.
Efficiency is another one of those IT “truths” we’ve always believed, but needs to be rethought if we’re going to take on a microservice initiative. That’s not to say that we can’t (or shouldn’t) try to minimize the impact of waste, but, as already discussed, a microservices initiative isn’t going to gain you efficiencies in the near term, especially if the environment contains services that need to be deconstructed over time.
A few patterns to consider if efficiency is on your mind today. Backends for Frontends and api gateway patterns can help bridge modern and legacy ecosystems and help future proof your investments. Look for SDK’s to help minimize development overhead to take advantage of those patterns. Create a common microservice platform that will consist of shared libraries that offer accelerators to common development tasks (abstraction, integration, orchestration, and security access token pattern) to help minimize the costs and risks of duplicating code. Take advantage of the platform as well to provide the scale, security and efficiency that the organization requires but that the developer hates providing as non-functional requirements traditionally create a lot of friction.
Finally, celebrate the deprecation of services as much as the introduction of others because efficiencies are gained by taking things out that have outlived their useful lives and are no longer valuable or offer avenues to achieve your business goals
As apparent by now, my intention wasn’t to offer a prescriptive approach to microservices implementations, but instead to offer a perspective that may help shape someone’s approach to taking on the initiative at all. I’ve hashed out my own approach by working with my customers and friends, and I’ve created my own compass and a point on the horizon to follow based on these ideas. The principles of removing friction and build to fail guide me when I’m lost and confused and waist-deep in technology and process.
Are we ready to turn traditional IT paradigms on its side? Are we ready to fail? Are we ready to deconstruct the monolith? Are we ready to think about architecture differently? Are we ready to swallow the red pill?