Service Decomposition: Drawing the Right Lines
Service decomposition is the architectural practice of splitting a monolithic application into distinct services that communicate over a network. The decomposition boundary defines what each service owns: its data, its logic, and its deployment lifecycle. Good decomposition produces services that can be deployed independently, scale independently, and are owned by teams independently. Poor decomposition produces distributed systems with tight coupling that is harder to operate than the original monolith.
Written by Yashveer Singh, founder of Yashveer Labs.
What you need to know
- Service boundaries should align with business capabilities and team ownership, not with database tables or technical layers.
- The strangler fig pattern (gradual extraction) is safer than big-bang rewrites. The system is always in a deployable state during incremental decomposition.
- Premature decomposition imposes distributed systems overhead without the organizational or scaling benefits that justify it. Most products should stay in a modular monolith longer than their instincts suggest.
- Shared databases undermine service independence even when deployment is separated. Each service should own its data and expose it through its API.
- Service decomposition is justified when scaling requirements, team size, or deployment independence requirements actually require it, not before.
The core argument
The microservices decision is made too early in most products I have seen. The motivation is usually one of two things: the engineers on the team have worked at a large company with microservices and default to what they know, or the technical lead read about Netflix's architecture and wants to build for scale before the product has users. In both cases, the result is a distributed system with all of the operational complexity and none of the organizational scale that justified that complexity at Netflix.
A well-structured monolith with clear module boundaries, a consistent data model, and a single deployment pipeline is easier to operate, faster to change, and more reliable than a premature microservices architecture. The monolith becomes a problem when team size creates deployment coordination bottlenecks, when specific capabilities have scaling requirements the rest of the application does not, or when organizational structure has evolved to the point where separate teams genuinely need independent deployment and ownership.
The right time to extract a service from a monolith is when the cost of the extraction is less than the cost of not extracting. For Velmora, the first service extraction was the PDF generation service: it consumed significant CPU on the web server, had no dependencies on the rest of the application state, and ran on its own deployment cadence. Extracting it produced a clear benefit (isolated CPU usage, independent scaling) with a clear interface (generate PDF from HTML, return a file URL). The extraction took two days and the result was obviously better. That is the pattern for justified decomposition.
Common mistakes
- Decomposing along technical layers instead of business capabilities. Extracting "the API layer" or "the database layer" as separate services creates tight coupling across service boundaries because every operation requires coordination between the extracted layers. Decompose by business capability (billing, notifications, user authentication) so that each service can perform complete operations without depending on other services for every request.
- Not establishing a data ownership principle before the first extraction. The most expensive mistake in service decomposition is retaining a shared database after extracting services. Shared database access creates invisible coupling that surfaces as schema migration coordination requirements, data consistency bugs, and tight operational dependency between "independent" services. Establish the data ownership principle before the first extraction: the new service gets its own database and the monolith reads from the new service's API.
- Under-investing in service communication patterns. Services that communicate synchronously (API calls where the caller waits for a response) create chains of availability dependencies: service A calls service B which calls service C; a failure in service C causes failures in service B which causes failures in service A. Asynchronous communication (event-driven patterns, message queues) breaks these chains. Design the communication pattern for each service interaction before the extraction, not after.
- Treating service decomposition as a one-time project rather than an ongoing evolutionary process. Service boundaries that are right today may not be right when the product has different scale, team structure, and domain understanding. Teams that treat decomposition as a project to be completed rather than an evolutionary practice end up with service boundaries that are frozen in the assumptions of the time they were drawn.
- Not monitoring distributed traces across service boundaries. When a request spans multiple services, latency and errors can originate at any point in the call chain. Without distributed tracing (Jaeger, Datadog APM, OpenTelemetry), diagnosing performance problems and errors in a multi-service architecture is significantly harder than in a monolith. Instrument distributed tracing before extracting the first service.
Where to start
- Identify the one capability in the current monolith that is a strong candidate for extraction. The criteria: a clear boundary (the capability does not share data models with other capabilities), an independent scaling requirement or different deployment cadence, and a well-understood interface (the rest of the application talks to it through a small, well-defined set of calls). If no capability clearly meets these criteria, the monolith is not yet ready for decomposition.
- Extract the candidate capability using the strangler fig pattern. Create the new service, implement the API, and route the relevant traffic to it while the monolith retains the existing code. Verify the new service is operating correctly. Gradually reduce and eventually remove the monolith's implementation of the same capability. This incremental approach produces a system that is always in a working state.
- Measure whether the extraction delivered the expected benefit. After the extraction, answer: is the capability now independently deployable? Does it scale independently? Did the team experience a deployment velocity improvement? If the extraction did not produce measurable benefit, the boundary may have been drawn incorrectly, or the product may not be at the scale where decomposition produces returns. Use the data to inform the decision about the next extraction.
Related reading
Frequently asked
About the author and why it matters
Yashveer Singh wrote this. I run Yashveer Labs out of New Delhi. The work I take on tends to come from founders who have been burned by an agency, a freelancer, or their own ambition. I do not promise miracles. I promise that the system will be online, the code will be readable, and the next engineer who touches it will not curse me. That is rarer than it should be.
Posts that line up with this one.
- SaaS Architecture and Scaling
Idempotency in API Design: Why It Matters More Than You Think
An idempotent API is one that handles repeated requests gracefully. Building it in from the start is far cheaper than retrofitting it after your first double-charge incident.
- SaaS Architecture and Scaling
Internal Admin Tools: Build vs Buy vs Retool
Every SaaS needs internal tools. The question is whether to build them, buy a platform like Retool, or use a lighter alternative. Here is the decision framework that saves engineering hours without creating tool debt.
- SaaS Architecture and Scaling
Job Failure Recovery: How Good SaaS Companies Sleep at Night
Every background job will fail eventually. The companies that sleep at night are the ones that built failure recovery into the system from day one, not as an afterthought when something broke in production.
- SaaS Architecture and Scaling
Monolith vs Microservices: Why Most Startups Get It Wrong
Microservices are the architecture that works at Netflix and fails at early-stage startups. Here is why the monolith is the right default, when microservices become rational, and how to make the transition without breaking everything.