Microservices Decomposition: When (and When Not) to Split a Monolith
The seam matters more than the split
Every monolith eventually gets a proposal to break it into services. The mistake teams make isn't deciding to split — it's splitting along org-chart lines or file-folder boundaries instead of along actual data-ownership boundaries. A "user service" and an "orders service" that both need to read and write the same user_preferences table haven't achieved separation; they've just added a network call to what used to be a function call, with all the same coupling and none of the performance.
The question that actually predicts success
Before extracting a service, I ask: does this piece of the system own data that nothing else needs to write to directly? Inventory management is a strong candidate — orders, marketplace connectors, and the admin dashboard all need to read stock levels, but only the inventory service should ever write them. That's a real boundary. A "notifications" service that just formats and sends messages triggered by everything else is a weaker candidate — it's more of a shared utility than an independent domain, and it's often better as a library or a single consumer of many event types rather than its own deployable with its own database.
What decomposition actually buys you
- Independent deploy cadence. The payments team can ship three times a day without coordinating with the catalog team.
- Fault isolation. A memory leak in the recommendation engine doesn't take down checkout.
- Scaling where the load actually is. Search traffic and checkout traffic have completely different scaling profiles — decoupling them means you're not over-provisioning one to serve the other.
What it costs, honestly
Distributed transactions become sagas. A stack trace becomes a trace across five log aggregators. "It works on my machine" becomes "it works in my docker-compose, which is missing four of the other twelve services." None of this is a reason to avoid microservices — it's a reason to only pay the cost where the boundary is real and the team is large enough that independent deployability is worth more than the operational overhead.
A rule of thumb
If a five-person team is maintaining fifteen services, the split happened along org-chart aspirations, not data boundaries. Start with a modular monolith — real internal module boundaries, one deployable — and only extract a service once you can point at the specific coupling problem the extraction solves. The best microservices migrations I've seen were reactive, not aspirational: "the catalog service needs to scale independently of checkout" is a reason. "Netflix does microservices" is not.