A Prelude to Successful Microservices

Crucial considerations before jumping into the architecture

As you prepare to venture into the world of microservices, explore a few key considerations that, if effectivly executed upon, will lead to the successful implementation of a microservices architecture in your organization.

So you’ve just boarded the return flight from DevBuzzwordCon(tm) or opened the latest email from "L33t Software Architecture Digest" and the promise of microservices is hard to shake. Seems like each and every tech darling of The Valley is touting their glitzy microservices architectures, fraught with product agility, team velocity, platform scalability—and depending on who you’re talking to, maybe all "the -ilities." Firing up your dark mode (from before dark mode was cool) terminal, you’re staring at the fragile codebase that serves as the sole execution vehicle for your application: the monolith.

It’s an unwieldy beast. Each deploy resembles Nik Wallenda’s latest stunt. The cyclomatic complexity has you at wit’s end. It is fragile and un-grokable, with each code change suited only for the bravest code spelunkers and OOP plate spinners. You reflect on nights gone by where any semblance of relaxation bestowed upon you by cracking a bottle of your favorite barrel-aged microbrew was completely wiped out by the pager alerts caused by the gremlins munching within (after midnight of course.) An access pattern gone awry locking up your favorite, albeit overworked, database table. Threads spun out of control, chewing up precious resources to handle a workload that has less to do with the front door of your application than freshness does with the the local strip mall’s Poke bowl special. And to boot, the teammates that you could get ahold of at this hour were unfamiliar with this bit of code and had no clue why it had run off the rails.

Finally, when the bug fix is in and it’s time to deploy that 2 A.M. remedy to the payment system, you hold your breath. Should you really have to deploy the inventory system with it? Talk about cognitive overhead and change volatility. It’s the proverbial ball of mud and mud wrestling went out of style when you graduated from college.

It is time. Time to throw the monolith’s laundry on the front lawn like that of an unfaithful partner. Time to become a card carrying member of the “I Slayed the Monolith” club. Time to determine if your engineering organization is ready to handle the shift, the evolution, the microservices.

Or is it?

I’m here to tell you: the juice can be worth the squeeze, the hype is not without warrant, and you can realize the aforementioned gains for your platform architecture and product development by embarking on this journey. But first, be warned. You are entering, not the Twilight Zone, but rather a dimension tangent to today's—one that will require your teams aligned, your domains defined, and your pipeline greased.

Effective Team Alignment and Collaboration

It’s simple right? Throw your high powering development team into a bingo ball tumbler and, as they leave that wire cage, focus their efforts on service du jour. Like we can trust gravity will pull objects to earth, we can trust that they’ll effectively intercommunicate approach, negotiate interfaces, claim ownership over the wrinkles. Or, is it more "Lord of the Flies: Microservices Edition”. Enter Conway’s Law.

Melvin Conway, two years before Woodstock, headed this one off with the infamous wisdom. Plainly stated—the communication within a software system reflects the communication of the teams that build it. The canonical example of this in action is that three teams tasked with building a compiler will build a three pass compiler. Simple enough, right?

So how does this apply to the famed silver bullet that is microservices architecture?

Think about it. You have a service, complete with its walled garden bounded context of the entities that it manages through its contractual interface. "All ye who shall access these entities must do so with the stated tome of access.” This is all fine and dandy if the data, and access patterns to find or modify said data, are there for the taking. But if not—careful, you could end up with development teams loitering in dark alleys behind the office negotiating data access deals because there’s no other way to get their fix.

Why does this happen?

Well all of our plates are full, our sprints jammed with aggressive goals. If I don’t understand why your initiative is important to the whole, then it’s simply a battle of your team’s priorities over mine. Without context, either my priority wins outright or negotiating that choice falls victim to the back alley deal—“you scratch my back and maybe I’ll scratch yours."

However, if the teams throughout an organization are aligned and driving toward the same goals, then proper communication can take over and the most important item, driven by the bigger picture, wins.

So now, getting that required REST endpoint written or pub/sub message plumbed so you can move that left-most sticky note to the right on your team’s ever autocratic Kanban board is no longer just a matter of your own team’s priorities, but also those of the business.

Rather than communicating non-collaboratively and siloing teams, we end up with the inverse.

Let’s feign an absence of that shared context. And even more severe, we’re brash and unwilling to submit to back alley deals. Consider a scenario where the data we need is actually available, but there’s other barriers to our team’s roadmap. The dreaded slow upstream service.

It is micro. Its contract is sound. But “Oh boy Oberto" is it sluggish. In a move that would make only Congress proud, you refuse to “reach across the aisle” and instead decide to craft your own solution to hide away the other application's performance. You build your own caching layer. This too is micro. So that’s ok, right? Unfortunately no. In haste, you have inadvertently added a layer of complexity to your system. It may present a solution to today’s problem, but has also mortgaged performance today for agility tomorrow. You have forgone communication, an opportunity to influence or, yes, pedal a backroom deal.

In this, you have paved over this other team's system. Now, your ability to leverage new capabilities of or upcoming enhancements to that very system upon which you are reliant is subject to this new extra leap (lovingly referred to as an abstraction.) With this, you, as a pave over artist, have increased cognitive overhead and reduced development agility. Not to mentioned assumed extra liability in the form of future tech debt. It’s Conway’s Law in action. It sucks.

