I’ve sat in more than one architecture discussion in which someone has suggested using microservices even before we’ve really established what problem we’re actually trying to solve. It usually goes something like this:
“We’ll probably want microservices for scalability.”
Maybe. But probably not for the reason you think.
Microservices can be a very good architectural choice (If you need a summary of microservices, you might be in the wrong place – but you can read up on them here). They can also be an excellent way of taking one reasonably straightforward application and turning it into twelve smaller applications that are way more complicated and harder to debug. The question I tend to ask is simple:
What problem are the microservices actually solving?
If the answer essentially boils down to “because that’s how modern systems are built”, it might be time to start getting nervous.
Microservices aren’t free
On an architecture diagram, splitting a system into services looks nice and tidy:
Customer Service / Order Service / Billing Service / Notification Service.
Everything’s neatly separated into clean and tidy boxes with pretty arrows between them. All seems right with the world. And then you actually build it. You know that function call that used to happen inside the application? Yeah, that’s now a network request. Which means…
It might time out, or could even just fail halfway through. It may need authentication, and it probably needs retry logic. You’ll then need to decide what happens if the retry runs the same operation twice. You’ll want to make sure you have tracing, so you can figure out why a request disappeared somewhere between four services at 2 am. And somebody will eventually ask why changing one field in the Customer Service has caused three other problems.
None of this means microservices are bad. It just means you’ve built yourself a distributed system. And distributed systems come with a fairly substantial instruction manual.
Sometimes the organisation needs microservices more than the application does
One of the strongest reasons for using microservices is organisational.
You may have eight engineering teams working on different areas of a large platform. These teams will need to release independently. They also have separate responsibilities. Different parts of the system will inevitably scale differently. And one team shouldn’t need to wait for another team’s deployment window.
Under these circumstances, microservices start to make a lot of sense.
Now imagine you’ve got three developers.
Those same ten services suddenly start to look a little different. Instead of three developers working on one application, you’ve got three developers maintaining ten deployments, ten CI/CD pipelines, ten sets of dependencies and quite possibly several different databases.
You haven’t necessarily increased the capability of the team. You’ve just increased the number of things they have to look after and the number of potential failure points they need to keep an eye on.
Don’t underestimate the data problem
The application code often gets most of the attention when people talk about moving to microservices. However, it’s the data that’s usually where things get interesting.
Ideally, each service owns its own data. That’s a good thing because it keeps the services independent.
Unfortunately, your business process probably doesn’t care how elegant your service boundaries are. An order might involve customer data, stock availability, payment processing and fulfilment.
In a monolith, that might once have been one database transaction. Now it’s potentially a conversation between several systems. So what happens if payment succeeds but fulfilment fails? What happens if one service hasn’t received the latest customer update yet? Do we need eventual consistency? A message queue? An event bus? Compensating transactions?
You can solve all of this. I’ve worked with systems that solve it very well. But we’ve travelled quite a long way from “let’s split the application into a few services”.
The boring option is often surprisingly good
I’m a big fan of the modular monolith as a starting point. It doesn’t sound particularly exciting, which is probably one reason it doesn’t get talked about as much. But…
You can still have well-defined boundaries: Billing can be separate from authentication, while reporting can be separate from customer management. You can enforce boundaries in the codebase without immediately putting a network between everything. And if one part genuinely needs to become a separate service later, you can extract it.
That tends to work better than starting with twenty services just in case you need them in future. You probably won’t. And if you do, the requirements will almost certainly have changed by then anyway.
So when would I use microservices?
There are loads of good reasons:
- Maybe different parts of the platform need completely different scaling characteristics.
- Maybe several teams genuinely need independent deployment cycles.
- Maybe you need strong isolation around a particular workload.
- Maybe there are clear business domains that deserve independent ownership.
Those are real architectural requirements. “Netflix uses microservices” isn’t. Netflix also has requirements that your business almost certainly doesn’t have.
Complexity has to earn its place
This is the bit I think sometimes gets lost. Architecture is about controlling complexity, not using as many different new technologies as possible. Yes, new technology and systems are exciting, but every new component should earn its place on its own merit, not just because it’s the new shiny tool. Every service you use should be introduced because it solves a problem that matters enough to justify the operational cost it introduces.
Sometimes the right answer is microservices. Sometimes it’s a monolith. But if you build a well-structured application, it’s also easy enough to split it up later, if there’s ever a genuine reason to do it. Nobody gets bonus points for having the most boxes on the architecture diagram.
