On this page

Single Responsibility Principle (SRP)

The Single Responsibility Principle (SRP) is the first of the five SOLID principles. It sounds easy, but it is often misunderstood. This page explains what it really means, how to spot a violation, and how to fix one.

Definition

A class should have only one reason to change.

A "reason to change" means one source of change requests. In Robert C. Martin's words, a class should be responsible to one actor: one person or group whose needs can force the class to change.

SRP does not mean "a class should do only one thing". A class can have many methods. The question is: if the requirements change, how many different people would ask me to edit this class? If the answer is more than one, the class has too many responsibilities.

Analogy: think of a restaurant. The chef cooks, the waiter serves, and the cashier handles the bill. If one person did all three, then a new menu, a new table layout, or a new payment system would all land on the same person. When that person is sick, everything stops. Separate roles mean a change in one area does not disturb the others.

Why It Matters

When a class mixes responsibilities, you get these problems:

  • A change in one place breaks another. Fixing the report format accidentally breaks the salary calculation.
  • Hard to understand. The class is long, and you must read everything to change one part.
  • Hard to test. To test the pay calculation, you must also set up the database.
  • Merge conflicts. Different teams edit the same file for different reasons.
  • Hard to reuse. You want the pay logic in another app, but it is tied to the database code.

A Violation

Here is an Employee class that does three different jobs:

public class Employee {
private String name;
private double hourlyRate;
private int hoursWorked;
// Job 1: pay rules (the finance team asks for changes here)
public double calculatePay() {
double pay = hourlyRate * hoursWorked;
if (hoursWorked > 160) {
pay += (hoursWorked - 160) * hourlyRate * 0.5; // overtime bonus
}
return pay;
}
// Job 2: database access (the engineering team asks for changes here)
public void save() {
Connection connection = Database.getConnection();
// ...write SQL to store this employee...
}
// Job 3: report format (the HR team asks for changes here)
public String generateReport() {
return "Employee: " + name + ", Hours: " + hoursWorked + ", Pay: " + calculatePay();
}
}

Each method has a different actor:

MethodWho asks for changesExample change request
calculatePay()Finance"Change the overtime rule"
save()Engineering"Move from MySQL to PostgreSQL"
generateReport()HR"Add the department name to the report"

This class has three reasons to change.

The Real Danger: Shared Code

The worst case is when the responsibilities share code. Say both the pay calculation and the report use the same helper:

public class Employee {
// Used by BOTH calculatePay() and generateReport()
private int regularHours() {
return Math.min(hoursWorked, 160);
}
public double calculatePay() {
return regularHours() * hourlyRate;
}
public String generateReport() {
return "Regular hours: " + regularHours();
}
}

Now Finance asks to count a paid holiday as regular hours, so a developer edits regularHours(). The HR report now shows different numbers, and nobody told HR. This is exactly the kind of bug SRP prevents.

The Fix

Split the class so that each class has one reason to change.

// Only holds employee data
public class Employee {
private final String name;
private final double hourlyRate;
private final int hoursWorked;
public Employee(String name, double hourlyRate, int hoursWorked) {
this.name = name;
this.hourlyRate = hourlyRate;
this.hoursWorked = hoursWorked;
}
public String getName() { return name; }
public double getHourlyRate() { return hourlyRate; }
public int getHoursWorked() { return hoursWorked; }
}
// Job 1: pay rules. Changes only when Finance changes the rules.
public class PayCalculator {
public double calculatePay(Employee employee) {
double pay = employee.getHourlyRate() * employee.getHoursWorked();
if (employee.getHoursWorked() > 160) {
pay += (employee.getHoursWorked() - 160) * employee.getHourlyRate() * 0.5;
}
return pay;
}
}
// Job 2: storage. Changes only when the database changes.
public class EmployeeRepository {
public void save(Employee employee) {
Connection connection = Database.getConnection();
// ...write SQL to store the employee...
}
}
// Job 3: report format. Changes only when HR changes the report.
public class EmployeeReportGenerator {
private final PayCalculator payCalculator;
public EmployeeReportGenerator(PayCalculator payCalculator) {
this.payCalculator = payCalculator;
}
public String generate(Employee employee) {
return "Employee: " + employee.getName()
+ ", Hours: " + employee.getHoursWorked()
+ ", Pay: " + payCalculator.calculatePay(employee);
}
}