Additionally, consider the opportunity cost of building that new abstraction. What else could your team have spent its time building instead? By simply communicating with those that maintain upstream system, how many immediate benefits or downstream byproducts could have been introduced to the overall system? After all, I’m willing to bet that your own application is not the only one that is dependent on this slug — in a microservices architecture, likely not. Be sure you’re taking full advantage of the knowledge, intelligence, and willingness to help across your team and across systems to serve the overall architecture. While the components are micro, they do not live in a vacuum and there’s some choreography that is required.

The point here, again, is effective collaboration through a shared context—the entire engineering organization building the same house and effectively communicating on how they’re doing so. Again, team structure and alignment to goals will absolutely influence the system design and its success.

If disparate teams of developers throughout the organization can’t agree on which considerations are most important—think characteristics such as data governance or performance—you’ll often find yourself in these entanglements. Without an understanding of the desired outcome for the systems to be built, you can’t possibly establish the correct corridors of collaboration and team organization for the success of that system's construction. Haphazardly throwing a list of todos to your standing team structure can, very quickly, serve as proof for the Gambler’s ruin.

One massive team will build one massive, tightly coupled system. That same team deconstructed too aggressively will result in some of the worst that distributed systems has to offer. These are the two undesirable ends of the spectrum and you want the success that lies somewhere in between.

So what comes first? Defining the borders in your architecture or that of your teams? I’m not here with an answer today. The density that lies within that question is greater than I wish to lift here. I will say the key lies in an understanding of your business as well as your product and its domains.

Understanding Domains

Initially, monoliths are easier to build. There’s less to consider, less scaffolding, coupling plays to your benefit as dependencies are more explicit. The network isn’t waiting to present itself as unreliable at every turn. Data and state are mostly consistent. You can move fast and couple together all of your favorite classes. You can conduct your application’s symphony via convenient method calls and integrate your data and state through a primary database. But most of all, what makes them easier is that you aren’t as beholden to business domains and designing your architecture around them.

In a microservices architecture, you are decomposing your logical application into many territories with boundaries. Each of these is often subject to articles of distribution such as applications across the network and teams across your office(s). Each service’s domain will take responsibility for one or many entities that, together, represent and govern a group of important business capabilities. The kicker here, though, is that it’s extremely difficult to get the boundaries and size of those domains just right.

I often liken this challenge to the entrance of imperial colonization in Africa in the early 1900s. It was at this point, European powers occupied and divided the continent in such a way that led to much of the conflict we continues to witness in the region today. Geopolitical commentary aside: in order to draw appropriate borders, a true understanding of the composition of a terrain and charactersitics of the people that inhabit it is paramount for the successful partitioning of a continent. How this relates to the introduction of microservices to your organization and architecture is obvious.

Without a solid understanding of your business and the application you wish to build, getting the domains correct will be quite tricky.

If you lack this knowledge or understanding, starting large can be a viable strategy. Much of the complexity of working in a microservices architecture comes from the aforementioned team structures and the fact that it’s a distributed system. By starting on this journey with either the monolith or building larger domains, you can approach your architecture with an eye toward stability and observance. With an emergent approach, identify the parts of the system that need to be deployed more frequently, perhaps require a different technology, or are ripe for a serious overhaul. Through this, these change-worthy entities that lie within your monolith can be rolled out and managed by new applications, allowing them to breath and evolve independently as well as stop being a monkey on the back of the rest of the existing applications.

Continuous Integration and Continuous Delivery

It is well known that in computer science you have three numbers— zero, one, many. If deploying one application has you feeling like Ed Harris’ character from Apollo 13 pacing the NASA control room, the leap to deploying many applications is going to be quite harrowing. As you adopt microservices architecture, embracing Continuous Integration and Continuous Delivery (CI/CD) can make your team and product development velocity hum. It’s not that CI/CD shouldn’t be employed and isn’t beneficial with a monolithic application, it’s that in microservices it is absolutely crucial to success.

Tying into the required organizational shift, each team should be responsible for the deployment of their services. Long gone are the days where you can be successful lobbing artifacts over the fence (e.g. emailing a binary) to an ops team. Teams should not only write the applications, but be continually submitting their changes to the code repository—always integrating—and pushing to production—always deploying. Just like the scope of the services are small, the scope of changes should too be small.

There are many ways to implement a CI/CD pipeline. What is key to its success is support for development teams to integrate not only build and deploy scripts, but also mechanisms that facilitate successful shipping. Triggering automated tests, offering teammates an opportunity for peer review, and orchestration of canary releases are all features of your pipeline that can be used to instill confidence and ensure stability for each new deploy.

On the confidence front, this is one of the benefits of deploying microservices via CI/CD that can’t be overstated. By increasing the frequency of deployments and reducing the scope of each deploy, volatility will decrease, hesitancy to deploy will lessen, and any fear of shipping will be overcome by a culture of always shipping.

When employing CI/CD, the focus should be on decreasing the cost of shipping (mentally and developer time spent) and turning it into a non-event. Make it so your team never has to “@channel we’re about to deploy” in Slack again. I always love to hear a development team boast about how many times they ship each day. It becomes a badge of, “Hey we’re doing this correctly.” And in the end, the more frequently you deploy, the more often you are converting code into business value.

In Conclusion

Adoption of a microservices architecture can be a very valuable shift for your organization. Success in this endeavor can lead to many gains—including team velocity and product agility. This move, however, should be accompanied by a thoughtful approach to organizational alignment, team collaboration, business understanding, and deployment processes. The themes explored here are just a handful of dimensions; but when embraced effectively, they can serve as the underpinnings for a successful journey.