DanLevy.net

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.

Violence in Software Architecture
Components, components everywhere

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.

No escape for this infinite work pattern
Components, components everywhere

What did all that purity buy?

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?

So messy it borders on art
So messy it borders on art

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:

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:

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.