Most developers have heard of the Single Responsibility Principle. Few actually understand it. The common interpretation—that a class should do one thing—is so vague as to be nearly useless. Does a UserService do one thing? What about a PaymentProcessor?

Robert Martin, who coined the principle, has spent years clarifying what he actually meant. The definition isn't about counting methods or behaviors. It's about identifying who requests changes and why. A class should have only one reason to change, and that reason should trace back to a single actor or stakeholder group.

This reframing transforms SRP from philosophical hand-wraving into a practical design tool. When you understand it correctly, you stop debating whether a class does too much and start asking a more useful question: if this code changes, who asked for it?

Actor-Based Thinking

The original formulation of SRP centers on actors—the people or groups who might request changes to your software. An actor might be the accounting department, the security team, or the user experience designers. Each represents a different reason your code might need to evolve.

Consider an Employee class that calculates pay, generates reports, and saves to a database. Three different actors care about these behaviors. The CFO cares about pay calculations. The COO needs accurate reports. The CTO owns the persistence layer. When you bundle these responsibilities together, changes for one actor risk breaking functionality that another actor depends on.

This is the real danger SRP protects against. It's not about code being too long or doing too much in some abstract sense. It's about change coupling. When the accounting team needs overtime calculations updated, you shouldn't risk breaking the reporting format that operations relies on.

Identifying actors requires stepping back from the code and asking: who in the organization would file a bug report or feature request that touches this class? If you can name multiple distinct groups, you've found a violation. The class has multiple reasons to change, and those reasons originate from people with different concerns and priorities.

Takeaway

A class has one responsibility when changes to it can be traced back to a single actor or stakeholder group. If different people could request different changes, split the class.

Cohesion Signals

Recognizing SRP violations in existing code requires developing an eye for certain patterns. The most reliable signal is divergent change—when you find yourself modifying a class for unrelated reasons over time. Your git history tells the story. If commits touch the same file for different features requested by different teams, you have a cohesion problem.

Another indicator is the presence of methods that use completely different subsets of the class's data. If calculatePayroll uses salary and hoursWorked while generateOrgChart uses managerId and reportingChain, these methods have no business living together. They're strangers sharing an apartment.

Watch for conditionals that route behavior based on type or role. A method that says if userType equals admin do this, else do that often signals multiple actors being served by one class. The admin functionality and standard user functionality probably change for different reasons.

Private methods offer clues too. When you see clusters of private helpers that only serve one public method each, you're looking at responsibilities that could be extracted. The class has become a holding company for loosely related operations rather than a cohesive unit.

Takeaway

Divergent change patterns, method clusters using different data subsets, and type-based conditionals all signal that multiple actors' concerns have been mixed into a single class.

Extraction Techniques

Once you've identified an SRP violation, splitting the class requires care. The goal is separation without disruption. Start by creating new classes for each distinct responsibility, but keep the original class as a facade. It delegates to the extracted classes while maintaining the existing interface.

This approach preserves backwards compatibility. Existing clients continue calling the original class. Behind the scenes, the work flows to properly focused components. You can migrate callers to the new classes gradually, or leave the facade in place permanently if the simplicity serves your codebase.

Managing the new dependency relationships matters as much as the extraction itself. The extracted classes should depend on abstractions rather than each other. If EmployeePayroll needs employee data, pass it in rather than having it reach back to an Employee class. This prevents recreating the coupling you just escaped.

Consider whether the extracted responsibilities belong at the same architectural layer. Sometimes SRP violations reveal deeper structural issues. A class handling both business logic and persistence isn't just doing too much—it's mixing layers. Extraction should respect your system's boundaries, not just split code arbitrarily.

Takeaway

Extract responsibilities into new classes while preserving the original as a delegating facade. This enables gradual migration and maintains backwards compatibility throughout the refactoring.

The Single Responsibility Principle becomes practical when you stop asking what a class does and start asking who would ask it to change. Actors drive change. When multiple actors' concerns live in one class, their changes collide.

This perspective shifts SRP from aesthetic preference to risk management. You're not splitting classes because small is beautiful. You're splitting them because independent changes should be independent in code.

The next time you're unsure whether a class has too many responsibilities, name the actors. If you can identify multiple stakeholders who might request changes to the same file for different reasons, you have your answer—and your refactoring target.