Single Responsibility Principle illustration
All posts
/ 2 min read

Single Responsibility 1/5

An introduction to the first letter of the S.O.L.I.D principles: what Single Responsibility really means and how to apply it.

  • SOLID
  • TypeScript
  • OOP
  • Software Design

This is the first post in a five-part series on the SOLID principles. Before diving in, here’s a quick overview of what SOLID stands for:

  • S - Single Responsibility Principle
  • O - Open/Closed Principle
  • L - Liskov Substitution Principle
  • I - Interface Segregation Principle
  • D - Dependency Inversion Principle

The acronym was coined by Michael Feathers to describe principles originally articulated by Robert C. Martin (Uncle Bob) in the early 2000s. Applying them leads to code that is more maintainable, extensible, and easier to reason about.

What is Single Responsibility?

Uncle Bob’s original definition is: “A class should be responsible for one thing.”

He later refined this to: “A class should only have one reason to change.”

The refinement is important. Bug fixes and refactoring are changes, but they aren’t what this principle is about. The key question is: which stakeholder necessitates each change?

A Concrete Example

Consider this class:

class User {
  public calculatePay(): Money {}
  public save(): void {}
  public reportHours(): string {}
}

This single class handles three distinct concerns:

  1. Pay calculations (driven by the finance team)
  2. Database persistence (driven by the infrastructure team)
  3. Hour reporting (driven by HR)

When the database system changes, there is no reason the business logic should need to change with it. These are separate responsibilities owned by separate stakeholders.

Refactoring It

Extract persistence into its own class:

class UserRepository {
  save(user: User): void {
    // Database call implementation
  }
}

The same logic applies to calculatePay() and reportHours(). Move them to dedicated classes like MoneyCalculator and UserHourReporter.

Now the User class only changes when the business requirements about users themselves evolve, for example adding an address or registration number field.

How to Apply It in Practice

Before writing code, ask yourself: “Should this class genuinely handle this functionality?”

Identifying responsibility boundaries takes practice. The question won’t always have an obvious answer, but asking it consistently prevents the most common violations.

Up Next

The next post in this series covers the Open/Closed Principle: “Software entities should be open for extension, but closed for modification.”