Microservices promised independent teams shipping independently, yet a great many organisations end up with the opposite: a tangle of services so interdependent that no team can change anything without coordinating with several others. The cause is almost always the placement of the boundaries. Where you draw the lines between services determines how much coordination the system demands, and poor boundaries produce a distributed monolith that has all the cost of microservices and none of the autonomy. This piece sets out principles for drawing boundaries that genuinely reduce complexity.
Boundaries are the whole game
The number of services in a system matters far less than where the boundaries between them fall. Good boundaries align with how the business changes, so that a typical change touches one service and one team. Bad boundaries cut across the natural seams of the domain, so that a typical change ripples through several services and forces the owning teams into constant coordination. The same code, split differently, can be a pleasure or a nightmare to evolve.
This is why boundary design deserves disproportionate attention early. It is far cheaper to get the seams right at the outset than to re-cut them later, when each service has accreted data, integrations and assumptions. Leadership should treat boundary decisions as architecturally significant, worthy of real deliberation, rather than letting them emerge by accident from whoever happened to build each piece first.
Aligning services with business capabilities
The most reliable principle is to align service boundaries with business capabilities, the distinct things the organisation does, rather than with technical layers or database tables. A service that owns a complete business capability, such as pricing or order fulfilment, encapsulates the data and the logic for that capability and can evolve largely on its own. This is the heart of domain-driven thinking, and it is what makes a service genuinely independent rather than merely separately deployed.
Capabilities tend to be stable because they reflect what the business fundamentally does, whereas technical structures change with every framework fashion. Boundaries drawn along capability lines therefore age well. The practical test is whether a service has a coherent purpose that a business person would recognise and whether changes to that capability are mostly contained within the one service. If a single business change routinely spans several services, the boundaries are in the wrong place.
Coupling, cohesion and the cost of coordination
The two forces to balance are coupling between services and cohesion within them. High cohesion means the things inside a service belong together and change together. Low coupling means a service can change without forcing changes elsewhere. The distributed monolith is the failure of both: services that are tightly coupled to one another and weakly cohesive internally, so that every change is expensive to make and expensive to coordinate.
The most expensive coupling is shared data. When two services read and write the same database, they are not really independent, however cleanly their code is separated, because a schema change forces both to move together. Strong boundaries give each service ownership of its own data, accessed by others only through its interface. This single discipline, data ownership per service, does more to reduce coordination cost than almost any other boundary decision a team can make.
Designing interfaces and contracts
Where services do interact, the interface is the contract, and the quality of that contract determines how independently the two sides can evolve. A well-designed interface exposes a stable, business-meaningful capability and hides the internal model behind it, so that the provider can change its implementation freely as long as the contract holds. A leaky interface that exposes internal structure couples consumers to details they should never have known about.
Treat contracts as first-class, versioned artefacts. Consumers and providers should be able to evolve on different schedules, which means changes must be backward compatible by default and breaking changes must be versioned and migrated deliberately. Contract testing, where each side verifies it still meets the agreed interface, catches accidental breakage before it reaches production. Disciplined contracts are what let independent teams stay independent even as the system grows.
When not to split, and when to merge back
Not every responsibility deserves its own service. Splitting too finely creates a swarm of tiny services with more coordination overhead than the monolith they replaced, where a single feature requires changes across many of them. If two services always change together, deploy together and cannot function without each other, that is strong evidence they should be one service. The willingness to merge services back together is a sign of architectural maturity, not failure.
A pragmatic path for new systems is to start with a well-structured modular monolith, where boundaries exist as internal modules with clear interfaces, and extract services only where there is a concrete need such as independent scaling, independent deployment or a separate team. This lets the boundaries prove themselves cheaply before the cost of distribution is incurred, and it avoids committing to a service split that real usage later shows to be wrong.
What good looks like
In a well-bounded system, most changes touch one service and one team, each service owns its own data and exposes a stable business-meaningful interface, and the willingness to merge or re-cut boundaries keeps the design honest as understanding improves. Teams ship independently because the architecture supports it, not despite it, and the coordination overhead stays low even as the number of services grows.
- Draw boundaries along business capabilities, not technical layers or database tables.
- Give every service exclusive ownership of its data, with no shared database access.
- Test that a typical business change is contained within a single service and team.
- Design interfaces as stable, versioned contracts that hide internal models from consumers.
- Use contract testing so providers and consumers can evolve on independent schedules.
- Merge services that always change and deploy together, and split only where a concrete need exists.
Common pitfalls
The defining pitfall is the distributed monolith: services that are separately deployed but so coupled, usually through shared data, that they must change together. Splitting too finely is another, creating coordination overhead worse than the monolith it replaced. Boundaries drawn around technical layers rather than business capabilities are a third, because they guarantee that every business change cuts across multiple services and teams.
The organisations that get this right treat boundary design as the central architectural decision it is. They follow the business capabilities, enforce data ownership, invest in clean versioned contracts, and stay willing to re-cut the lines as they learn. Done this way, microservices deliver the team autonomy and independent delivery they promise, instead of the coordination tax that poorly bounded systems inflict.
Need support applying this approach? Email sales@halfteck.com.