agentleFS
kayamankayaman/skills/solid-principles/SKILL.md

solid-principles

Skill3 starsChanged 8 months agoCC0-1.0View on GitHub

Enforces SOLID principles (SRP, OCP, LSP, ISP, DIP) in object-oriented and multi-paradigm code. Use when designing classes and interfaces, evaluating responsibility assignment, reviewing code for coupling issues, applying dependency inversion, refactoring fat interfaces, or checking that subtypes are truly substitutable.

11 other projects use a version of this.

Adopted by

Most recently taken up by

The file

Show its rules and text

We split this skill into the rules it gives an agent

24 rules. 0 are common, 20 are distinctive, and 4 are very specific to this project.

  • 0

    Common

    Most of the 9 edited versions we read keep these.

  • 20

    Distinctive

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    MUST identify which actor (stakeholder/team) each class serves

    line 31
  2. Distinctive:

    MUST separate classes that serve different actors, even if they use similar data

    line 32
  3. Distinctive:

    SHOULD use SRP as the primary driver for class extraction during refactoring

    line 33
  4. Very specific:

    MUST use polymorphic dispatch (interface implementations) instead of if/else or switch chains that check type

    line 46
  5. Distinctive:

    SHOULD design plugin points at known variation boundaries — but only where variation has been demonstrated

    line 47
  6. Distinctive:

    MUST NOT add speculative extension points "just in case" — this violates YAGNI

    line 48
  7. Distinctive:

    MUST verify that overridden methods honor the parent's contract, not just its signature

    line 80
  8. Very specific:

    MUST NOT throw UnsupportedOperationException or NotImplementedException in subtype methods — this is an LSP violation

    line 81
  9. Distinctive:

    MUST NOT override a method to do nothing or return a hardcoded dummy value

    line 82
  10. Distinctive:

    SHOULD use "Design by Contract" (Meyer): document and enforce preconditions, postconditions, and invariants

    line 83
  11. Distinctive:

    MUST split fat interfaces into focused role interfaces based on client needs

    line 95
  12. Distinctive:

    MUST NOT create a single interface that serves all possible clients

    line 96
  13. Very specific:

    SHOULD name role interfaces by capability: Printable, Serializable, ForPlacingOrders — not by implementation: PrinterInterface

    line 97
  14. Distinctive:

    MUST define interfaces in the high-level module, not the low-level module (the abstraction belongs to the consumer)

    line 124
  15. Distinctive:

    MUST use constructor injection as the default DI mechanism

    line 125
  16. Very specific:

    MUST NOT instantiate concrete dependencies inside business logic classes (new ConcreteService())

    line 126
  17. Distinctive:

    SHOULD use a DI container or composition root to wire dependencies at application startup

    line 127
  18. Distinctive:

    SHOULD NOT depend on concrete classes across module boundaries — always go through an interface

    line 128
  19. Distinctive:

    All five principles converge on one practice: program to an interface.

    line 141
  20. Distinctive:

    MUST NOT create an interface for every class — only when there are (or will be) multiple implementations or the boundary needs testing …

    line 173
  21. Distinctive:

    MUST NOT split a simple class into fragments just to satisfy SRP dogmatically

    line 174
  22. Distinctive:

    SHOULD apply SOLID when a code smell appears (shotgun surgery, divergent change, parallel inheritance), not preemptively

    line 175
  23. Distinctive:

    SHOULD balance OCP extensibility against YAGNI: being "open for extension" (good structure) is not the same as "already extended" …

    line 176
  24. Distinctive:

    When designing or reviewing code, verify:

    line 182

“Common” is measured: kept by at least 6 in 10 of the 9 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

SOLID Principles

Reference: Clean Architecture (Martin), Practical Object-Oriented Design (Metz), Code That Fits in Your Head (Seemann)

