Simplicity: Monoliths and Microservices

John Wilkinson
5 min readApr 3, 2020

--

A bunch of monoliths

The idea of microservices took the software development world by storm about six years ago, in no small part to Netflix’s ability to brag really loudly.

This has made a lot of people very angry and been widely regarded as a bad move.

Personally, I’m not upset. The internet, in no small part, runs on microservices. By and large, I find the internet quite useful.

The whole argument is actually kind of strange. I’ve never met a carpenter that got angry at other carpenters for using a particular tool.

“You’re just cargo-culting that reciprocating saw, you know! I’ve used a handsaw for years, I’ve cut many things, with a handsaw you don’t need to manage batteries or power cords or replace blades!”

“Oh yeah? We’ll you’re stuck in the past! I sold all my handsaws and purchased reciprocating saws! Sure it was expensive but now I can use one in each hand and still have a spare! What do you mean impractical?”

I’ve never met a carpenter that didn’t own both a reciprocating saw AND a handsaw, often multiple of both. They solve different problems.

Monoliths and microservices solve different problems.

Ok, that’s the short of it, but I’ve been wondering why there is so much contention in the engineering space around these two different system designs. From what I can tell, advocates of each believe their approach is simpler, and therefore better. I think it boils down to confusion over simplicity.

What Is Simplicity?

According to one guy that quotes the dictionary a lot (Kevlin Henney), simplicity comes from simplex, which means a single fold or braid.

Interesting, to be sure, but perhaps not very helpful here.

Really, I’m more interested in Richard Gabriel’s famous The Rise of Worse is Better. In it, he argues that the most valuable aspect of software is simplicity, which leads to adoption and therefore longevity¹. By simplicity, Gabriel means specifically simplicity of implementation.

Simplicity-the design must be simple, both in implementation and interface. It is more important for the implementation to be simple than the interface. Simplicity is the most important consideration in a design.

Ok, this gives us our first type of simplicity: that of implementation. Simple software is software that is easy to implement.

Let’s look at the second type.

Why Go is Successful (aka Simplicity is Complicated) Slides

I’ve said Go is simple, but it’s not. It’s very complicated. I know, I worked on it. It’s one of the most complicated things I’ve ever worked on. And yet it feels simple, and I think that’s a really important idea. And it feels simple because pieces fit together orthogonally. And that requires a lot of design, a lot of thinking, a lot of refinement and implementation and re-implementation.
And to put it very bluntly, simplicity, at least in this context, is the art of hiding complexity.

This gives us our second definition: usability. Simple software is software that is easy to use.

These two definitions of simplicity have helped me understand monoliths and microservices.

Monoliths are, in part, about simplicity of implementation. They can be implemented easily, and if a new feature needs to be added the appropriate modules, classes, functions and variables are updated. Simple primitives. Simple implementation. When done correctly, development speed is very fast, and the only constraints are the amount of cores and memory you can size on an AWS instance.

Microservices are, in part, about simplicity of use. Their internals are very complex and require a lot of thought, and the ability to refine and re-implement them is part of their explicit value. However, when done correctly, the end result is pieces that fit together orthogonally, and can be used to construct large, complex, performant systems in manageable ways.

Implementation simplicity, over time, evolves into usability simplicity. This is a good thing, because it means the initial implementation had value.

Go could not exist without C. The understanding of what a simple programming language could look like had to be informed by the successes and failures of its predecessors.

Monoliths are a good way to test virality. They can be built and maintained without worrying about network partitions or latency, distributed logging or consensus, external API exposure, token management, or a slew of other concerns. And, if they provide so much value to so many people that greater scale is needed and greater complexity must be managed, pieces can be chipped off and transformed².

Microservices come with a lot of internal complexity. The very existence of service mesh is horrifying on some level, and only made worse by the fact that it’s necessary. There’s a big ( like, really big) cost for microservices.
But, if you need to scale, and you want to achieve external simplicity, that is: the user of the system has the complexity hidden from them⁴, they just see the building blocks, microservices are required.

Tools in a toolbox.

[1]: I am aware that his line of reasoning involved much more than the reductionist viewpoint that simplicity is the most valuable attribute of software, and than Mr. Gabriel does not necessarily agree with the view he posited in the article. Don’t @ me bro.

[2]: This process is significantly easier if the monolith was built with modularity, decoupling, and flexibility in mind. If you build spaghetti code monoliths, microservices probably³ won’t save you.

[3]: Ok, they might save you iff the reason it’s spaghetti code is because there’s too many teams touching the same codebase, too much to manage and track, and really you just need to isolate that one team over there so their damage is mitigated. I get it, it happens. Microservices can help delineate ownership, which occasionally fixes organization problems.

[4]: Look, I know my mapping of types of simplicity to monoliths and microservices is imperfect and inexact. I’m aware. You can stop with your angry comments. Microservices are about external simplicity in that the internals may be highly complex but the external representation should be a set of orthogonal APIs. These APIs can be combined in useful ways to easily create new behavior. In a microservices system, there is a high cost and a lot of refinement to get to this point, but once there it is pretty straight-foward to modify behavior or add new behavior without impacting any other part of the system.

[5]: I don’t know why you’re reading this footnote, there was no footnote⁵ up above. I’ll leave you with:

The MIT guy then muttered that sometimes it takes a tough man to make a tender chicken, but the New Jersey guy didn’t understand (I’m not sure I do either).

--

--