Ok, so I have to first preface this whole blog post by a few things:
- I really struggle with the term microservices. I can’t put my finger on exactly why. Maybe because the term is hopelessly ill-defined, maybe because it’s gotten picked up by the hype train. Whatever. But I have to stick to some type of terminology so let’s just roll with it.
- This blog post might be mildly controversial, but I’m throwing it out there because I’ve had this itchy feeling for so long and I can’t get rid of it. I respect it if you want to disagree vehemently, and maybe there’s something both of us can learn.
- I have a weird story. My first “real” company, Spotify, used a service-oriented architecture from scratch. I also spent some time at Google which used a service-oriented architecture. So basically since 2006 I’ve been continuously working in what people now call a “microservice architecture”. It didn’t even occur to me that some people might want to build things as monoliths. So I guess I’m coming at it from a different direction than many other. Either way, there were particular non-standard reasons why Spotify and Google had to do this that I’ll get back to later.
Let’s start by talking about iteration speed!
What’s up with iteration speed!
I’m sort of obsessed about iteration speed. I’ve written about this in the past and it deserves more posts in the future, but the quick summary is that iteration speed is always going to be the strongest competitive advantage in this industry. You can’t really patent anything and proprietary technology is often much less valuable than companies would like to admit. So what do you do? You start shipping new feature quicker, you learn faster from users, and you run faster than your competitors (a.k.a. the “gingerbread man strategy”).
Let’s talk about testing and deploying
There’s of course many ways we can iterate faster, but for today let’s focus on two particular aspects of it: testing and deploying more often. I’m a big proponent of continuous deployment. I’m also a huge proponent of fast test suites. Why? You have been reading this far without any graphics so you deserve one. Tracing back all the dependencies and how it fits together, it looks like something like this in my head:
So it seems like we could improve a lot of things if we could test and deploy things faster! Of course, there’s a long series of steps to get there:
- Do you have fully automated tests? If not, write them, then come back.
- Are deploys automated? If not, do them, then come back.
- Are you deploying multiple times per day? If not, figure out how to get there, then come back.
Anyway, if only there was a “trick” to test and deploy things faster… maybe splitting things up into small independent units… if only there was a way ?
98% of microservice benefit is being able to test and deploy independently
By now it should be clear why splitting things up makes sense. But just as xy=0 when y=0 regardless of how large x is, don’t expect that you can just breaking your sweet old monolith up into two services and derive tremendous value from that. It’s rarely very valuable unless you can test and deploy those parts independently:
Here’s where I see so many blog posts where people are missing the point:
- If you need to deploy two services to production in tandem, you’re doing things wrong
- If you need to run two services together in order to run tests, you’re doing things wrong
- If you end up with a microservice that can’t be tested in isolation, you’re doing things wrong
- If you end up with a microservice that can’t be deployed in isolation, you’re doing things wrong
Why are you doing wrong things? Because you’re putting in tons of work separating out things into independent units, without reaping the benefits of fast testing and deploying cycles.
Of course, things get harder
I’m not going to dwell on this and there’s much that has been written, including Steve Yegge’s epic rant. Testing things in isolation means each part needs to make assumptions about how the other parts will behave and mock them out properly. Deploying a new version of an API call can be annoying and has to be done in multiple smaller steps. Tracing requests can be a massive pain. I could go on all day.
But as Americans are fond of saying, there are no free lunches.
Some questionable reasons to consider microservices
I mentioned 98% of the value is being able to test and deploy things independently? I think the other benefits are fairly marginal at best:
- Writing services in different languages. I think this argument is mostly invoked by some junior dev who wants to implement a new system in Clojure. Great news for the poor person waking up at 2am getting paged because the shopping cart service is down.
- Forcing applications into independent pieces so they don’t sprawl into cobwebs of interdependencies. I used to think this was a super strong argument! But clearly, some huge monolithic code bases are great. The Linux kernel shows that you can write highly modular code, all inside a single process (actually, the Torvalds-Tennenbaum flame war from 1992 is still highly relevant)
- Scaling two pieces of software independently. Not necessarily a strong reason, since you can also scale up a fat binary – look at Facebook.
- Breaking up software with different performance characteristics. This could occasionally be a valid argument, say if you have a Node-based webserver and you need to do something CPU heavy. But could in many cases be solved by something like background threads or “modes” – the same codebase is run both for worker processes and web server processes.
I’m obsessed with iteration speed and could write about 17 more blog posts about it. If there are any takeaways I want to leave you with, it’s these
- Automated testing is awesome.
- Continuous deployment is really sweet.
- Do those two things first.
- Once you see engineers starting to twiddle thumbs waiting for tests to run, you know the time is right to split things up.
- Microservices can be awesome for this.
- Keep splitting until you no longer see engineers twiddling thumbs and all tests blazingly fast.
- Lean back, relax, and watch your company out-iterate all your competitors through a superior development process.