What Is the Single Responsibility Principle?

What Is the Single Responsibility Principle?

One class, one job, one reason to change. It sounds almost too simple to matter — until you see what happens to a codebase when nobody bothers to follow it.

01

The Big Idea, in One Breath

Every class in a program should be given exactly one clear job — not five unrelated jobs bundled together — so that a change to one part of a system does not accidentally put unrelated parts at risk.

Imagine a school where one single teacher is responsible for teaching every subject, grading every test, organising the cafeteria menu, driving the school bus, and fixing the plumbing. The day the plumbing breaks, math class gets cancelled. The day the bus needs a new route planned, nobody grades the science tests. One overloaded person, wearing far too many unrelated hats, means that any single problem anywhere ripples out and disrupts everything else.

The Single Responsibility Principle, often shortened to SRP, is the idea that every class in a program should be given exactly one clear job — not five unrelated jobs bundled together — so that a change to one part of a system does not accidentally put unrelated parts at risk. It is the very first letter in SOLID, the well-known set of five object-oriented design principles, and many developers consider it the most foundational of the five, since the other four tend to build naturally on top of it.

Everyday Analogy

Think about a Swiss Army knife versus a proper toolbox. A Swiss Army knife tries to be a knife, a screwdriver, scissors, and a bottle opener all in one small object — convenient for light use, but genuinely bad at each individual job compared to a dedicated tool. A toolbox, on the other hand, holds separate, focused tools, each excellent at exactly one job. When the screwdriver wears out, you replace just the screwdriver — the scissors are never affected.

This guide is part of a series exploring object-oriented design, following earlier guides on the four pillars of OOP, inheritance, polymorphism, abstract classes versus interfaces, and an overview of all five SOLID principles together. This particular guide takes a much closer look at the very first of those five letters — the “S” in SOLID — since it is widely considered the most foundational, and arguably the easiest one to start practicing today, in almost any piece of code.

02

The Formal Definition

A class should have only one reason to change. That is the entire principle in a single sentence — deceptively short, but genuinely rich once you start applying it.

The most commonly cited definition of the Single Responsibility Principle comes from software engineer Robert C. Martin, who described it this way: a class should have only one reason to change. That is the entire principle in a single sentence — deceptively short, but genuinely rich once you start applying it to real code.

It is worth being precise about what “responsibility” means here, because the word can be misleading. It does not mean “one method” or “one line of code.” A class can have several methods and still respect this principle perfectly well, as long as every one of those methods serves the same single, coherent purpose, tied to a single group of people or a single reason someone might ever need to change it.

i
In plain words

“Does this class follow the Single Responsibility Principle?” is really asking: “if I listed every reason this class might ever need to change, would all of those reasons trace back to the same single concern?”

It also helps to notice what this principle is deliberately not saying. It does not say a class should be small in terms of line count, and it does not say a class should only have one method. Plenty of perfectly well-designed classes are genuinely long, with many methods, while still respecting this principle completely — as long as every single piece of that class exists to serve the exact same underlying purpose. The measure that matters is not size; it is coherence.

03

“Reason to Change,” Explained

Everyone who might ever ask for a change to a class should be asking for the same underlying kind of reason. When two teams can trigger unrelated changes to the same file, that class is quietly serving two masters.

The phrase “reason to change” is really the heart of this whole principle, and it deserves a closer look, because it is easy to misread at first glance. It does not mean a class should only ever be edited once. It means that everyone who might ever ask for a change to that class should be asking for the same underlying kind of reason.

A more precise way some experienced developers phrase this: a class’s responsibility should be owned by a single group of people, or a single stakeholder, whose concerns are genuinely distinct from everyone else’s. If both the finance team and the marketing team could reasonably ask for changes to the exact same class, for entirely unrelated reasons, that class is probably serving two masters at once — a strong sign it should be split.

One Class too many reasons to change Finance team Marketing team IT team
When three unrelated teams can all trigger changes to one class, it is carrying too many responsibilities.

This “who’s asking” framing turns out to be surprisingly practical, especially in larger organisations where different teams genuinely do own different concerns. If the billing team wants to change how tax is calculated, and the notifications team wants to change how a confirmation email looks, and both requests land on the exact same file, that file’s design is quietly working against both teams — forcing them to coordinate over something that, ideally, should never have required any coordination at all.

04

Why It Matters

Four practical wins for anyone reading, changing, testing, or reusing code — and one very human bonus about naming.

Easier to understand

Less to hold in your head

A class doing one focused job is far quicker for anyone to read and genuinely understand.

Safer to change

Fewer accidental side effects

Editing a focused class rarely risks breaking something completely unrelated hiding nearby.

Easier to test

Narrow, predictable behaviour

A class with one job needs far fewer, far simpler tests to fully cover its behaviour.

Easier to reuse

Small, portable pieces

