SOLID is five design principles for building maintainable, flexible OOP systems. Each addresses a common cause of rigid, fragile code.
SOLID is five design principles for building maintainable, flexible OOP systems. Each addresses a common cause of rigid, fragile code.
| Letter | Principle | One-line meaning |
|---|
| S | Single Responsibility | A class should have one reason to change |
| O | Open/Closed | Open for extension, closed for modification |
| L | Liskov Substitution | Subtypes must be usable wherever the base type is |
| I | Interface Segregation | Many small interfaces beat one fat one |
| D | Dependency Inversion | Depend on abstractions, not concrete classes |
# Dependency Inversion: high-level code depends on an abstraction
class Notifier: # abstraction
def send(self, msg): ...
class EmailNotifier(Notifier):
def send(self, msg): print("email:", msg)
class OrderService:
def __init__(self, notifier: Notifier): # injected abstraction
self.notifier = notifier # not "new EmailNotifier()"
def place(self):
self.notifier.send("order placed") # works with ANY Notifier
OrderService doesn't depend on email specifically — swap in SMS or a test double without touching it (D). Adding a new notifier type needs no change to OrderService (O).
SOLID is guidance, not law. Over-applying it — an interface per class, indirection everywhere — produces "abstraction soup" that's harder to follow than the problem it solved.
SOLID gives a shared diagnostic language: reviewers can name why a class is hard to change ("this violates SRP") instead of arguing by taste.
Applied with judgment, the principles reduce ripple effects — changes stay local, new features extend rather than rewrite, and tests can substitute fakes for real dependencies.