Skip to content
FollowtheDamnCode

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

  1. List who would ask for changes to this class. Each distinct asker is a responsibility.
  2. Move each responsibility into its own class (or module) with a name that says what it does.
  3. 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.

One class with three masters, split into three

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.

Fix it: the pay change leaked into the hours report
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 4

Run 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

ProsCons
Changes stay local to one owner's codeMore, smaller classes to navigate
Smaller classes are easier to testOver-splitting fragments one concept
Fewer merge conflicts between teamsNeeds a judgement call on who the "actors" are

How it relates

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.