Now:

  • A new overtime rule only touches PayCalculator.
  • A database change only touches EmployeeRepository.
  • A new report layout only touches EmployeeReportGenerator.

Easier Testing

The pay rule can now be tested alone, with no database and no report:

@Test
void overtimeIsPaidAtOneAndAHalfRate() {
PayCalculator calculator = new PayCalculator();
Employee employee = new Employee("Asha", 100.0, 170);
double pay = calculator.calculatePay(employee);
assertEquals(17500.0, pay); // 170 * 100 + 10 * 100 * 0.5
}

How to Spot a Violation

Ask these questions about a class:

  • Who asks me to change this class? If the answer is more than one team or role, split it.
  • Can I describe the class without using "and" or "or"? "It calculates pay and saves to the database" is a warning sign.
  • Does the class import things from unrelated areas? For example, SQL classes, JSON libraries, and email libraries in one file.
  • Does the class have groups of methods that never use the same fields? The class is really two classes living together.
  • Is it hard to unit test without mocking many things?
  • Is it a very large class (a "god class") with a name like Manager, Helper, Utils, or Handler?
  • Do many developers edit this file for unrelated reasons? Look at the Git history for frequent conflicts.

SRP in Layered Applications

You already see SRP in the standard layers of a backend application. Each layer has one job:

LayerResponsibilityShould not do
ControllerHandle the HTTP request and responseBusiness rules, SQL
ServiceBusiness rules and workflowHTTP details, SQL
RepositoryRead and write dataBusiness rules
Validator / MapperCheck input, or convert between objectsSaving data
@RestController
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping("/orders")
public ResponseEntity<OrderResponse> placeOrder(@RequestBody OrderRequest request) {
Order order = orderService.placeOrder(request); // only translates HTTP to a service call
return ResponseEntity.ok(OrderResponse.from(order));
}
}
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final PaymentGateway paymentGateway;
private final NotificationSender notificationSender;
// Constructor omitted for brevity
public Order placeOrder(OrderRequest request) {
Order order = Order.from(request);
paymentGateway.charge(order); // delegates payment
orderRepository.save(order); // delegates storage
notificationSender.sendConfirmation(order); // delegates email
return order;
}
}

OrderService coordinates the steps, but each step is delegated to a class that owns it. Coordinating is the service's one job.

Summary

  • SRP: a class should have only one reason to change, meaning it serves one actor.
  • It is not "do only one thing". It is about who asks for changes.
  • Mixing responsibilities causes ripple bugs, hard testing, merge conflicts, and poor reuse.
  • The danger is highest when two responsibilities share code.
  • Fix it by splitting the class by reason to change, and let a coordinating class delegate to the others.
  • Spot violations with the "who asks for changes?" question, the "and" test, big god classes, and a long list of unrelated imports.
  • SRP applies to methods, classes, modules, and microservices.
  • Do not over-split. Keep things that change together in one place.

Interview Answer: What Is the Single Responsibility Principle?

The Single Responsibility Principle says that a class should have only one reason to change. More precisely, it should be responsible to one actor, meaning one group of people whose requirements can force the class to change. It does not mean the class has only one method.

For example, if an Employee class calculates pay, saves itself to the database, and generates reports, then Finance, Engineering, and HR can all force changes to it. A change for one team can break another, especially when they share helper code. I would split it into PayCalculator, EmployeeRepository, and EmployeeReportGenerator, so each has one reason to change.

The benefits are that changes are isolated, classes are easier to test without a database, there are fewer merge conflicts, and the code is easier to reuse.

I spot violations by asking who would ask me to change this class, and by checking whether I need the word "and" to describe it. I also avoid over-splitting: I split by reason to change, not by method count, so things that change together stay together.

Next TopicOpen/Closed Principle (OCP)