skip to content

How does the Single Responsibility Principle relate to the older ideas of cohesion and coupling, and why is "maximize cohesion, minimize coupling" not the same instruction as "one class, one method"?

level: seniorimportance: should knowfreq 54%

answer

  1. SRP = decidable criterion for "cohesive"
  2. cohesion ladder: coincidental → functional
  3. one-method classes: more boundaries, more coupling
  4. Common Closure Principle = SRP at package level
  5. connascence: strong inside, weak across boundaries

basics

~20 s

Cohesion means the parts of a module belong together; coupling means how much modules depend on each other. SRP is a rule for picking boundaries that keeps cohesion high. Splitting into one-method classes raises coupling without adding cohesion.

solid answer

~50 s

SRP is essentially a restatement of structured design's cohesion criterion with the selection rule made explicit. Cohesion asks "do these elements belong together?"; SRP answers "yes if they change for the same reason / same actor." That is functional cohesion — the strongest kind — as opposed to weak forms like temporal or coincidental cohesion ("these run at startup", "these were left over"). Coupling is the dual: elements that change together but live apart create cross-module dependencies and ripple edits. The two must be optimized jointly, which is why one-method classes fail — they maximize the *number* of boundaries, and each boundary is a dependency plus a wiring cost, so total coupling rises while cohesion per unit does not improve. The real objective is that a single change request lands inside one boundary. Connascence and the Common Closure Principle formalize the same trade-off.

go deeper

for a junior

Define cohesion and coupling plainly and say SRP is about grouping things that belong together so changes stay local.

for a middle

Explain that SRP defines "belong together" as "change for the same reason", and show why one-method classes increase coupling without buying change isolation.

for a senior

Name the cohesion ladder and functional cohesion, connect to the Common Closure Principle and connascence, and describe measuring change coupling from history to validate boundaries.

for a principal

Argue boundaries as an investment priced by expected change and by team topology; discuss when to deliberately keep coarse boundaries, and how cross-cutting concerns are factored out orthogonally rather than as more responsibilities.

## The three ideas, defined - **Cohesion** — how strongly the elements *inside* one module belong together. High cohesion = they exist for the same purpose. - **Coupling** — how strongly modules depend on *each other*. High coupling = a change in one forces changes in others. - **SRP** — a *selection rule* for where to draw boundaries so that cohesion is high and coupling low: put together what changes for the same reason (same actor); separate what changes for different reasons. Cohesion and coupling come from **structured design** (Larry Constantine, Glenford Myers, Ed Yourdon, 1970s), long before SOLID. SRP's contribution is not a new property; it is a crisp, decidable criterion for what "belong together" means. ## The classic cohesion ladder (worst → best) 1. **Coincidental** — grouped for no reason (`Utils`). 2. **Logical** — same broad category, selected by a flag (`handleAllInputEvents(kind)`). 3. **Temporal** — happen at the same time (`initialize()` doing ten unrelated things). 4. **Procedural** — executed in a sequence. 5. **Communicational** — operate on the same data. 6. **Sequential** — output of one is input of the next. 7. **Functional** — all contribute to exactly one well-defined task. ← what SRP targets. SRP effectively says: aim for functional cohesion, where "one task" is judged by **who asks for changes to it**. ## Why the naive reading fails Read as "do one thing", SRP degenerates into one-method classes. Consider a payment flow decomposed into `AmountValidator`, `CurrencyChecker`, `FeeAdder`, `RoundingApplier`, `LedgerWriter`, each with one method and each injected into the next. - **Cohesion did not improve where it matters.** Each class is trivially cohesive, but the *behavior* — "charge a customer correctly" — is now spread over five files. Nobody can read the rule in one place. - **Coupling rose.** Five types, five interfaces, five registrations, an orchestrator that knows all of them. Every added boundary is a contract to maintain. - **Change did not isolate.** A single business request ("fees are now computed after rounding") edits several files plus the orchestrator — the exact opposite of SRP's goal. This is why the objective is stated as a *joint* optimum: **a typical change request should land inside one boundary**. Boundaries have cost; you buy them with expected change. ## Formalizations worth naming in an interview - **Common Closure Principle (CCP)** — "classes that change together belong in the same package." This is SRP lifted to package granularity, and it states the goal directly in change terms. - **Connascence** (Meilir Page-Jones) — a taxonomy of *how* two elements must change together: connascence of name, type, meaning, position, algorithm, timing, value, identity. Design guidance: keep strong connascence *local* (inside one module) and only weak connascence *across* boundaries. SRP is the special case "do not spread strong connascence across a boundary that separates actors." - **Change coupling / logical coupling** — the empirical measurement: files that historically appear in the same commits. Strong change coupling across a boundary means the boundary is in the wrong place. ## Practical procedure 1. List recent change requests (last 3–6 months) and mark, for each, the files edited. 2. Requests that consistently edit *one* file cluster → good boundary; leave it. 3. Requests that consistently edit files across boundaries → boundary is wrong; merge or redraw along the change axis. 4. One file consistently edited by unrelated requests → god class; split along requester. This converts a subjective aesthetic argument into an evidence-based one. ## Edge cases - **Stable, rarely changed code** barely benefits from perfect boundaries; the analysis is about change, so no change means little value. - **Performance-critical inner loops** may deliberately fuse concerns to avoid indirection; document the deviation. - **Cross-cutting concerns** (logging, transactions, authorization, metrics) intersect every responsibility. They are handled not by more classes but by orthogonal mechanisms — decorators, middleware/pipelines, aspects — so the core module keeps a single reason to change.

  • How do cross-cutting concerns like logging or authorization fit into SRP?
    They intersect every responsibility, so they are pulled out orthogonally rather than as more sibling classes — decorators/wrappers, middleware pipelines, or aspects. The core module then keeps a single business reason to change while the cross-cutting policy changes in one shared place.
  • What evidence would convince you a boundary is drawn in the wrong place?
    Change coupling from version history: if files on opposite sides of the boundary keep appearing in the same commits, the boundary cuts through a single reason to change and should be redrawn or removed.

Rooms in a house. Cohesion is putting all cooking things in the kitchen; coupling is how often you must walk between rooms. Giving every appliance its own room is maximal "separation" and a terrible house — you'd spend the whole meal walking.

context