Single Responsibility Principle
A class should have one, and only one, reason to change.
Overview
The Single Responsibility Principle (SRP) states that every module, class, or function should have responsibility over a single part of the functionality. This isn’t about doing “one thing” in a literal sense—it’s about having one reason to change. A “reason to change” typically maps to a stakeholder or actor whose requirements could cause you to modify the code.
When a component has multiple responsibilities, changes to one responsibility risk breaking behavior related to the other. SRP minimizes this coupling by ensuring that each component changes only in response to one kind of requirement change.
Key Principles
Identify the Actors
Think about who might request changes to a piece of code. A User class that handles both authentication logic and report formatting has two actors: the security team (authentication rules) and the reporting team (format requirements). These should be separate components.
// Two reasons to change: authentication rules OR profile display rules
class User {
authenticate(password: string): boolean { ... }
formatProfileForDisplay(): string { ... }
}
// Separated by responsibility
class UserAuthenticator {
authenticate(user: User, password: string): boolean { ... }
}
class UserProfileFormatter {
format(user: User): string { ... }
}Cohesion as a Guide
Responsibilities are cohesive when they change together. If two pieces of functionality always change for the same reason, they likely belong together. If they change for different reasons, they should be separated.
Façade Pattern for Organization
When separating responsibilities creates many small classes, a façade can provide a simpler interface that delegates to specialized components internally.
// Façade providing unified access
class UserService {
private authenticator = new UserAuthenticator();
private formatter = new UserProfileFormatter();
private repository = new UserRepository();
authenticate(userId: string, password: string): boolean {
const user = this.repository.find(userId);
return this.authenticator.authenticate(user, password);
}
getProfileDisplay(userId: string): string {
const user = this.repository.find(userId);
return this.formatter.format(user);
}
}Example
Consider an Employee class that calculates pay, saves to database, and generates reports:
// Before: Three reasons to change
class Employee {
calculatePay(): number {
// Business rules from accounting
}
save(): void {
// Persistence mechanism from DBAs
}
generateReport(): string {
// Format requirements from reporting team
}
}The accounting department might change pay calculation rules. The DBA might change the database schema. The reporting team might change report formats. Each change risks affecting unrelated functionality.
// After: Each class has one reason to change
class Employee {
id: string;
name: string;
hoursWorked: number;
hourlyRate: number;
}
class PayCalculator {
calculate(employee: Employee): number {
return employee.hoursWorked * employee.hourlyRate;
}
}
class EmployeeRepository {
save(employee: Employee): void {
// Persistence logic
}
find(id: string): Employee {
// Retrieval logic
}
}
class EmployeeReportGenerator {
generate(employee: Employee): string {
// Reporting format
}
}Common Mistakes
Interpreting “one thing” too literally - SRP isn’t about functions that have one line of code. It’s about cohesive responsibilities that change together.
Creating too many tiny classes - Over-applying SRP can lead to a proliferation of classes that obscures the overall design. Use judgment.
Ignoring the “reason to change” test - Ask: who would request this change? If the answer is “two different teams,” consider separation.
Coupling through constructors - Even well-separated classes can become coupled if they all require each other in their constructors. Use dependency injection.
Related Concepts
- SOLID Principles - SRP is the “S” in SOLID
- Small Functions - SRP applied at function level
- Separation of Concerns - Broader principle behind SRP
- Coupling and Cohesion - SRP promotes high cohesion
References
- Martin, R. C. (2008). Clean Code: A Handbook of Agile Software Craftsmanship. Chapter 10.
- Martin, R. C. (2017). Clean Architecture: A Craftsman’s Guide to Software Structure and Design. Chapter 7.