So you’ve heard the term “SOLID principles” thrown around in code reviews, blog posts, and Java interviews — and SRP (Single Responsibility Principle) always seems to come up first.

But what does it actually mean in day-to-day coding?

The One-Line Definition

SRP says: a class should have only one job.

That’s it.

No fancy theory needed. If your class is doing more than one “job,” it’s probably violating SRP — and that usually leads to messy, hard-to-maintain code down the line.

A Quick Java Example: The “God Class” Problem

Here’s a class many developers write without thinking twice:

public class Invoice {

  private String customerName;
  private double amount;

  public Invoice(String customerName, double amount) {
    this.customerName = customerName;
    this.amount = amount;
  }

  public double calculateTotalWithTax() {
    return amount + (amount * 0.18);
  }

  public void saveToDatabase() {
    System.out.println("Saving invoice for " + customerName + " to DB");
  }

  public void sendEmailToCustomer() {
    System.out.println("Sending invoice email to " + customerName);
  }

  public void printInvoice() {
    System.out.println("Invoice for: " + customerName);
    System.out.println("Total: " + calculateTotalWithTax());
  }
}

Looks harmless, right? But this Invoice class is now responsible for:

  • Holding invoice data
  • Calculating tax
  • Talking to a database
  • Sending emails
  • Printing/formatting output

That’s five jobs in one class.

If your email provider changes, you’re editing the Invoice class.
If your tax rate logic changes, same class. If the database changes… same class again.

This is what’s often called a “God class” — and it’s a classic SRP violation.

Fixing It: One Class, One Job

Let’s break this into focused classes — each with a clear, single purpose.

// Just holds invoice data
public class Invoice {
  private String customerName;
  private double amount;

  public Invoice(String customerName, double amount) {
    this.customerName = customerName;
    this.amount = amount;
  }

  public String getCustomerName() {
    return customerName;
  }

  public double getAmount() {
    return amount;
  }
}
// Handles tax calculations
public class TaxCalculator {
  public double calculateTotalWithTax(Invoice invoice) {
    return invoice.getAmount() + (invoice.getAmount() * 0.18);
  }
}
// Handles persistence
public class InvoiceRepository {
  public void save(Invoice invoice) {
    System.out.println("Saving invoice for " + invoice.getCustomerName() + " to DB");
  }
}
// Handles notifications
public class InvoiceNotifier {
  public void sendEmail(Invoice invoice) {
    System.out.println("Sending invoice email to " + invoice.getCustomerName());
    }
}
// Handles printing/formatting
public class InvoicePrinter {
  private TaxCalculator taxCalculator;

  public InvoicePrinter(TaxCalculator taxCalculator) {
    this.taxCalculator = taxCalculator;
  }

  public void print(Invoice invoice) {
    System.out.println("Invoice for: " + invoice.getCustomerName());
    System.out.println("Total: " + taxCalculator.calculateTotalWithTax(invoice));
    }
}

Now, each class answers to exactly one “boss.”

Need to switch from email to SMS notifications?
Touch InvoiceNotifier only.

Tax rules change? 
TaxCalculator only.

Database swapped from MySQL to PostgreSQL? 
InvoiceRepository only.

How to Spot SRP Violations Quickly

Here’s a quick checklist you can run through on any class:

Can you describe the class’s purpose in one short sentence without using “and”?
If you find yourself saying “this class handles X and Y and Z” — that’s a red flag.

Does the class import things from very different areas (e.g., database drivers + email libraries + PDF generators)? That’s usually a sign it’s doing too much.

If a junior developer changes one method, could it accidentally break something completely unrelated? If yes, responsibilities are tangled together.

Is the class name generic, like ManagerHandlerProcessor, or Utils? These names often hide multiple responsibilities under one roof.

Don’t Overdo It

A common mistake developers make once they “discover” SRP is going overboard — creating a separate class for every tiny piece of logic, leading to dozens of micro-classes that are hard to navigate.

SRP isn’t about creating the maximum number of classes. It’s about grouping related logic together and separating unrelated logic. Use good judgment based on the size and complexity of your application.

Where You’ll See SRP in Real Projects

If you’ve worked with Spring Boot, you’ve already used SRP without realizing it:

  • @Controller / @RestController → handles requests
  • @Service → handles business logic
  • @Repository → handles database access
  • DTOs/Entities → represent data

This layered structure is SRP applied at an architectural level, and it’s a big reason Spring projects tend to be easier to navigate once you understand the pattern.

Why This Matters for You as a Developer

  • Cleaner code = easier code reviews
  • Focused classes = easier unit testing (you can mock dependencies easily)
  • Fewer surprises = less time spent debugging unrelated breakages
  • Better collaboration = different team members can work on different classes without stepping on each other’s toes

Final Thoughts

The Single Responsibility Principle isn’t about following a rule for the sake of it — it’s about writing Java code that’s easier to read, test, debug, and extend.
Every time you create a class, ask yourself: “What’s the one job this class is responsible for?”

If you can answer that clearly, you’re on the right track.
If you can’t, it might be time to split things up — just like we did with our Invoice example.

Want to go deeper? SRP is just the first letter in SOLID — check out the Open/Closed Principle next to see how to extend code without modifying existing classes.

Categorized in:

Design Principles,