Callum Evans

Of Microservices and Monoliths

"Microservices vs. Monoliths" has probably been the biggest source of controversy between myself and the substantially less attractive denizens of Manchester that I usually socialise with. Having regularly reflected on and revisited the topic for a few years now, I've found it harder and harder to make sense of the terminology.

We developers have a peculiar obsession with perfectionism: code has to be as SOLID as possible; whatever we write should be able to stand-up to business requirements 1/2/3/X years from now; you should be able to swap out your SQL Server database with MongoDB, just in case. This mentality can lead to some unfortunate software design decisions, where systems are built to stand up to imagined criteria that will never materialise. In many businesses, particularly startups, time is the most valuable resource, and the time it takes to deliver a piece of software could be the metric that deserves the most attention.

Given that line of thinking, there's an argument to be made that blasting out a single, slick and cohesive API and calling it a monolith is faster than dealing with the veritable hellscape of juggling dozens of services, deployments, nodes, clusters and, dare I say, pods. And whilst this can be true, it isn't always true.

I've worked at a couple of companies that have tried to transition from a monolithic architecture to microservices. One of them in particular was spurred on by stories from companies considered technical powerhouses, like Twilio and Uber. The excited, possibly naive, optimism we all regurgitated around the office and pub as we faced a potential microservice revolution might be familiar to some.

"Twilio have a bunch of microservices. They put them together like lego to create even more functionality, like building blocks. We should do that!"

"Uber has thousands of microservices. They're very successful. They're very technical. If microservices work for them they should work for us."

And, to be fair, they did work for us.

In this case, our older monolithic architecture was a typically unwieldy ASP Web Forms application that did everything. It worked (and, I hear, still does) very well. There were problems with its maintainability, however. And not necessarily due to bad code. I figure it was mostly considered 'bad' because it was legacy, which inevitably carries baggage; it was harder for the newer developers to understand, which churn exacerbated over time. There were also things the business wanted to do that made more sense as greenfield projects. Over a few months, a squad structure emerged that promoted self-ownership of most areas of the tech-stack, leaving developers a lot of autonomy with how solutions were implemented. Speed of delivery was the aim of the game, and what we called microservices provided a solution that fit in nicely with how we worked.

Our approach wasn't without its problems. We weaved a tangled web of APIs. Services didn't so much talk to each other as they did perform some x-rated parody of a Vulcan mind-meld. A single inbound HTTP request to a public-facing API could easily generate a further two or three internal HTTP requests, and then they risked snowballing into additional requests; any one of links in this chain of requests failing or not returning what it was expected to was a potential point of failure. Scalability was also made more difficult because of this. If one chatty service was given multiple instances, it would make little to no difference if the various services it talks to weren't also scaled with it.

This unenviable situation is sometimes described as a distributed monolith, wherein your architecture features few of the benefits associated with a microservices architecture but has all the problems. Everything's coupled together, and breaking, scaling or otherwise changing one service affected everything else.

There's a very interesting presentation by Jimmy Bogard that addresses some of these problems.

But, for us at least, it seemed to work. And for all our bad decisions and sloppily written services, we could develop new services and make ideas a reality quickly in an increasingly competitive marketplace. We were able to deliver some extremely cool and unique experiences for our users.

Though I don't believe this to be a triumph of a microservices architecture per se. Rather, it was a testament to the way we worked as a development team. The autonomy of each squad, each having their own aims and objectives that contributed to the business' goals, allowed us to work with the productivity of, arguably, multiple startups. It was only natural that each squad would build their own services, with their own processes and practices, in a way that worked for them.

We likely jumped on some of the definitions too hard. We were eager to "do microservices", and we probably weren't content with just having something that worked for us. We had conference calls with other companies. We went to some talks and read some blogs. "Microservices are agile!", "Microservices are scalable", "Microservices will make your dick bigger" and other such buzzword soups are still common.

Microservices were the in-thing and we definitely wanted to be a part of the hype. We were keen to emulate Uber and Twilio in particular, with their 2000+ microservices. Not realising that they're also global, building atop infrastructure they've spent years developing, and had literally 100 times the developers we had. Also, it was probably the case that many companies would present talks or publish blog posts detailing their experiences with microservices because that was a vogue topic and a shit-hot SEO keyword, which we, perhaps, didn't digest as critically as we should have done. In trying to imitate the success of others, we made our situation a lot worse as we missed the nuance and lost perspective in our own implementation.

