SOLID is a set of five design principles that help create maintainable, flexible, and robust software systems. Each letter stands for a principle that guides how to structure code to minimize the impact of change.
The Principles
S — Single Responsibility One reason to change. One job. If a class has more than one axis of change, split it.
Keep one axis of change. Example:
- Bad:
UserServicedoes user CRUD + email sending. - Good:
UserService(CRUD) +EmailService(email).
O — Open/Closed Open for extension, closed for modification. Add new behavior without editing existing stable code. Use abstractions/interfaces.
Extend behavior without editing core logic. Example:
- Bad:
DiscountServicemodifies code whenever a new discount type appears. - Good: Add new
DiscountStrategyclasses;DiscountServicestays untouched.
L — Liskov Substitution Subclasses must be drop-in replacements for their parents. No surprises. If overriding breaks expectations, hierarchy is wrong.
Subtype must not violate base expectations. Example:
Bad: Penguin extends Bird but fly() throws.
Good: Bird and Penguin are separate; FlyingBird adds fly().
I — Interface Segregation Small, focused interfaces. Never force consumers to depend on methods they don’t use.
Expose only what a client actually needs. Example:
- Bad:
IStoragehassave,delete,list,watch,compressfor all clients. - Good:
IWriter,IReader,IWatcherseparated.
D — Dependency Inversion Depend on abstractions, not concrete implementations. High-level modules decide “what”, low-level modules plug in “how”.
High-level modules depend on abstractions. Example:
- Bad:
ReportServicedirectly createsnew MySQLDatabase(). - Good:
ReportServicedepends onDatabaseinterface, receives implementation via injection.
Mental Model
- S: Limit blast radius.
- O: Add, don’t edit.
- L: Replace without breaking.
- I: Only what you need.
- D: Point upward (abstract), not downward (concrete).