The Computing Series

The Concept

Coupling measures how much one component depends on another. Two components are tightly coupled when a change in one requires a change in the other. They are loosely coupled when each can change independently. Low coupling is the goal at component boundaries.

Cohesion measures how well the elements within a component belong together. A component with high cohesion performs one clearly-defined function. Its elements are related by purpose. Low cohesion — a class that manages users, sends emails, and writes to the audit log — signals that the component covers too many responsibilities and will be modified for too many different reasons.

The target state is always: high cohesion within components, low coupling between components. These goals are complementary but not free. Achieving low coupling requires deciding what belongs together (cohesion) and enforcing boundaries around those clusters.

Coupling types, ordered from weakest to strongest:

Data coupling — components communicate by passing only the data they need. A function receives amount: Money and returns Receipt. The caller and callee are minimally entangled.

Stamp coupling — components pass whole objects when they only need a field. processPayment(order: Order) when the function only uses order.amount. The callee now depends on the entire Order type.

Control coupling — one component tells another what to do via a flag. process(data, mode="strict"). The caller has knowledge of the callee’s internal branching logic.

Common coupling — multiple components share global state. A global configuration dictionary, a module-level registry, a class variable. All components reading that state are coupled to each other through it.

Content coupling — one component directly accesses the internals of another. Service B reads Service A’s database table. One class modifies another’s private fields. This is the strongest and most damaging form.

The fintech example in the introduction is content coupling: Service B accessed Service A’s database directly, giving Service B knowledge of Service A’s internal storage format.

Two code smells signal coupling problems in practice. Feature envy appears when a method in class A spends most of its logic reading and transforming fields from class B. The method belongs in B; it was placed in A by accident or convenience. Shotgun surgery appears when a single change — a new field on Order, a new payment status — requires modifications in ten different places. The change propagates because the concept is spread across many components instead of being owned by one.

Read in the book →