Microservices promise independent teams, independent deploys, and systems that scale with the business. Many teams adopt them and end up with the opposite: one tightly coupled system that now also has network latency.
This is the distributed monolith. It keeps every cost of a monolith and adds every cost of a distributed system.
It rarely comes from one big mistake. It builds up from small decisions that each looked reasonable at the time. Here are the five I see most often.
1. Slicing by technical layer
Symptom: The "services" are a Controller Service, a Business Logic Service, and a Database Service. Every feature touches all three.
That isn't decomposition. It's a layered monolith with HTTP between the layers. A change to "how refunds work" still means changing and deploying every layer in step.
Fix: Slice by business capability: Orders, Riders, Payments, Wallet. Each service should map to something the business would recognise and own its full vertical slice, from API to storage.
A good test: can you explain what the service does without using the words "layer", "handler" or "repository"?
2. One database to rule them all
Symptom: Four services read and write the same schema. Nobody is sure who is allowed to change the orders table.
A shared database is the most effective way to couple services while looking decoupled on the architecture diagram. Any schema change becomes a cross-team negotiation, and any service can quietly break any other.
Fix: Each service owns its data. Other services get it through an API or by subscribing to events. Sharing infrastructure is fine (one database cluster, separate schemas). Sharing ownership is not.
3. Synchronous call chains
Symptom: Order → Rider → Wallet → Payment → Notification, all over blocking HTTP.
Every synchronous hop adds latency and is another place the request can fail. Availability multiplies: five services at 99.9% each give you roughly 99.5% end to end. The slowest service sets the response time for everyone.
Fix: Use synchronous calls when you need an answer right now, such as "is this payment authorised?". Use events for work that can be decoupled, such as "order placed, notify the customer, update analytics, credit loyalty points". Asynchronous communication is not about speed. It keeps one service's bad day from becoming everyone's.
4. Many services, zero independence
Symptom: You have 30 services, and shipping a feature still needs five teams, a release calendar, and a coordinated deploy.
The number of services was never the goal. If services can't change on their own, you've added operational overhead and kept all the coupling.
Fix: Optimise for independent change, not service count. Watch the signals: how often does a change need more than one service deployed together? How many teams need to sign off on a release? Those numbers matter more than how many boxes are on the diagram.
5. Assuming the network is reliable
Symptom: Everything works in staging. In production you get timeouts, retry storms, duplicate charges, and events that arrive out of order.
In a distributed system these aren't edge cases. They are normal behaviour. Brokers redeliver messages, and networks partition. A retry without idempotency can turn one payment into two.
Fix: Design for failure from day one:
- Timeouts on every outbound call
- Retries with backoff and jitter, not tight loops
- Idempotent consumers with idempotency keys on anything that moves money or state
- Dead-letter queues for messages that can't be processed
- Circuit breakers so one failing dependency doesn't take others down with it
- Observability: distributed tracing, correlation IDs, and alerts on queue depth
Before you add service #N+1
Ask five questions:
- ✅ Does it have a clear business boundary?
- ✅ Does it own its data?
- ✅ Can it ship on its own?
- ✅ Is its runtime coupling minimal?
- ✅ Are its failure modes handled?
If the answer to any of these is "no", a well-structured module inside an existing service is often the better choice. A modular monolith with clean boundaries is better than a distributed monolith with messy ones.



