Single Responsibility Principle
A class should have one reason to change — split code by the people and pressures that change it.
Big idea
The Single Responsibility Principle (the S in SOLID) says a class should have one reason to change. "Reason" means an actor or pressure — the finance team, the UI designer, the database admin. When two of them can force edits to the same class, their changes collide; give each its own class.
The problem it solves
An Employee class has calculate_pay() (finance owns the rules), report_hours() (operations owns the format), and save() (the DBA owns the schema). Both pay and hours reports share a private _regular_hours() helper. Finance asks to change how overtime is counted; a developer edits the helper; the operations report is now quietly wrong too. Nobody on the operations side asked for anything.
One class, three masters, one shared helper: that's the collision SRP prevents.
Think of it like
A restaurant where the chef also takes bookings and does the accounts. A busy Saturday means the books are wrong; a tax change means dinner is late. Split the jobs — chef, host, bookkeeper — and each can change how they work without the other two noticing.
How to apply it
- List who would ask for changes to this class. Each distinct asker is a responsibility.
- Move each responsibility into its own class (or module) with a name that says what it does.
- Keep a thin coordinator — or a Facade — if callers want one entry point.
"One reason to change" is not "one method". A Money class with twenty methods all about arithmetic on money has one responsibility.
Watch it
Hands on
The program below mixes pay rules and report formatting in one class. Finance changed overtime to start after 40 hours — but the shared helper means the hours report now shows overtime it shouldn't (operations wants to show anything over 8 hours a day as "long days", their own rule). Split the class so each rule lives with its owner and the output matches.
class Employee:
def __init__(self, name, daily_hours, rate):
self.name, self.daily_hours, self.rate = name, daily_hours, rate
def _extra_hours(self):
# finance changed this to "over 40 per week"; operations also uses it
return max(0, sum(self.daily_hours) - 40)
def pay(self):
base = min(40, sum(self.daily_hours)) * self.rate
return base + self._extra_hours() * self.rate * 1.5
def hours_report(self):
# operations wants hours beyond 8 on each day, summed
return f"{self.name}: {sum(self.daily_hours)}h, long-day hours {self._extra_hours()}"
e = Employee("Rina", [10, 10, 8, 6, 6], 20)
print(f"pay {e.pay():.2f}")
print(e.hours_report())output
pay 800.00
Rina: 40h, long-day hours 4Run and edit it in the app.
The bug wasn't a typo; it was two owners sharing one helper. Once each rule has its own home, finance can change overtime again tomorrow without touching operations.
Which best describes a "reason to change" in the Single Responsibility Principle?
When to apply it — and when not to
Split when you can name two different askers, when unrelated tests break together, or when a class name needs "And"/"Manager"/"Util". Don't split a cohesive class into one-method fragments because it "does more than one thing" in a literal sense — that scatters one idea across files and raises coupling.
Pros and cons
| Pros | Cons |
|---|---|
| Changes stay local to one owner's code | More, smaller classes to navigate |
| Smaller classes are easier to test | Over-splitting fragments one concept |
| Fewer merge conflicts between teams | Needs a judgement call on who the "actors" are |
How it relates
- It's cohesion stated as a rule.
- The smells Large Class and Divergent Change are SRP violations; Extract Class (Moving Features) is the fix.
- Open/Closed is next: once responsibilities are separated, you can extend one without editing the others.
A Money class has 20 methods — add, subtract, convert, format, compare, and so on. Does it violate SRP?
Key terms
- Single Responsibility Principle — A class should have only one reason to change.
- Actor — A person, team, or concern whose needs drive changes to a piece of code.
- Extract Class — The refactoring that moves one responsibility out into a new class.