SOLID Quick Reference
| Letter | Principle | Core Idea |
|---|---|---|
| S | Single Responsibility | One class, one reason to change |
| O | Open/Closed | Extend without modifying existing code |
| L | Liskov Substitution | Subtypes must honour parent contracts |
| I | Interface Segregation | Many specific interfaces > one fat interface |
| D | Dependency Inversion | Depend on abstractions, not concretions |
S Single Responsibility Principle
A class should have one reason to change. If multiple stakeholders or concerns drive changes to the same class, split it.
Bad: God class with multiple responsibilities
Good: Each class has one responsibility
How to spot SRP violations
If you need to use "and" to describe what a class does ("it handles authentication AND sends emails AND generates reports"), it violates SRP. Each "and" is a separate responsibility.
O Open/Closed Principle
Software entities should be open for extension (add new behaviour) but closed for modification (don't change existing, tested code). New features plug in through abstractions, not if/else chains.
Bad: Modifying the class for every new payment type
Good: New payment types extend without modifying
L Liskov Substitution Principle
Objects of a subclass must be replaceable by objects of the parent class without breaking the program. Subclasses should honour the behavioural contract of the parent — not weaken preconditions or strengthen postconditions.
Classic violation: Square extends Rectangle
LSP and "is-a" — Geometrically True, Behaviourally False
A square IS geometrically a rectangle. But a Square cannot substitute for a Rectangle in code that relies on independent width/height. LSP is about behavioural substitutability, not taxonomic relationships. When a subclass overrides methods to restrict behaviour, that is an LSP violation.
I Interface Segregation Principle
Clients should not be forced to depend on methods they do not use. A "fat" interface forces implementing classes to implement methods that are irrelevant to them — leading to empty stub implementations.
D Dependency Inversion Principle
High-level modules (business logic) should not depend on low-level modules (database, email, file system). Both should depend on abstractions (interfaces). This enables testing with mock dependencies and swapping implementations.
DIP and Dependency Injection Containers
Frameworks like Spring (Java), Angular (TypeScript), Laravel (PHP), and ASP.NET Core (C#) provide DI containers that automatically wire up dependencies. You define what each class needs (via constructor or annotations) and the framework injects the right implementations — making DIP effortless at scale.
How We Research and Update This Guide
We test the underlying formula or workflow, compare outputs with reliable references, and revise examples whenever the page content changes.
- The workflow or formula is tested directly in the tool and compared against independent reference examples.
- Examples are kept practical so readers can verify the result without hidden assumptions.
- Pages are revised whenever the interface, calculation flow, or surrounding guidance materially changes.
Frequently Asked Questions — SOLID Principles
S — Single Responsibility Principle: a class should have only one reason to change. O — Open/Closed Principle: classes should be open for extension but closed for modification. L — Liskov Substitution Principle: objects of a subclass should be replaceable with the parent class without breaking the program. I — Interface Segregation Principle: clients should not be forced to depend on interfaces they do not use. D — Dependency Inversion Principle: high-level modules should not depend on low-level modules; both should depend on abstractions.
A class should do one thing and have one reason to change. If your UserService class handles authentication, sends emails, AND generates PDF reports, any change to PDF generation could break authentication logic. Split into AuthService, EmailService, and ReportService. Benefits: easier testing (smaller units), less coupling, changes are localised. "Reason to change" means stakeholder/actor — if different people/teams need different behaviours, those behaviours should be in different classes.
Classes should be open for extension (you can add new behaviours) but closed for modification (you do not change existing code). Achieved through abstractions — instead of modifying a PaymentProcessor class every time a new payment method is added, define a PaymentMethod interface and add new implementations (CreditCard, PayPal, Stripe). The processor never changes; new methods plug in via the interface.
If S is a subtype of T, then objects of T may be replaced with objects of S without breaking the program. Classic violation: Square extends Rectangle. A rectangle has independent width and height. A square must have equal sides. Setting a Rectangle's width to 5 and height to 3 gives area 15. Doing the same on a Square-as-Rectangle gives area 9 (square overrides setWidth to also set height). The Square breaks the Rectangle contract — it violates LSP.
High-level modules should not depend on low-level modules; both should depend on abstractions. Instead of UserService directly instantiating MySQLDatabase (tight coupling — hard to test, hard to swap), UserService should depend on a DatabaseInterface. The concrete MySQLDatabase (or MockDatabase in tests) is injected in. This enables Dependency Injection — passing dependencies in from outside rather than creating them internally.
No — SOLID is guidance, not law. For small scripts or prototypes, strict SOLID adds unnecessary complexity. For long-lived production codebases with teams, SOLID pays dividends. Apply it where the complexity is warranted. The most commonly violated and highest-value principles are Single Responsibility (prevents God classes) and Dependency Inversion (enables testing). Start with those.