How do cohesion and coupling act as opposing forces when you're deciding how large or small to make a microservice, and what heuristics help you find the right boundary?
answer
- high cohesion inside, low coupling across
- splitting always adds network coupling
- common-closure: changes together -> lives together
- transactional-boundary test
- bounded context / DDD vocabulary test
basics
~20 sCohesion means things that change together and belong together should live in the same service. Coupling means how tangled services are with each other. You want high cohesion inside a service and low coupling between services. Getting the size right means grouping things that truly belong together, and not more.
solid answer
~50 sCohesion is the degree to which the responsibilities inside a service belong together and change for the same reasons; coupling is the degree to which services depend on each other's internals, data, or release timing. These pull in opposite directions when you resize a service: splitting a service tends to increase coupling (more network calls, more shared-contract dependencies, more coordination) while it may or may not increase cohesion (only if the split separates genuinely unrelated concerns); merging services tends to reduce coupling between the merged pieces but risks lowering cohesion if you fold together things that don't actually change together. Practical heuristics: use the 'common closure' principle (things that change for the same reason belong together), draw boundaries around bounded contexts/business capabilities rather than technical layers or entities, check whether two pieces are called together in the same transaction or need strong consistency, and watch for a high ratio of cross-service calls or shared-database access relative to in-service logic as a signal the boundary is wrong.
go deeper
Should correctly define cohesion and coupling in plain terms and state the basic goal (high cohesion inside, low coupling across) without necessarily naming formal heuristics.
Should explain why splitting a service can increase coupling and name at least one concrete heuristic (e.g., things that change together should stay together).
Should apply multiple heuristics (common-closure, transactional boundary, bounded context) to a concrete scenario and diagnose which failure mode (low cohesion vs. high coupling) a given symptom points to.
Should discuss the trade-off at an organizational level (Conway's Law, team autonomy vs. duplication) and describe how to run boundary-setting as an ongoing, revisitable architectural decision rather than a one-time design step.
## The two forces, defined - **Cohesion** measures how strongly the responsibilities gathered inside one module — here, one microservice — belong together, typically judged by whether they change for the same reasons and are used together. - **Coupling** measures how much one module depends on another's internal details, and in a distributed system this includes not just code dependencies but shared data, shared release timing, and shared runtime availability. The classic software-design heuristic, going back to structured design and restated for OO/SOLID design, is: maximize cohesion within a module, minimize coupling between modules. Applied to service sizing, this means the 'right' service boundary is the one where everything inside genuinely belongs together (high cohesion) and where interactions with other services are as few, as coarse-grained, and as stable as possible (low coupling). ## Why every split adds coupling Splitting a service always adds some coupling, because whatever used to be an in-process function call between the split pieces now becomes a network call with a versioned contract, and the two new services must agree on data ownership, release compatibility, and failure handling. Whether the split is worth that coupling cost depends entirely on cohesion: - **If the two halves genuinely didn't belong together** — they changed for different reasons, were owned by different teams, needed to scale differently — then splitting removes an accidental coupling that existed only because they were bundled, and the net effect is positive. - **But if the two halves actually did belong together** (e.g., they're two steps of one workflow that almost always change in the same commit), splitting them doesn't remove real coupling, it just relocates it from 'compile-time, in-process' to 'runtime, over-the-network' — strictly worse, since network coupling is harder to change atomically and fails independently. This is the core intuition behind why 'smaller is not automatically better': every split is a bet that the cohesion gained outweighs the network coupling introduced. ## Concrete tests for the boundary Several concrete tests help locate the right boundary. 1. **The common-closure principle** asks: do these things tend to change in the same commit/PR? If yes, they're cohesive and belong together. 2. **The transactional-boundary test** asks: does this operation need strong, immediate consistency across these pieces of data (e.g., debiting one account and crediting another must both succeed or both fail)? If yes, keeping them in one service lets you use a local ACID transaction instead of building a saga; if the consistency requirement is genuinely eventual, splitting is safer. 3. **The bounded-context test**, from Domain-Driven Design, asks whether two areas use the same business vocabulary with the same meaning — if 'Customer' means something different to Billing than it does to Support, that's a signal they're separate bounded contexts even though they share a name. 4. **Team-ownership alignment** (deliberately shaping architecture to match desired team structure, sometimes called an 'inverse Conway maneuver') is also a heuristic: a boundary that lets one small team own a service end-to-end, without needing sign-off from another team to ship a change, is usually closer to correct than one that requires cross-team coordination for routine changes. ## Getting it wrong in either direction Getting this wrong in either direction has recognizable symptoms, and here is how each shows up. | Failure mode | Symptoms | |---|---| | Too little cohesion inside a service (grab-bag responsibilities bundled for convenience) | a service touched by many unrelated feature teams, frequent merge conflicts, and surprising blast radius because unrelated features share deploy risk | | Too much coupling between services | chatty-communication smells, cascading failures, and 'distributed monolith' symptoms — needing to deploy several services together in a specific order to ship one feature, which erases the independent-deployability benefit microservices are supposed to provide | Both failure modes are visible in code review and incident retros — watch for: - pull requests that routinely touch two or three services together (a coupling smell suggesting they should merge) - services whose changelog reads like an unrelated grab-bag of features (a cohesion smell suggesting they should split) ## The trade you make at each boundary A widely cited real scenario is Amazon's early-2000s 'two-pizza team' reorganization, credited by various retrospective practitioner accounts with pushing services toward boundaries owned end-to-end by small teams, explicitly trading some duplication of code and data across services for drastically reduced cross-team coordination — accepting slightly lower 'DRY-ness' (a coupling reduction) in exchange for team-level autonomy (a cohesion/ownership win). The lesson generalizes: cohesion and coupling aren't absolute values to be globally minimized/maximized independently, they're a trade you make deliberately at each boundary, and the right granularity is the one where you've consciously chosen which coupling you're willing to accept in exchange for which cohesion you're buying.
- If two services are always deployed together because of a strict dependency, is that a coupling problem or a granularity problem?It's usually a granularity problem wearing a coupling symptom - if two services must always ship together to be safe, they don't have independent release cadences in practice, so you're paying the network-coupling cost of separation without getting the deployability benefit. The fix is often to merge them, unless there's a strong reason like drastically different scaling needs to keep paying that cost.
- How do you tell whether low cohesion or high coupling is the bigger problem in a specific service graph?Look at where the pain actually shows up: frequent unrelated-feature collisions and merge conflicts within one service point to a cohesion problem, while frequent multi-service PRs, chatty runtime calls, and coordinated deploys point to a coupling problem. They can coexist in the same system in different places, so the diagnosis has to be per-boundary, not system-wide.
- Can you have high cohesion and high coupling in the same service boundary at once?Yes - a service can be internally very cohesive while still being tightly, even necessarily, coupled to another service, e.g., an Order service that must synchronously call a Payment service to authorize charges. The goal isn't zero coupling, it's minimizing unnecessary coupling while keeping the coupling that reflects a genuine, stable business dependency.
It's like organizing a toolbox: keep every tool you need for one job (say, plumbing) in one drawer (cohesion), but don't force unrelated jobs' tools into that same drawer just because they're both 'tools' (coupling), and don't split the plumbing drawer into five drawers by tool color, because now every plumbing job means opening five drawers instead of one.
saying these in an interview costs you the question
- Treats cohesion and coupling as the same concept or uses them interchangeably
- Claims splitting a service always reduces coupling
- Gives no concrete test (common-closure, transactional boundary, bounded context) for finding the boundary
- Ignores team-ownership/Conway's Law as a legitimate boundary-setting force
- Treats 'more services' or 'fewer services' as unconditionally better rather than a trade-off