
Microservices aren’t a flex — they’re a tax. Modular monoliths aren’t “temporary” — they’re often the best architecture. Here’s the decision framework, the failure modes, and the migration path that doesn’t create a distributed mess.
Axel Domingues
Microservices became popular for a good reason:
They solve real problems.
They also create real problems.
And a lot of teams adopt them for the wrong reason:
“We want to be modern.”
This month is about replacing ideology with engineering.
Not “microservices bad” or “monolith bad”.
Just the adult question:
When should you split a system — and how do you do it without building a distributed monolith?
The real goal
Ship changes safely, at the speed your business needs.
The real constraint
Every architectural choice creates a tax (complexity, ops, coordination).
The usual failure
Teams pay the microservices tax before they need the microservices benefits.
The “grown-up” path
Start with boundaries. Split only when boundaries + teams demand it.
If you only remember one line, remember this:
Microservices are a scaling strategy for teams, not a scaling strategy for CPUs.
Yes, services can scale independently. But independent scaling is rarely the first problem you have.
The first problem is usually one of these:
Those are organizational + architectural problems — and they can be solved (for a long time) inside a modular monolith.
A modular monolith is not “one big ball of mud”.
It’s a monolith that behaves like a set of internal services:

Microservices are not “many small apps”.
They’re a decision to accept:
…and to build the discipline to survive that world.

Let’s make the decision concrete.
Modular monolith fits
Early product phase, domain discovery, fewer teams, strong consistency needs.
Microservices fit
Multiple teams with stable boundaries + real need for independent deploys.
Most “microservices migrations” are actually this:
We have a bad monolith, so we’ll fix it by adding distributed complexity.
That almost always makes it worse.
A bad monolith is fixed by:
Those are the same prerequisites for microservices — but cheaper to learn in one deployable.
The actual disease is a lack of boundaries and engineering discipline.
You have “services”, but:
Diagnosis: the boundaries are wrong or the domain is still changing too fast.
Teams extract microservices, but keep everything “consistent” via shared libraries:
Now every change becomes a multi-repo release train.
Diagnosis: coupling moved from codebase coupling to dependency coupling.
There’s a sane, repeatable path that works for most systems:
Here’s how.
Pick a candidate that:
Common first candidates:
If extraction didn’t reduce coordination or improve resilience, don’t keep splitting blindly. Fix the boundary first.
It’s “extract the parts where independent change is worth the cost”.
A modular monolith rots when boundaries become social agreements instead of enforced rules.
Use these guardrails early:
Own your module
Named team ownership. Clear entry points. “Who maintains this?” must have an answer.
Enforce dependencies
Tooling + tests that fail builds when dependency rules are violated.
Stop leaking tables
Access data via module interfaces, not cross-module queries.
Ship with discipline
CI gates, fast tests, rollback strategy. Operational discipline is architecture.
The “microservices tax” is paid in operations and coordination.
To avoid death-by-a-thousand-services:
If a service can’t be owned end-to-end by a team, it’s probably too small.
Synchronous is fine within a boundary. Across boundaries, prefer events and workflows to avoid call chains.
Teams shouldn’t reinvent:
That’s expensive — and it burns teams out.“Everyone maintains their own mini-production.”
If you’re undecided, use this:
Or even more blunt:
If you can’t clearly name the bounded contexts, you’re not ready for microservices.
Team Topologies
A clear model for team boundaries and why architecture is often a reflection of communication paths.
Domain-Driven Design
Bounded contexts are the missing vocabulary in most “microservices vs monolith” debates.
Not necessarily.
Many high-scale companies run modular monoliths successfully for years — sometimes permanently — because the economics are great:
If your team topology and domain boundaries don’t demand microservices, a modular monolith can be the best long-term architecture.
You can, but you’ll reinvent the pain.
DDD isn’t an ideology — it’s vocabulary:
Without that, teams tend to split by technical layers, and coupling returns immediately via data and workflows.
Distributed data.
The moment a business flow spans multiple services, you need:
If you ignore that cost, the system still pays it — just as outages.
If microservices vs modular monolith is the “shape” question…
…the next question is:
How do you ship any shape reliably?
In January 2022, we go operational:
“Containers, Docker, and the Discipline of Reproducibility”
Because once you have multiple deployables (or even one), deployment discipline becomes architecture.
Containers, Docker, and the Discipline of Reproducibility
Containers aren’t “how you deploy apps” — they’re how you make environments stop being a variable. This is the operational discipline: immutable artifacts, repeatable builds, and runtime contracts you can actually rely on.
Queues, Retries, and Idempotency: Engineering Reality in Async Systems
Async work is where production gets honest. This month is a practical playbook for queues, retries, idempotency keys, and the patterns that keep “background jobs” from duplicating money or burning trust.