The notion of 2000+ services almost seemed to become the goal in its own right. Our idea of success was measured, in part, by how many services we had.

The microservices bandwagon might not even be a wagon. There is an ongoing curiosity amongst myself and other software developers who are trying to get our misshapen heads around some of this fancy vernacular. What exactly is a monolith, and what exactly is a microservice? Well, dear reader, I'm sorry to say I can provide no clear answers. Much like every other topic in our esteemed industry, it very much depends on who you ask.

As far as I can piece together from what people say, what I've read and what I've seen, a monolith can be defined as any vaguely large, singular piece of infrastructure or code. A microservice is, equally vaguely, a smaller piece of infrastructure or code that might make up some greater-whole of the system, along with other similarly sized pieces of infrastructure.

And if you now feel compelled to close this site and never come back after I've provided such a characteristically unsatisfying climax, I would absolutely not hold it against you.

There are, of course, more detailed technical definitions. There are entire books and courses. Reams of documentation to trawl through. You can go to the ends of the earth and back on your journey to understand this new and exciting topic and make sense of the dilemma that is to monolith or to microservice? And maybe you should. You'll be able to digest and internalise other peoples' real-world experiences, problems and solutions and, if nothing else, come away with a broader idea of what's going on in our industry.

However, in reading the microservice holy scriptures of the possibly real, probably evil development deities, you will find some allusion to the idea that microservices are saviours. Successors to the archaic monoliths. That they solve the problems of the past and do away with dated practices. That not adopting microservices will let the competition get a leg-up on you and go to town. To my mind, there is some accidental truth to this, but not much. It is true that it has become easier to fall into a microservices-style approach as technologies such as SPAs, cloud infrastructure and continuous integration and deployment processes have come into their own over the years, though you need not build a microservice to benefit from these.

One occasional observation I've made is that the microservices vs. monolith zeitgeist is prone to going in circles. You start a new job, the legacy system is horrible, and whatever you do next you want to do the opposite. Whatever the infrastructural paradigm is for the legacy system, you probably want to write the next thing to not be it. Lo and behold, the monolith was the problem all along, and microservices were the solution and vice versa.

Another term one might have stumbled across is service oriented architecture. This is an old term, by software development standards, and there are a few ways of describing what it means. Google turns up some interesting results, but if you restrict the search history to the early 2000s, we see some familiar-sounding ideas come to light. One of the top results (from service-architecture.com), defines SOA thusly: "A service-oriented architecture is essentially a collection of services. These services communicate with each other. [...] A service is a function that is well-defined, self-contained, and does not depend on the context or state of other services." And that sure as shit sounds a lot like microservices as we know them today.

It's both expected and true to say that terminology evolves, and it's fair to say that a current interpretation of microservices places them as a subset of a service orientated architecture. However, the ideas aren't new. And you shouldn't feel compelled to jump into them because they're trendy, or because some article has scared you into using them lest your competitors outpace you. There's room in this world for both monoliths and microservices; big and small. They've both been around forever, and they can both work.

These days, it seems more sensible to me to not decide to specifically adopt a microservice or monolith approach, but instead to give your attention to how you want to work. Considerations such as how your teams are structured; which group of people will work on what and how often. What stuff is likely to change at the same time. You'll be thinking about what hosting infrastructure you want to leverage, or what provider to use, and what sort of DevOps culture you want in place to get software onto it. Do you have many products or one, and how tightly integrated are they? What sort of scalability do you need? These areas can be discussed without needing to mention the words microservice or monolith.

If you design your architecture as best you can to answer these types of questions and have the confidence to not awkwardly shoehorn yourself into a paradigm, you might be in with a better chance of avoiding the pitfalls that a preordained interpretation of microservices or monoliths comes loaded with. Don't feel compelled to squeeze your infrastructure into either definition. And when you relax any dogma, on one side of that argument or the other, you're more likely to end up with a balanced approach, tailored for your business requirements.

It goes without saying you'll probably run into problems, no matter what you do. And I guess that's just software, but at least build it your way and make them your problems in your software.