SOLID Principles
Apply SOLID principles (SRP, OCP, LSP, ISP, DIP) for tactical class design, method extraction, and interface definition. Use when reviewing class design, refactoring code for maintainability, or designing interfaces and abstractions.
npx skills add github:pantheon-org/tekhne --skill software-engineering--design-principles--solid-principlesTactical design principles for class-level decisions, interface design, and dependency management.
When to Use
- Reviewing class design for single responsibility violations
- Evaluating interface design and client dependencies
- Refactoring code with tight coupling or poor testability
- Designing new classes, methods, or abstractions
- Assessing whether to extend or modify existing code
When Not to Use
- Architectural decisions about service boundaries or module structure (use clean-architecture)
- Choosing structural design patterns (use design-patterns)
- Configuring testing strategies (use testable-design)
SOLID Checklist
Apply during code review or design:
| Principle | Check | Refactor Signal |
|---|---|---|
| SRP | Does this class have one reason to change? | Multiple concerns → split into focused classes |
| OCP | Can you extend behavior without modifying existing code? | Adding features requires editing stable code → use abstraction |
| LSP | Do subtypes preserve base behavior contracts? | Subtype breaks parent assumptions → redesign hierarchy |
| ISP | Do clients depend only on methods they use? | Clients ignore many interface methods → split interface |
| DIP | Do you depend on abstractions, not concrete details? | Direct concrete coupling → introduce interface/port |
Workflow
Step 1: Identify SRP Violations
Output: List of classes with multiple reasons to change.
Ask for each class:
- Does it handle more than one concern (e.g., business logic + persistence + notifications)?
- Would changes to different features require editing this class?
Example:
SRP violation: UserService handles auth, persistence, and notifications.
Refactor: Extract NotificationService and PersistenceGateway.
Validation: Each class now has one reason to change.
Step 2: Check Open-Closed Compliance
Output: List of areas where new features require modifying stable code.
Ask:
- Do new features require editing existing code?
- Could abstraction/polymorphism eliminate the modification?
Example:
OCP violation: Adding payment processor requires editing PaymentService.
Refactor: Extract PaymentProcessor interface + implementations.
Validation: New processors added without touching existing code.
Step 3: Validate Liskov Substitution
Output: Subtypes that break base type contracts.
Check:
- Do subtypes strengthen preconditions or weaken postconditions?
- Do clients need to check types before using them?
Example:
LSP violation: CachedRepository.save() throws exception when cache is full.
Refactor: Base Repository contract promises no exceptions; use Result type instead.
Step 4: Segregate Interfaces
Output: Interfaces split by client needs.
Check:
- Do clients depend on methods they never call?
- Are there multiple unrelated method groups in one interface?
Example:
ISP violation: IUserRepository has save(), find(), and sendNotification().
Refactor: Split into IUserRepository and INotificationService.
Step 5: Invert Dependencies
Output: Abstractions introduced at module boundaries.
Check:
- Do high-level modules depend on low-level modules?
- Are concrete types instantiated in business logic?
Example:
DIP violation: OrderService instantiates PostgresRepository.
Refactor: OrderService depends on IOrderRepository interface.
Injection: Container provides PostgresRepository implementation.
Quick Commands
# Find SRP violations (multiple concerns)
rg -n "class.*Service.*Repository|class.*Manager.*Handler" src
# Find OCP violations (type checks)
rg -n "instanceof|typeof.*==|switch.*type" src
# Find DIP violations (concrete instantiation)
rg -n "new [A-Z].*Repository\(|new [A-Z].*Service\(" src
Anti-Patterns
NEVER apply SOLID as a mechanical checklist to every class
- WHY: SOLID principles are design guidelines, not mandates; applying them without judgment creates unnecessary abstraction layers and over-engineered code in simple domains where change pressure does not exist.
- BAD: Extract an interface for every class because “you should always program to interfaces” (misapplied DIP), resulting in one-implementation interfaces that add indirection with no benefit.
- GOOD: Apply SOLID principles where the design is causing pain — tight coupling, frequent change in one direction, difficulty testing — not as a universal rule regardless of context.
NEVER create God Classes to avoid “too many files”
- WHY: Condensing multiple responsibilities into one class saves file count but creates high coupling, low cohesion, and fragile code where unrelated changes break each other.
- BAD: A
UserManagerclass that handles authentication, profile editing, email notifications, and audit logging — violating SRP by having four distinct reasons to change. - GOOD: Split into
AuthService,UserProfileService,NotificationService, andAuditLogwith clear single responsibilities and independent change trajectories.
NEVER confuse ISP with “one method per interface”
- WHY: The Interface Segregation Principle says clients should not depend on methods they do not use; it does not mean every interface must have exactly one method, which leads to over-granularization and interface explosion.
- BAD: Creating dozens of single-method interfaces for every operation (
ICanSave,ICanFind,ICanDelete) that are always implemented and injected together. - GOOD: Group cohesive method sets into role-specific interfaces that represent a single role or behavior contract used by a specific type of client.
NEVER use deep inheritance to satisfy OCP when composition achieves the same result
- WHY: Deep inheritance hierarchies are fragile; subclass changes ripple up the chain, LSP violations are common, and the Open/Closed Principle is better achieved through composition and dependency injection.
- BAD:
class PremiumUser extends AuthenticatedUser extends User extends BaseUserto add behavior, creating a four-level hierarchy where changing any layer risks breaking all subclasses. - GOOD: Inject behavior through composition:
new User(new PremiumFeatureSet())— open for extension by adding new feature sets, closed for modification of theUserclass itself.
References
| Topic | Reference | When to Use |
|---|---|---|
| Step-by-step review questions, output format and candidate searches | references/review-checklist.md | Running a full SOLID review |
SOLID principles are tactical class-level design rules. For architectural decisions (boundaries, modules, dependencies), use the clean-architecture skill.
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Runtime validation via eval scenarios proving the skill actually changes agent behaviour. Requires evals/ directory, instructions.json, ≥3 scenarios, and ≥80% instruction coverage in summary.json.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
Runtime validation via eval scenarios proving the skill actually changes agent behaviour. Requires evals/ directory, instructions.json, ≥3 scenarios, and ≥80% instruction coverage in summary.json.
No eval scenarios available.
SOLID Review Checklist
Use this reference when running a SOLID review step by step. Each step lists the questions to ask, the signal that a refactor is needed, and the output to record.
Output format
Record each finding in three lines:
<Principle> violation: <what is wrong and where>.
Refactor: <the change>.
Validation: <how to confirm the problem is gone>.
SRP: one reason to change
- Does the class handle more than one concern, such as business logic, persistence and notifications?
- Would changes to different features require editing this class?
- Signal: multiple concerns in one class. Split into focused classes.
OCP: extend without modifying
- Do new features require editing existing code?
- Could abstraction or polymorphism remove the edit?
- Signal: adding a feature edits stable code. Introduce an abstraction and add implementations.
- Prefer composition and injection over deep inheritance to achieve extension.
LSP: substitutable subtypes
- Do subtypes strengthen preconditions or weaken postconditions?
- Do clients need to check types before using a value?
- Signal: a subtype breaks parent assumptions. Redesign the hierarchy, or return a Result type where the base contract promises no exceptions.
ISP: client-shaped interfaces
- Do clients depend on methods they never call?
- Does one interface hold several unrelated method groups?
- Signal: clients ignore many methods. Split by client role, but keep cohesive method sets together rather than one method per interface.
DIP: depend on abstractions
- Do high-level modules depend on low-level modules?
- Are concrete types instantiated inside business logic?
- Signal: direct concrete coupling. Introduce an interface or port and inject the implementation.
Candidate searches
These searches find candidates only. Inspect each hit before calling it a violation.
rg -n "class.*Service.*Repository|class.*Manager.*Handler" src
rg -n "instanceof|typeof.*==|switch.*type" src
rg -n "new [A-Z].*Repository\(|new [A-Z].*Service\(" src
Judgement
Apply SOLID where the design causes pain: tight coupling, frequent change in one direction, or difficulty testing. Do not extract an interface for every class.