A focused class can often be reused elsewhere; a tangled, multi-job class rarely can.

There is also a very human, team-based reason this matters. When two developers need to change the same class for two completely unrelated reasons at the same time, they are far more likely to step on each other’s work, creating awkward conflicts and mix-ups. Keeping responsibilities separate keeps people’s work separate too.

It is worth adding one more, slightly less obvious benefit: naming gets easier. A class with exactly one job is almost always easy to name clearly and honestly — PayCalculator, EmailSender, InvoiceValidator. A class doing three unrelated jobs at once tends to end up with a vague, unhelpful name like “Manager” or “Handler,” precisely because there is no single, honest word that captures everything it is actually doing. Struggling to find a clear name is often the very first, earliest hint that a class has quietly taken on more than one responsibility.

05

How to Spot a Violation

Five reliable warning signs — sometimes called “code smells” — that a class has quietly picked up more than one responsibility.

Recognising a Single Responsibility violation is a skill that improves with practice, but there are a few reliable warning signs, sometimes called “code smells,” worth learning to notice. None of these signs are absolute proof on their own, but seeing two or three of them together in the same class is usually a strong enough signal to take a closer look.

1

The class name is vague or joined by “and”

Names like “Manager,” “Handler,” or “UserAndReportProcessor” often hint at a class quietly doing several unrelated jobs at once.

2

You struggle to describe it in one sentence without “and”

If explaining a class’s purpose naturally requires the word “and” between two unrelated ideas, that is a strong signal it is carrying two responsibilities.

3

It imports or depends on wildly unrelated things

A class that needs both a database connection and an email-sending library is often a sign two unrelated jobs got tangled together.

4

Changes for one reason keep breaking unrelated tests

If updating the tax calculation logic somehow breaks a test about sending emails, those two concerns were never properly separated.

5

The class file keeps growing longer with every unrelated feature

A file that steadily accumulates new methods for every new feature request, regardless of how unrelated each feature is, is often quietly absorbing responsibilities that should have been split off into their own classes long ago.

None of these signals require any special tooling to notice — they are really just habits of attention. Pausing, every so often, to reread a class’s full list of methods and honestly asking “do all of these genuinely belong together?” catches a surprising number of these violations early, long before they grow large enough to cause real pain.

06

A Before-and-After Example

One class doing three unrelated jobs, then the same feature refactored into three focused classes — each with exactly one reason to change.

Definitions and warning signs are useful, but nothing makes this principle click quite as clearly as watching it applied to a real, if small, piece of code. Let us walk through a small, realistic example, showing a class that violates the principle, followed by a cleaner version that respects it.

Before — one class, three unrelated jobs
class Employee:
    def __init__(self, name, hours):
        self.name = name
        self.hours = hours

    def calculate_pay(self):
        return self.hours * 25   # payroll logic

    def save_to_database(self):
        ...                        # data storage logic

    def generate_report(self):
        ...                        # reporting logic

This single class now has three completely different reasons to change: a new pay policy from the finance team, a new database structure from the engineering team, or a new report format from the operations team. None of these three teams should need to coordinate with each other just to make their own, unrelated change.

After — three focused classes, each with one job
class Employee:
    def __init__(self, name, hours):
        self.name = name
        self.hours = hours

class PayCalculator:
    def calculate_pay(self, employee):
        return employee.hours * 25

class EmployeeRepository:
    def save(self, employee):
        ...

class EmployeeReportGenerator:
    def generate(self, employee):
        ...

Now, a change to how pay is calculated only touches PayCalculator. A change to how data is stored only touches EmployeeRepository. A change to report formatting only touches EmployeeReportGenerator. Each class has exactly one reason to ever change, and each team can work confidently, knowing their changes will not accidentally disturb someone else’s responsibility.

Notice, too, what happened to the original Employee class itself in the refactored version: it shrank down to simply holding the data that describes an employee, nothing more. This is a common and healthy pattern in a well-organised codebase — a simple, focused “data” class sitting at the centre, surrounded by several separate, focused classes that each know how to do one specific thing with that data. Nobody had to guess how to extend this design either: adding a fourth concern, like a class responsible for sending a welcome email to new employees, would simply mean adding one more small, focused class alongside the existing ones, without touching any of them.

07

SRP Beyond Just Classes

The same underlying idea applies at other levels — a single function, a single class, an entire module. One coherent job at every scale.

Although the Single Responsibility Principle is usually discussed in terms of classes, the same underlying idea applies at other levels of a codebase too.

LevelWhat “One Responsibility” Looks Like
A single functionDoes one clear task — validating input, formatting text, calculating a value — not several unrelated tasks chained together.
A single classRepresents one coherent concept or job, as covered throughout this guide.
A module or packageGroups together classes and functions that all serve one broader, related purpose, like “billing” or “authentication.”