When to Apply

  • Designing new classes, interfaces, or modules
  • Reviewing code for responsibility bloat, coupling, or substitutability issues
  • Refactoring a class that changes for multiple unrelated reasons
  • Evaluating whether an abstraction or interface is appropriately sized
  • Deciding where dependencies should point in a layered or modular architecture
  • Resolving the tension between SOLID extensibility and YAGNI simplicity

S — Single Responsibility Principle

Refined definition (Martin, 2017): "A module should be responsible to one, and only one, actor."

SRP is about people and change reasons, not "doing one thing." A class violates SRP when changes requested by different stakeholders (actors) would modify the same class.

Classic example: An Employee class with calculatePay() (CFO's team), reportHours() (COO's team), and save() (CTO's team). A shared regularHours() method creates accidental coupling — when the CFO's team changes the pay calculation, the COO's hours report silently breaks.

Rules

  • MUST identify which actor (stakeholder/team) each class serves
  • MUST separate classes that serve different actors, even if they use similar data
  • SHOULD use SRP as the primary driver for class extraction during refactoring
  • Violation signals: class name includes "And" or "Manager"; class changes for unrelated business reasons

O — Open/Closed Principle

"Software entities should be open for extension, but closed for modification."

Achieve this through abstract interfaces and polymorphism, not inheritance hierarchies. New behavior is added by creating new implementations, not by modifying existing code.

Rules

  • MUST use polymorphic dispatch (interface implementations) instead of if/else or switch chains that check type
  • SHOULD design plugin points at known variation boundaries — but only where variation has been demonstrated
  • MUST NOT add speculative extension points "just in case" — this violates YAGNI
  • Martin's caveat: "Since closure cannot be complete, it must be strategic" — choose which changes to protect against based on experience
Violation SignalRemedy
switch on type or enum to decide behaviorExtract interface; create implementations per case
Adding a new feature requires modifying existing classesIntroduce abstraction at the variation point
Shotgun surgery — one change touches many filesConsolidate related behavior behind a single interface

L — Liskov Substitution Principle

Liskov (1987): Subtypes MUST be substitutable for their base types without altering program correctness.

This is behavioral subtyping — semantic, not just syntactic. The compiler won't catch most violations.

Formal Constraints

ConstraintRule
PreconditionsA subtype MUST NOT strengthen preconditions (cannot require more from callers)
PostconditionsA subtype MUST NOT weaken postconditions (must deliver at least what the parent promises)
InvariantsA subtype MUST preserve all invariants of the parent type
History ConstraintA subtype MUST NOT allow state changes the parent would not allow

The Rectangle/Square Problem

Square cannot be a subtype of Rectangle because setting width independently of height violates Rectangle's behavioral contract (width and height are independent). The "is-a" relationship in the real world does not imply "is-a-subtype" in code.

Rules

  • MUST verify that overridden methods honor the parent's contract, not just its signature
  • MUST NOT throw UnsupportedOperationException or NotImplementedException in subtype methods — this is an LSP violation
  • MUST NOT override a method to do nothing or return a hardcoded dummy value
  • SHOULD use "Design by Contract" (Meyer): document and enforce preconditions, postconditions, and invariants

I — Interface Segregation Principle

"No client should be forced to depend on methods it does not use."

Fat interfaces force implementors to provide dummy implementations or throw exceptions for methods they don't need — violating both ISP and LSP.

Rules

  • MUST split fat interfaces into focused role interfaces based on client needs
  • MUST NOT create a single interface that serves all possible clients
  • SHOULD name role interfaces by capability: Printable, Serializable, ForPlacingOrders — not by implementation: PrinterInterface
  • ISP applies to APIs too: SHOULD NOT force API consumers to parse responses containing data they don't need
Violation SignalRemedy
Implementors throw UnsupportedOperationException for some methodsSplit into role interfaces; implement only what applies
Interface has methods used by different, unrelated clientsExtract client-specific interfaces
A change to one method forces recompilation of unrelated clientsSegregate into independent interfaces

D — Dependency Inversion Principle

Two parts:

  • (A) High-level modules MUST NOT depend on low-level modules — both depend on abstractions
  • (B) Abstractions MUST NOT depend on details — details depend on abstractions

DIP vs DI vs IoC

TermWhat It IsLevel
DIPDesign principle — depend on abstractions, not concretionsArchitecture principle
DI (Dependency Injection)Pattern — supply dependencies from outside via constructor, setter, or methodImplementation technique
IoC (Inversion of Control)Paradigm — framework calls your code, not the other way aroundBroad principle

Rules

  • MUST define interfaces in the high-level module, not the low-level module (the abstraction belongs to the consumer)
  • MUST use constructor injection as the default DI mechanism
  • MUST NOT instantiate concrete dependencies inside business logic classes (new ConcreteService())
  • SHOULD use a DI container or composition root to wire dependencies at application startup
  • SHOULD NOT depend on concrete classes across module boundaries — always go through an interface

How SOLID Principles Interconnect

SRP ──enables──▶ OCP (single-responsibility classes are extended, not modified)
DIP ──enables──▶ OCP (abstractions allow substitutable implementations)
LSP ──ensures──▶ OCP extensions don't break behavior
ISP ──prevents─▶ LSP violations (no unsupported methods forcing dummy implementations)

All five principles converge on one practice: program to an interface.


SOLID in Different Paradigms

Functional Programming

PrincipleFP Equivalent
SRPFunctions and modules have a single purpose
OCPHigher-order functions extend behavior without modifying existing functions
LSPFunction signatures are contracts; substituting compatible functions preserves correctness
ISPModules export only what consumers need; minimal public APIs
DIPPass functions as parameters instead of depending on concrete implementations

Microservices

PrincipleMicroservice Equivalent
SRPEach service owns a single business capability
OCPStable API contracts; new behavior via new endpoints or new services
LSPConsumer-driven contract tests verify substitutability
ISPMinimal endpoint surface per service; BFF pattern for client-specific APIs
DIPMessage queues and event buses as abstractions between services

Balancing SOLID with Simplicity

SOLID principles are guidelines, not rigid rules. Apply them where complexity warrants it.

  • MUST NOT create an interface for every class — only when there are (or will be) multiple implementations or the boundary needs testing isolation
  • MUST NOT split a simple class into fragments just to satisfy SRP dogmatically
  • SHOULD apply SOLID when a code smell appears (shotgun surgery, divergent change, parallel inheritance), not preemptively
  • SHOULD balance OCP extensibility against YAGNI: being "open for extension" (good structure) is not the same as "already extended" (speculative code)

Checklist

When designing or reviewing code, verify:

  • Each class is responsible to one actor — changes for different business reasons live in different classes
  • New behavior is added via new implementations, not by modifying existing code (where variation is known)
  • Subtypes are fully substitutable — no UnsupportedOperationException, no weakened contracts
  • Interfaces are segregated by client role — no implementor is forced to provide dummy methods
  • Dependencies point toward abstractions — high-level modules define the interfaces they need
  • Constructor injection is used for all required dependencies
  • No speculative abstractions — every interface has a demonstrated need
  • SOLID is balanced against KISS/YAGNI — the right level of abstraction for the current complexity

Key References

BookAuthor(s)PublisherYear
Agile Software Development: Principles, Patterns, and PracticesRobert C. MartinPearson2002
Clean ArchitectureRobert C. MartinPearson2017
Practical Object-Oriented Design (2nd ed.)Sandi MetzAddison-Wesley2018
Code That Fits in Your HeadMark SeemannAddison-Wesley2021
A Philosophy of Software Design (2nd ed.)John OusterhoutStanford2021
Object-Oriented Software ConstructionBertrand MeyerPrentice Hall1997
Get Your Hands Dirty on Clean Architecture (2nd ed.)Tom HombergsPackt2023

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.