Skip to content
Richard Cooper
Go back

The distributed systems tax

There’s a reflex in our industry that says the grown-up version of any system is a pile of microservices. Draw the boxes, put a queue between them, and you’ve arrived. But every time you take something that used to be a function call and turn it into a network call, you start paying a tax — and like most taxes, it’s easy to sign up for and hard to stop paying. Whether it’s worth it is, as ever, it depends. What I want to do here is itemise the bill, because too many teams sign up for it without reading it.

What you’re actually paying

The moment a call leaves the process, you inherit a list of problems you simply didn’t have before:

None of these are reasons never to distribute. They’re the price. The mistake isn’t paying it — it’s paying it without noticing, and getting nothing back.

The worst deal: paying the tax, buying nothing

The failure mode I’ve watched most often isn’t a monolith that should have been split. It’s a system that was split for the aesthetics of microservices and paid the entire tax for none of the benefit. A dozen services that all share one database. A “feature” that can’t ship without coordinating a release across three of them. A request that can’t complete unless four services are all up at once. That’s a distributed monolith: you’ve taken on every cost of distribution and kept all the coupling of a monolith. The worst of both columns.

It almost always traces back to splitting on the wrong lines — carving by technical layer, or splitting before the boundaries were even clean — rather than along the seams the business actually has. (Which is the whole case for finding your bounded contexts first.)

When the tax is worth paying

You pay the tax when you’re buying something with it that you genuinely need and can’t get any other way — the same short list of real forces every time:

If a split buys you one of those, the tax is a fair price. If it buys you “it feels more modern,” you’ve just volunteered for a standing bill.

The default that dodges the tax

This is why, for the bulk of a system, the better trade is to not distribute — to keep things in one process with strong internal boundaries, and reach for the network only where a real force pays the tax back. A well-built monolith isn’t the embarrassing option; it’s the one that skips the whole list above for the 90% of your system that gets nothing from being distributed.

It also keeps your options open. Splitting a system is close to a one-way door — re-merging services is a miserable job nobody budgets for — whereas not splitting stays cheap to change your mind about, especially if the internal boundaries are clean enough that you could extract a piece later if a force ever shows up. Keep the boundary crisp and deletable and the topology becomes the reversible decision it should have been all along.

So before you draw the boxes and reach for the queue, do the boring thing: add up the tax, and name what you’re buying with it. If the answer is “independent scaling for this one hot path,” pay it gladly. If the answer is a shrug, keep your money — and your function calls.


Share this post:

Previous Post
Bounded contexts and aggregates
Next Post
Modular microliths