SOLID principles: what each one protects
Five design principles for keeping change local, code understandable, and dependencies manageable.
What It Is
SOLID principles are five design principles that help solve known common software design problems. Goal is, change in one place does not break many other places.
Advantages are:
- Reduces the complexity of the code
- Reduces tight coupling, so the system is loosely coupled
- Reduces errors and increases reusability
- Improves readability, extensibility, and maintenance
- Gives better testability
These are goals, not guarantees. Applied too much, SOLID gives many small classes and extra layers, so there is a cost (see “Where people go wrong”).
The five principles:
- Single Responsibility (SRP): “A class should have one responsibility and one reason to change.”
- Open/Closed (OCP): “Open for extension, closed for modification.”
- Liskov Substitution (LSP): “A child class should not break the parent class’s type definition and behavior.”
- Interface Segregation (ISP): “Any client should not be forced to use an interface which is irrelevant to it.”
- Dependency Inversion (DIP): “Entities depend on abstractions, not on concrete classes.”
How It Works Inside, With Examples
Single Responsibility Principle: Longer the file or class, more difficult it will be to achieve this. The “reason” to change is one kind of requirement. If a class changes only when that one 'requirement'' changes, it follows SRP. Each class does one job completely, then passes the result to the next class.
Example: StatementGenerator builds the statement, and
StatementEmailSender sends it. If the email provider changes, the generator
stays untouched.
Open/Closed Principle: Instead of modifying working
code, add new code by extension. If you see many if/else or switch
blocks on a type, think of a base class or interface with derived classes. Two ways to do
it: add a new derived class from the base class, or let the client work through an abstract
interface.
Example: Fees depend on the account type. Instead of one
CalculateFee with an if per account type, use an
IFeeRule with SavingsFeeRule, CurrentFeeRule, and
CorporateFeeRule. A new account type is a new class, and the existing rules are
not touched.
Liskov Substitution Principle: Any subclass must be usable in place of its base class without errors. It must keep all the base behavior and may add new behavior. It must handle the same requests and finish the same tasks as the parent.
Example:
public class Account { public virtual void Withdraw(decimal amount) { /* debit */ } }
public class FixedDepositAccount : Account
{
public override void Withdraw(decimal amount)
=> throw new InvalidOperationException("Locked until maturity");
}
// A caller that loops over Account objects and calls Withdraw now crashes.
The caller trusted Account, and the subtype broke that
promise. A fix is to split the types: IWithdrawable is implemented only by
accounts that allow withdrawal, and FixedDepositAccount does not implement it.
Interface Segregation Principle: Prefer many small interfaces made for specific clients over one big general interface. Two rules: no class should be forced to implement a method it does not use, and keep interfaces small so clients only see what they need. Interfaces, like classes, should have a single responsibility.
Dependency Inversion Principle: High-level modules should not depend on low-level modules, and both should depend on abstractions. Abstractions should not depend on details, and details should depend on abstractions. Keep the high-level and low-level modules loosely coupled.
public interface IPaymentGateway { PaymentResult Charge(Account from, decimal amount); }
public class TransferService
{
private readonly IPaymentGateway _gateway;
public TransferService(IPaymentGateway gateway) => _gateway = gateway;
// TransferService never creates a concrete gateway itself
}
In production you pass the real gateway. In a unit test you pass a fake, so no real money moves.
Where People Go Wrong
- Calling Dependency Inversion (DIP) the same as dependency injection.DIP is a SOLID design principle that encourages high-level and low-level modules to depend on abstractions. Dependency Injection is a technique to provide those dependencies externally. Inversion of Control is the broader concept of delegating control over object creation or other decisions. An IoC container, such as ASP.NET Core's built-in DI container, automates dependency resolution and injection.
- Thinking LSP (Liskov Substitution Principle) is only “it compiles”. A subtype that throws for a base method compiles fine and still breaks LSP.
- Making an interface for every class “because of SOLID”. With one implementation and no real variation, it is only noise.
- Applying Open/Closed Principle (OCP) too early. Wait for the second variation before building the abstraction.
- Believing SOLID always reduces complexity. It trades one big class for many small ones, and following the code takes more jumps.
- Believing that breaking OCP always breaks SRP. They are related, but not the same, so do not claim it in an interview.
Related Notes
- Dependency injection in ASP.NET Core
- Composition over inheritance
- Method hiding (
new) vsoverride
Sources
- Architectural principles, Microsoft Learn
- Robert C. Martin, “Design Principles and Design Patterns” (2000)
- Barbara Liskov and Jeannette Wing, “A Behavioral Notion of Subtyping” (1994)
Last checked: 17 Oct 2026
Quick recall
S: one reason to change. O: extend, don’t edit (no growing if/else). L: subtype must behave like base, not only match the signature. I: small client-specific interfaces. D: depend on abstractions, not concrete classes (not the same as DI). Goals: less coupling, better testing, easier change. Cost: more classes and indirection. Examples: StatementGenerator vs EmailSender, IFeeRule per account type, IWithdrawable, IVitalsSensor vs IDoseDispenser, IPaymentGateway.
Planning a complex platform decision?
I’m always interested in thoughtful conversations around architecture, cloud strategy, and practical AI-enabled systems.
Start a Conversation