solid
SOLID Principles
Five core principles of object-oriented design that lead to systems which are simpler to maintain, test, and extend. Robert C. Martin (Uncle Bob) formalized these ideas, and Michael Feathers coined the SOLID acronym.
Principle Index
| Principle | Summary | Reference |
|---|---|---|
| S — Single Responsibility | A class should have only one reason to change | reference |
| O — Open/Closed | Open for extension, closed for modification | reference |
| L — Liskov Substitution | Subtypes must be substitutable for their base types | reference |
| I — Interface Segregation | Prefer many specific interfaces over one general-purpose interface | reference |
| D — Dependency Inversion | Depend on abstractions, not concretions | reference |
Why SOLID Matters
Without SOLID, codebases gradually develop these problems:
- Rigidity — one change ripples through many unrelated modules
- Fragility — modifying one area breaks another seemingly unconnected area
- Immobility — components are so intertwined that extracting them for reuse is impractical
- Viscosity — doing things correctly is harder than hacking around the design
SOLID tackles each of these by defining clear boundaries, explicit contracts, and flexible points for extension.
Quick Decision Guide
| Symptom | Likely Violated Principle | Fix |
|---|---|---|
| Class does too many things | SRP | Split into focused classes |
| Adding a feature requires editing existing classes | OCP | Introduce polymorphism or strategy |
| Subclass breaks when used in place of parent | LSP | Fix inheritance hierarchy or use composition |
| Classes forced to implement unused methods | ISP | Break interface into smaller ones |
| High-level module imports low-level details | DIP | Introduce an abstraction layer |
Quick Examples
SRP — Before and After
// BEFORE: Class handles both user data AND email sending
class UserService {
public function createUser(string $name, string $email): void { /* ... */ }
public function sendWelcomeEmail(string $email): void { /* ... */ }
}
// AFTER: Each class has one responsibility
class UserService {
public function __construct(private UserNotifier $notifier) {}
public function createUser(string $name, string $email): void {
// persist user...
$this->notifier->welcomeNewUser($email);
}
}
class UserNotifier {
public function welcomeNewUser(string $email): void { /* ... */ }
}
OCP — Extend Without Modifying
interface DiscountPolicy {
public function calculate(float $total): float;
}
class PercentageDiscount implements DiscountPolicy {
public function __construct(private float $rate) {}
public function calculate(float $total): float {
return $total * $this->rate;
}
}
// Adding a new discount type requires NO changes to existing code
class FlatDiscount implements DiscountPolicy {
public function __construct(private float $amount) {}
public function calculate(float $total): float {
return min($this->amount, $total);
}
}
DIP — Depend on Abstractions
// High-level policy depends on abstraction, not on database details
interface OrderRepository {
public function save(Order $order): void;
}
class PlaceOrderHandler {
public function __construct(private OrderRepository $repository) {}
public function handle(PlaceOrderCommand $cmd): void {
$order = Order::create($cmd->items);
$this->repository->save($order);
}
}
Relationships Between Principles
The five principles complement and reinforce one another:
- SRP + ISP: Both narrow the surface area of a class or interface to a single, focused concern
- OCP + DIP: Abstractions are the mechanism that enables extension without modification
- LSP + OCP: Correct substitutability is essential for polymorphic extension to work
- ISP + DIP: Well-segregated interfaces make it easier to depend on precisely the right abstraction
Common Misconceptions
- "One method per class" — SRP means one reason to change, not one method. A class can contain many methods as long as they all serve the same responsibility.
- "Never modify existing code" — OCP does not prohibit bug fixes. It means new behavior should be introducible without changing existing working code.
- "Always use interfaces" — DIP calls for depending on abstractions. Sometimes a well-crafted base class is the appropriate abstraction. Do not create interfaces for classes that will never have a second implementation.
- "Inheritance is bad" — LSP does not discourage inheritance. It establishes rules for correct inheritance so that subtypes remain safely substitutable.
Best Practices
- Adopt SOLID incrementally — do not refactor everything in one pass
- Treat SOLID as a diagnostic tool: when code resists change, check which principle is being violated
- Pair with design patterns — Strategy (OCP), Adapter (DIP), and Decorator (OCP) are direct implementations of SOLID ideas
- Write tests first — TDD naturally steers designs toward SOLID compliance
- Target PHP 8.3+ with strict typing, readonly classes, and enums
More from krzysztofsurdy/code-virtuoso
symfony-components
Comprehensive reference for all 38 Symfony framework components with PHP 8.3+ and Symfony 7.x patterns. Use when the user asks to implement, configure, or troubleshoot any Symfony component including HttpFoundation, HttpKernel, DependencyInjection, Form, Validator, Cache, Messenger, Console, EventDispatcher, Workflow, Serializer, Security, Routing, Twig, Doctrine integration, or any other Symfony component. Covers APIs, configuration, best practices, and common pitfalls.
81agentic-rules-writer
Interactive tool to generate tailored rules and instruction files for any AI coding agent. Use when the user asks to set up agent rules, configure Claude Code instructions, create Cursor rules, write Windsurf rules, generate Copilot instructions, or establish consistent AI coding standards for a team. Supports 13+ agents (Claude Code, Cursor, Windsurf, Copilot, Gemini, Codex, Cline, OpenCode, Continue, Trae, Roo Code, Amp) with global, team-shared, and dev-specific scopes. Defers to the `using-ecosystem` meta-skill for ecosystem discovery (skills, agents, recommendations) and runs an interactive questionnaire for workflow preferences.
47security
Application security principles and OWASP Top 10 for building secure web applications. Use when the user asks to review code for security vulnerabilities, implement authentication or authorization, handle secrets or API keys, configure security headers, prevent injection attacks (SQL, XSS, CSRF), prepare for a security audit, or respond to a vulnerability report. Covers input validation, data protection, secrets management, session handling, and common security antipatterns.
40testing
Stack-agnostic testing principles, strategies, and patterns for building reliable test suites. Use when the user asks to design a test strategy, choose between unit/integration/e2e tests, apply TDD, fix flaky tests, improve test quality, use test doubles (mocks, stubs, fakes, spies), or review tests for antipatterns. Covers the testing pyramid, test design heuristics, arrange-act-assert structure, test isolation, property-based testing, and common testing pitfalls.
40design-patterns
Comprehensive skill for all 26 Gang of Four design patterns with practical implementations and real-world examples. Use when the user asks to apply a design pattern, refactor code using patterns, choose between competing patterns, or review existing pattern usage. Covers creational (Abstract Factory, Builder, Factory Method, Prototype, Singleton, Object Pool), structural (Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy, Private Class Data), and behavioral patterns (Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor, Null Object) with real-world examples, trade-offs, and anti-patterns.
40api-design
REST and GraphQL API design principles for consistent, predictable, and evolvable APIs. Use when the user asks to design a new API, review an existing API, choose between REST and GraphQL, plan API versioning, define error response contracts, implement pagination, or establish API standards for a team. Covers resource modeling, endpoint naming, HTTP methods, status codes, authentication patterns, rate limiting, HATEOAS, and API evolution strategies.
38