Applying this idea at every level, from the smallest function up to the largest module, creates a kind of consistent, fractal-like organisation throughout a codebase — small, focused pieces nested inside slightly larger, still-focused groupings, all the way up.

This layered consistency is genuinely valuable in practice, because it means a developer only ever needs to learn one underlying habit — “keep this focused on one job” — and can apply that same instinct whether they are writing a five-line function or organising an entire folder of related files. Rather than juggling separate rules for separate levels of a codebase, the same simple question keeps working at every scale: does everything in here genuinely belong to the same single concern?

08

SRP and Testing

A class hard to test is often a class quietly carrying more than one responsibility. This is often the first place developers really feel the difference.

One of the most practical, day-to-day benefits of the Single Responsibility Principle shows up specifically when writing automated tests, and it is worth calling out on its own, since it is often the first place developers genuinely feel the difference.

A class with one clear responsibility usually needs a correspondingly small, focused set of tests — check that PayCalculator correctly multiplies hours by rate, check a couple of edge cases like zero hours, and that is essentially the whole job covered. A class juggling three unrelated responsibilities needs tests covering all three concerns at once, and those tests inevitably become tangled together — a test that is supposed to check payroll math ends up also needing a working database connection just to run, purely because the class it is testing dragged that unrelated concern along with it.

Everyday Analogy

Imagine trying to check whether a car’s brakes work properly, but the test also requires the radio to be functioning and the air conditioning to be working, simply because all three systems were wired together into one tangled bundle. A well-designed car lets you test the brakes on their own, completely independent of the radio. Well-designed classes offer that same independence to the people testing them.

This is part of why experienced developers often treat “this class is hard to test” as an early warning sign worth investigating — more often than not, the actual underlying problem is not the testing itself, but a class quietly carrying more than one responsibility, tangled together in a way that makes isolating any single behaviour unnecessarily difficult.

09

Weighing It Fairly: Pros and Cons

Not a universally free win — a genuine trade-off. Small upfront cost, big long-term payoff for code that will actually be maintained.

Like every principle covered in this series, the Single Responsibility Principle is not a universally free win — it is a genuine trade-off, and a fair evaluation means being honest about both sides of it.

Strengths

  • Code becomes easier to read, since each class has a clear, narrow purpose.
  • Testing becomes simpler, with fewer unrelated behaviours to account for at once.
  • Teams can work on separate responsibilities with far less risk of conflict.
  • Bugs tend to stay contained within the one responsibility they actually belong to.

Trade-offs

  • A codebase ends up with more, smaller classes, which some find harder to navigate at first.
  • Deciding exactly where one responsibility ends and another begins takes real judgment and practice.
  • Over-applying it can fragment simple logic into an unnecessary number of tiny pieces.

A helpful way to hold this trade-off in mind: the Single Responsibility Principle trades a small amount of upfront navigation effort — more files, more classes to keep track of — for a much larger amount of long-term safety and clarity. For a tiny, throwaway script, that trade rarely pays off. For a codebase that a team will maintain and grow for years, it tends to pay for itself many times over, especially the first time a change needs to be made under real time pressure, and the class responsible for it turns out to be blessedly easy to find and understand.

10

Common Pitfalls to Avoid

Four recurring traps that catch even developers who genuinely understand this principle. Each easy to recognise once named.

Even developers who genuinely understand this principle can fall into a handful of recurring traps when applying it to real, evolving code.

Splitting things so finely that nothing makes sense alone

Taken too far, this principle can lead to dozens of tiny classes, each holding just one method, forcing anyone reading the code to jump between many files just to follow a single, simple idea. A responsibility should be a coherent job, not the smallest imaginable fragment of one. If understanding one simple business rule now requires opening six separate files, the splitting has likely gone further than it needed to.

Confusing “one method” with “one responsibility”

A class with several related methods can still respect this principle perfectly well, as long as all of those methods genuinely serve the same underlying purpose. The count of methods was never really the point — coherence of purpose is.

Applying it rigidly to every tiny script

A short, one-off script that will be thrown away in a day rarely benefits from being split into several formal, separate classes. This principle earns its keep most clearly in code that is expected to be maintained, extended, and revisited over time.

Guessing at future responsibilities that do not exist yet

It is tempting to preemptively split a class based on a responsibility it might need someday, even though no real need for that split exists yet. This kind of speculative splitting often guesses wrong about how the code will actually need to grow, adding structure that does not match reality. It is usually wiser to split a class once a second, genuine reason to change actually shows up, rather than imagining one in advance.

!
Watch out for

Treating “responsibility” as a fixed, universal size. What counts as one coherent responsibility can genuinely differ between a small personal project and a large, multi-team enterprise system — judgment matters more than a strict formula.

One clear job. One reason to change. One class you can name honestly in a single word.
11

