Foundations · F1

Single Responsibility Principle

Every thing should do one thing and one thing only.

Summary

Aim for one reason to change per unit. More than that and every edit is a guess about what else moves. Other vocabularies cover the same idea: stakeholder, axis of change, volatile design decision. David L. Parnas argued in 1972 that each module should hide one decision likely to change; Robert C. Martin's actor framing in Clean Architecture (2017)1Robert C. MartinClean Architecture (2017): SRP as one actor / one reason to change — the formulation that replaces vague “do one thing.” makes the question explicit: who pays when this changes? Mixed actors mean mixed ownership, sprawling tests, and names that need “and” to stay honest. Length and complexity rules flag the smell; the actor test names the rule.

Copy a note and link

Grab this short comment and drop it into a PR comment or an LLM chat to prompt the right change.

Every method or function should do one thing only. Separate concerns out to make everything else easier.

/tenet/single-responsibility-principle/F1
Copy for paste

Opinion

If you take one thing from this entire site, take SRP. It is the keystone tenet, the one every other Foundation hangs off, and the one I've never had to argue. Every developer I've worked with agrees in principle: a function should do one thing. The argument is always at the example: whether processOrder is one thing or six, whether validateAndSubmit is two jobs sharing a name. The seam, not the rule. That is what makes SRP load-bearing for the rest of the canon. Once a function does one thing, it can be named for what it does (F2 Intention-Revealing Names); the comment stops earning its keep (F5 Self-Documenting Code); duplication becomes visible and F3 DRY turns it into a refactor; complexity stays inside the budget F4 Simplicity draws; the test surface narrows, which is what P1 Test First rewards; TS6 Behaviour Testing needs only one assertion to cover one behaviour. The mixed-concern function pays a tax at every call site: someone re-derives intent from the body because the name no longer covers the work.

Read what the code is doing. If the description needs and then, the function is the wrong size. Extract the second job, name the extraction, and follow A2 Three-Tier Hoisting into the right module. The same logic scales: A4 Common Closure Principle is SRP at the package, and S2 Cyclomatic Caps is the linter that forces the conversation when judgement falters. Cut the debt as it appears, not when it accretes.

AI eyes only

Rule: one function, one verb. One reason to change per unit.

Reject: function or method names containing “and”, “or”, or “then”. Reject: a handler that parses, validates, persists, and formats a response in one body.

Generate: separate functions for input validation, business transformation, persistence, and response shaping. Each takes typed input and returns typed output. Each is independently testable.

Diagnostic: if the function needs “and then” to describe, extract until each step has a name. Length is not the constraint; count of reasons-to-change is.

Why?

  • Easier to name. A function that does one thing can be named for that thing. A function that bundles several jobs gets named for one of them; the rest are invisible from the signature. See F2 Intention-Revealing Names.
  • Easier to test, maintain and modify. A single-purpose function has a small surface, so test setup stays small, edits stay contained, and there are fewer ways to break what was not changed.
  • PR reviews are faster because the code is easier to read. One thread of logic per function scans quicker than mixed concerns tangled in one body.
  • The right home becomes visible. Once a unit is abstracted to a single responsibility, it is obvious whether it belongs in the local module, the product folder, or the generic folder. Buried inside a multi-purpose function, the same logic stays where it does not belong. See A2 Three-Tier Hoisting.
  • Existing code surfaces. When a function is lifted to where it belongs, an equivalent function may already exist there — the existing version is reused instead of duplicated. See F3 Don't Repeat Yourself.
  • Smaller diffs. A change to a single-purpose function affects only that function. A change to a function with mixed concerns ripples across the file.
  • Easier onboarding. A single-purpose function explains itself from its name and signature. A multi-purpose function needs documentation that often does not exist.

Related