Beware the Single-Purpose People
So pure it hurts
The Single Responsibility Principle is one of those ideas that sounds so sensible it can sneak past your judgment.
Do one thing. Do it well. Keep modules focused. Give code a reason to change. Good advice.
Then someone turns the advice into a measuring tape and starts declaring that any function over five lines is a code smell.
The problem is not SRP. The problem is treating “small” as a substitute for “cohesive.”
At that point you have met the Single-Purpose People: developers who are not wrong about modularity, exactly, but have confused useful boundaries with maximum fragmentation.

I. The Useful Idea Underneath
Adding a single checkbox to a form should ideally only affect one file. Not 8 files across 5 directories… I’m looking at you, React/Redux.
When SRP is applied with judgment, it helps. Code units focused on a single conceptual task are easier to understand. Tests can target behavior at a sensible boundary. Clear modules make it easier to change one part of the system without dragging the rest of the application into the room.
Even the classic Unix examples are more pragmatic than the slogan suggests. ls lists files, yes, but it also coordinates calls like opendir, readdir, closedir, and stat. The useful unit is not the smallest possible operation. The useful unit is the smallest coherent thing that solves the task.
The original Unix philosophy was about composition and simplicity, not about reducing everything to a single function or file.
That distinction matters. “One responsibility” is not the same as “one line of behavior.”
II. Over-Abstraction: When Simplicity Turns to Chaos
Our architect insists every function longer than 5 lines is a ‘code smell’. Our codebase now smells faintly of clueless desperation.
The failure mode is easy to spot after it has already made your week worse.
The codebase has more files, but less shape. Every helper has a helper. Every concept has been split across folders named after technical roles instead of product meaning. Adding a checkbox requires touching a component, a hook, a selector, an action, a reducer, a constant, a test fixture, and a barrel export that exists mostly to keep the import paths from looking guilty.

What did all that purity buy?
- File System Shrapnel: Source directories blooming into nightmarish landscapes of countless tiny files, often containing a single, tragically lonely function. Navigation becomes an exercise in spelunking.
- Dependency Tangles: A web of imports and exports so dense that tracing execution requires a large whiteboard and more patience than the feature deserves. Files imported exactly once sit there pretending to be reusable.
- Testing Treachery: Tests become brittle, hyper-specific sentinels guarding minuscule implementation details. Change a function signature? Watch dozens of tests crumble like ancient pottery. The test suite transforms from a safety net into a minefield.
- Velocity Vanished: Simple changes metastasize into multi-file modification sagas. Onboarding new developers involves weeks of handing them maps and compasses just to find where the
UserProfilecomponent actually lives this week. Forward progress slows to a geological crawl under the sheer weight of this “organization.”
I’ve stared into the abyss of codebases where a straightforward 100-line feature was vivisected across 15+ files, each a “pure” little angel containing maybe one or two functions. The cognitive blast radius of trying to hold that mess in your head utterly negated any theoretical win from the separation. It wasn’t simpler; it was just scattered.
III. The Toll of Perfection: Impact on Developers
We spend more time debating file structure and naming conventions than actually shipping features. Is this Agile?

This pathological fragmentation is not just an aesthetic problem. It changes how developers spend their attention:
The Productivity Drain: Forget technical debt; this is organizational debt accrued through obsessive-compulsive directory nesting. Every minor tweak becomes an archaeological dig through layers of abstraction. Time vanishes into the black hole of cd .. and grep.
The Testing Tax: Instead of providing confidence, the test suite becomes a source of friction. Hours melt away fixing tests broken by trivial refactors, tests that were too tightly coupled to the microscopic details they were supposed to be verifying.
The Cognitive Load: There is a hard limit on how many disconnected pieces of information a human brain can juggle. Forcing developers to piece together program flow from a dozen scattered files actively hinders understanding and makes confident changes harder.
IV. Embracing Pragmatism: A Practical Alternative
I suggested we put two related functions in the same file. The room reacted like I had proposed deleting staging. — A recovering purist reader
The escape hatch is not abandoning SRP. The answer is applying it at the right level of meaning.
Here’s what that looks like in practice:
- Focus on Cohesion, Not Atoms: Group things that change together and belong together conceptually. A module might handle several related aspects of user authentication. That’s fine. It’s probably better than six separate files each holding one function related to login state.
- Keep Kin Together: Don’t split related code unless there’s a screamingly obvious, tangible benefit – like genuine reusability in practice, not in some hypothetical future that never arrives. Proximity matters for comprehension.
- Let Reality Drive: Organize based on the actual features and workflows of your application, not some abstract ideal of functional purity³. Does this structure make it easier or harder for someone to understand and modify
Feature X? - Mind the Meatware: Remember the poor developer. What organization minimizes the mental juggling act required to work on the code? Optimize for human understanding.
- Test What Matters: Write tests that verify behavior at a sensible boundary, not tests that are intimately soldered to the internal wiring of every tiny function. Aim for confidence, not just coverage percentage theatre.
The objective isn’t theoretical perfection worthy of a PhD thesis; it’s creating code that your colleagues (and future you) can navigate, understand, and modify without wanting to set the building on fire.
Sometimes this means a file is 200 lines long instead of 50. Sometimes a function handles fetching data and transforming it slightly. Sometimes a class has two responsibilities that are so tightly coupled they should live together. If it makes the system easier to work with overall, it is probably the right call.
Stay relentlessly focused on the practical questions:
- Can someone new find their way around?
- Can we change
Xwithout breaking unrelatedY? - Does this test actually tell me if the feature works?
- Are we shipping value, or just rearranging folders?
V. Conclusion: Fostering Cohesive and Maintainable Code
The Single Responsibility Principle is a useful tool. It is not a mandate to pulverize your codebase into atomic dust. Like any tool, its value depends on the judgment of the person using it.
So when you encounter the Single-Purpose People, ready to wage war on any function daring to exceed three lines, take a breath. Remember the 12-file checkbox.
Our job is not to construct theoretically immaculate snowflake functions. Our job is to build software that works, solves problems, and does not punish the next person who has to touch it.
Stay pragmatic. Focus on outcomes. Do not let the pursuit of perfect purity become the enemy of maintainable code. Your sanity, and your team’s velocity, depend on it.
¹ The irony being that achieving actual single purpose at the lowest levels requires immense complexity hidden just beneath the surface.
² We’re talking about conceptual purity here: the idea that a function should do only “one thing” logically. Do not confuse this with functional programming’s concept of a “pure function” with no side effects, which is a different, though sometimes related, idea.