Nobody argues with Single Responsibility in principle. Every developer I've worked with agrees a function should do one thing. The argument is always at the example, whether processOrder is one thing or six, and that is the part the rule itself doesn't settle.
The name test
If you need an "and" to describe what the function does, it's two jobs. That sounds glib, but it holds up in review better than most things I've tried, mostly because saying it out loud forces the description to be honest. validateAndSubmit is two jobs sharing a name. Once you have said "it validates the order and then submits it", the extraction is obvious and nobody has to argue about line counts.
The actor question
The sharper version comes from Robert C. Martin's Clean Architecture: a module should be responsible to one, and only one, actor. In review that becomes who pays when this changes. Two sets of people filing bugs against the same function is a boundary problem however short that function is.
David Parnas got to the same place in 1972 without using the word responsibility, by listing the design decisions likely to change and giving each one a module that hides it. Same cut, with different vocabulary.
Length limits are proxies
I run max-lines-per-function, complexity and max-params, and they catch tangles early. They do not define the rule. A long function with a single reason to change is fine, and a short one serving two teams isn't. The linter flags the smell, but someone still has to say what the job is.
Why this got sharper with agents
A developer reading a fuzzy boundary works the intent out from the surrounding code. An agent takes the boundary as given and builds on it, and by the time anyone notices there is code that grew up assuming the bad shape was a fact.
Full write-up, including the counter-argument and the enforcement config for TypeScript, JavaScript, Python, Java and PHP: https://prickles.org/tenet/single-responsibility-principle/F1
Top comments (0)