Where You Will Meet This Every Day

The principle quietly shapes an enormous amount of the software behind everyday apps, even in places that never look like “code” to the person using them.

The Single Responsibility Principle quietly shapes a huge amount of the software behind everyday apps, even in places that never look like “code” to the person using them.

Everyday ExampleHow SRP Shows Up
A photo editing appThe “crop” tool, the “filter” tool, and the “share” button are separate, focused features, not one tangled mega-button.
A food delivery appOrder placement, payment processing, and delivery tracking are handled by separate, focused parts of the system.
An email clientComposing, sending, and spam-filtering are distinct concerns, each able to change independently.
1

Clear job per class

Each class exists to do exactly one thing and does it well — nothing more, nothing tacked on.

1

Reason to change

Every request to modify a class should trace back to the same underlying concern — not two different teams asking two different things.

S

First letter of SOLID

The foundational habit the other four SOLID principles quietly build on top of.

Kitchen & cabinet

Beyond software

Well-run kitchens and well-organised filing cabinets already follow the same pattern — focused stations, clearly labelled folders.

It is worth noticing this exact pattern outside of software too, since it makes the underlying idea feel less abstract. A well-run kitchen typically has a station dedicated to grilling, another to salads, another to desserts — each cook focused on one job, rather than one person trying to handle the entire menu alone. A well-organised filing cabinet keeps tax documents, medical records, and receipts in separate, clearly labelled folders, rather than one giant mixed pile. Whenever a system — a kitchen, a filing cabinet, or a piece of software — divides its work into focused, well-labelled areas, it is leaning on the exact same underlying wisdom the Single Responsibility Principle formalises for code.

12

Questions People Often Ask

Six honest questions that come up over and over — and honest answers to each.

Is not splitting one class into several just more work?

It can feel that way upfront, but the time saved later — when a change only requires touching one focused class instead of untangling a bundled one — usually more than makes up for the small extra effort spent organising things well in the first place. The cost of splitting is paid once, early; the cost of not splitting tends to be paid repeatedly, every single time someone needs to make a change.

How do I know when a class has “too many” responsibilities?

A reliable test: try describing the class’s purpose out loud in one plain sentence. If that sentence naturally needs the word “and” to connect two unrelated ideas, the class is very likely doing more than one job. Another useful test: imagine two entirely different people, each wanting an unrelated change to the same class, and ask whether they would genuinely need to coordinate with each other. If the honest answer is no, yet they would both end up editing the same file, that is worth a closer look.

Does SRP apply to functions too, or only classes?

The same underlying idea applies to functions, and even to entire modules, as covered earlier in this guide. The core principle — one coherent job, one reason to change — scales naturally to any size of code, from a five-line function all the way up to an entire folder of related files.

Is SRP the same as keeping code short?

Not exactly. A class can be genuinely long and still follow SRP well, as long as everything inside it serves the same single purpose. Length is not the deciding factor — coherence is. A short class that mixes two unrelated concerns still violates the principle, while a long class dedicated entirely to one well-defined job does not.

What is the very first step to start applying SRP in an existing codebase?

Rather than attempting a large, sweeping reorganisation all at once, most experienced developers recommend starting small: the next time a class needs to be touched for any reason, pause and honestly ask whether it is carrying more than one responsibility. If it is, that is a natural, low-risk moment to split off the unrelated piece, rather than adding yet another unrelated concern onto an already-overloaded class.

Can two classes ever share the exact same responsibility?

Generally, no — if two separate classes both seem to genuinely own the exact same concern, that is usually a sign of unnecessary duplication rather than good design. In a well-organised codebase, each distinct responsibility should have exactly one clear, findable home, so that anyone looking for where a particular concern lives knows exactly where to look.

13

Key Takeaways

A simple habit of asking one honest question about every class, again and again, over the life of a codebase: does everything in here genuinely belong to the same single job?

The Single Responsibility Principle is, at its core, a simple habit of asking one honest question about every class before it grows too large: does everything in here genuinely belong to the same single job? Answering that question honestly, again and again, over the life of a codebase, is really the entire skill this guide has been building toward.

Remember this

  • The Single Responsibility Principle states that a class should have only one reason to change.
  • “Responsibility” means a coherent job tied to one concern or stakeholder, not a fixed number of methods.
  • Violations often show up as vague class names, “and”-joined descriptions, or unrelated dependencies bundled together.
  • The same idea applies at the level of functions and modules, not just classes.
  • Used well, it makes code easier to read, test, and safely change; overused, it can fragment simple logic unnecessarily.
  • A simple, honest test for spotting violations: try describing a class’s purpose in one sentence without needing the word “and.”
  • The best time to apply this principle to existing code is naturally, the next time that code needs to be touched — not through a large, disruptive rewrite.

Leave a Reply

Your email address will not be published. Required fields are marked *