Coupling in Practice
The kinds of coupling that actually hurt, ranked by how much they constrain independent change.
The practical test for coupling is not aesthetic: if I change this component, how many others must be deployed at the same time? That number is the coupling that costs money.
The ladder, worst to best
- Shared mutable database. Two services writing the same tables are one system with two deployment pipelines and no owner. Schema changes require coordination across teams and nothing can be refactored.
- Shared internal library with business logic. Every consumer must upgrade in lockstep to get a fix.
- Synchronous call with no fallback. Structural coupling plus temporal coupling: the callee must be up for the caller to work at all.
- Synchronous call with a defined fallback. Still coupled, but the failure is bounded.
- Asynchronous event on a published schema. The producer does not know who consumes it.
- Shared nothing but a stable contract.
Temporal coupling deserves separate attention because it is invisible on a component diagram. Two services can be beautifully decoupled structurally and still form a single availability domain, which is what turns one dependency's slowdown into everyone's outage.
Industry example
LinkedIn's move toward Kafka as a central nervous system is best read as an attack on point-to-point coupling rather than as a throughput story. The problem was combinatorial: every new system that needed profile or activity data added another bespoke integration to every source, and the number of integrations grew far faster than the number of systems.
Publishing to a durable log inverts the dependency. Producers write once; consumers subscribe without the producer knowing they exist. What you buy is the ability to add a consumer without touching any producer. What you pay is eventual consistency everywhere downstream, schema governance as a standing obligation, and a new failure mode — consumer lag — that has to be monitored as carefully as any service's error rate.
Failure scenario
The "distributed monolith": services split cleanly on paper, but every user action fans out through six synchronous hops and any release requires a coordinated deployment plan. All of the operational cost of microservices, none of the independence.
Trade-off
Decoupling is not free and not always right. Each layer of indirection buys independent change and costs latency, debuggability and cognitive load. Two components that always change together should probably not be separated at all.
Interview question
"Two teams share a database table. Describe three ways to break that coupling and what each one costs."