Lesson 1 of 8 in the free SOLID Principles with Examples course on DevLadder.

"A class should have one, and only one, reason to change." — Robert C. Martin

"Reason to change" = one actor or stakeholder. A class that formats invoices and saves them and emails them changes when finance, DBAs or the email provider change.

// ❌ Violates SRP — three reasons to change public class InvoiceService { public decimal Calculate(Invoice i) => i.Lines.Sum(l => l.Qty * l.Price) * 1.18m; public void Save(Invoice i) { using var c = new SqlConnection("..."); /* INSERT ... */ } public void Email(Invoice i) { var smtp = new SmtpClient("smtp.example.com"); /* ... */ } } // ✅ One responsibility each public class InvoiceCalculator { public decimal Total(Invoice i) => i.Lines.Sum(l => l.Qty * l.Price) * 1.18m; } public interface IInvoiceRepository { Task SaveAsync(Invoice i); } public interface IInvoiceSender { Task SendAsync(Invoice i); } public class InvoiceWorkflow(InvoiceCalculator calc, IInvoiceRepository repo, IInvoiceSender sender) { public async Task CompleteAsync(Invoice i) { i.Total = calc.Total(i); await repo.SaveAsync(i); await sender.SendAsync(i); } } 

Interview tip: SRP doesn't mean "one method per class". It means one cohesive job. Controllers that only orchestrate are fine.

Interview questions on SOLID Principles with Examples

What are the SOLID principles? Explain each briefly.

  • Single Responsibility: one reason to change per class.
  • Open/Closed: extend behaviour by adding code, not modifying existing code.
  • Liskov Substitution: subclasses must work anywhere the base class works.
  • Interface Segregation: small client-specific interfaces instead of a fat one.
  • Dependency Inversion: high- and low-level modules depend on abstractions; inject dependencies.

Follow with one concrete example from your own project, e.g. "payment providers behind IPaymentGateway, registered in DI — that's OCP and DIP."

Give a real example of the Single Responsibility Principle.

An InvoiceService that calculates totals, writes to SQL and sends email has three reasons to change. Split it into InvoiceCalculator, IInvoiceRepository and IInvoiceSender, with a small workflow class orchestrating them. Now an SMTP-to-SendGrid change touches only the sender, and each part has focused unit tests.

How do you implement the Open/Closed Principle in C#?

Through abstractions and polymorphism: define an interface (IDiscountRule, IExportFormatter), implement each variant as its own class, and let the consumer receive IEnumerable<IDiscountRule> from DI. Adding a variant means a new class and registration; no existing, tested code changes. Other OCP tools: Strategy, Decorator, middleware, filters, events.

Read the full lesson with progress tracking and the rest of the course on DevLadder — free step-by-step tutorials and interview questions for developers.

Next in the course: O — Open/Closed Principle →