Software Architecture & Design Principles

Coupling & Cohesion

Cohesion measures how tightly a module's own responsibilities belong together; coupling measures how tightly it is bound to other modules. Every durable design heuristic — SRP, layering, component boundaries — is really an argument for raising one and lowering the other.
  • High cohesion: everything inside a module serves one purpose and changes together
  • Low coupling: modules interact through narrow, stable interfaces and know little about each other's internals
  • Coupling spectrum (tightest → loosest): content, common, control, stamp, data
  • Cohesion spectrum (weakest → strongest): coincidental, logical, temporal, procedural, communicational, functional
  • Afferent (Ca) / efferent (Ce) coupling counts and instability I = Ce / (Ca + Ce) make coupling measurable, not just a feeling
Coupling, tightest to loosest
KindWhat's sharedWhy it's costly
ContentOne module reaches into another's internals directlyAny internal change breaks the caller — no encapsulation at all
CommonModules share global/mutable stateHidden channel of influence; order-of-execution bugs
ControlOne module passes a flag telling another how to behaveCaller must know callee's internal logic to pick the flag
StampA whole object passed when only a few fields are usedCaller depends on fields it doesn't need — any of them changing risks breakage
DataOnly primitive/simple data passed, exactly what's neededThe loosest practical form — the goal to aim for

Cohesion is the mirror image: a class with functional cohesion does one well-defined thing end to end (a TaxCalculator); one with coincidental cohesion bundles unrelated methods that happen to live in the same file (a Utils class with string helpers, date helpers, and network helpers). Low cohesion doesn't crash anything — it just means the class has no reason to exist as a unit, and every change to it risks disturbing unrelated callers.

Stamp coupling → data coupling
// Stamp coupling: whole Customer passed, only the email is used —
// this method now depends on every field Customer has.
void sendReceipt(Customer customer, Order order) {
    mailer.send(customer.getEmail(), order.toReceiptText());
}

// Data coupling: depends only on the data it actually needs.
void sendReceipt(String recipientEmail, String receiptText) {
    mailer.send(recipientEmail, receiptText);
}
Sources
  • Clean ArchitectureCh. 12–14 — Component Coupling
  • Refactoring: Improving the Design of Existing Code (2nd ed.)Ch. 3 — Bad Smells in Code