The full picture
| Principle | Class level | Architecture level |
|---|---|---|
| SRP | One class, one job | A service owned by one team, changing for one business reason; avoids the shared 'common' service everyone edits |
| OCP | Strategy/template method over switch | New payment provider = new adapter deployed, zero edits to checkout core |
| LSP | Subtype honors contract | Any PaymentGateway impl honors idempotency + timeout semantics; a 'fast fake' that skips them breaks callers |
| ISP | Small interfaces per client | BFFs / consumer-driven contracts instead of one giant API for all clients |
| DIP | Depend on abstractions | Ports & adapters: domain doesn't import Kafka/JPA; infrastructure depends on domain, never the reverse |
The senior move is admitting the tensions: SRP taken too far yields anemic classes and a thousand paper-cut services; OCP's abstractions have a carrying cost before the second implementation exists (YAGNI); DIP without discipline becomes interface-for-everything ceremony. Principles are forces to balance, and the balancing test is always the same — what changes together should live together; what changes for different reasons should be separable.