The Single Responsibility Principle: One Reason to Change

2026-04-29

The Single Responsibility Principle (SRP) is the most misunderstood of the SOLID principles. People reduce it to "do one thing," which leads to an explosion of tiny classes that accomplish nothing individually. Robert Martin's actual definition is more precise: a module should have one, and only one, reason to change. A "reason to change" maps to a stakeholder or business concern — not a line count.

Consider a UserService class that handles user registration, sends welcome emails, generates analytics events, and formats user data for the API response. That's four different stakeholders who might request changes: the product team (registration rules), the marketing team (email content), the data team (analytics schema), and the frontend team (API shape). When marketing wants to A/B test the welcome email, you're editing the same class that handles registration — and risking a regression in signup flow.

The fix isn't creating UserFirstNameValidator and UserLastNameValidator as separate classes. That's over-decomposition driven by "do one thing" thinking. The fix is separating along change boundaries:

Each of these changes for one reason, driven by one stakeholder. Registration can now evolve independently of email templates.

The practical test: describe what your class does. If you use the word "and," that's a smell. "This class registers users and sends emails and tracks analytics" — three "ands," three responsibilities. But "this class manages the lifecycle of a database connection" is fine even though it handles opening, pooling, health checks, and closing. Those all change together for the same reason.

Rule of thumb: if a class has more than 3 dependencies injected into it, it's likely violating SRP. Each dependency typically represents a separate concern. A class with 6 injected services is almost certainly coordinating responsibilities that should be split apart.

SRP also applies at the function and module level. A function that parses input, applies business logic, and writes to the database is doing three things for three reasons. A module (package/directory) that mixes HTTP handlers with database queries and email templates will force unrelated teams into the same code reviews.

The payoff is testability. A WelcomeEmailSender can be unit tested with a stub user object. A monolithic UserService requires mocking a database, an email provider, and an analytics client just to test one registration rule.

See it in action: Check out Single Responsibility (SOLID Design Principles) by Zak H. to see this theory applied.
Key Takeaway: SRP means each module has one stakeholder-driven reason to change — split along business boundaries, not arbitrary "do one thing" lines.

All newsletters