dependency-inversion
Skill4 starsChanged 4 months agoMITView on GitHub
Depend on abstractions rather than concretions — high-level policy and low-level detail both hinge on interfaces owned by the policy — and wire the concrete implementations in at a single composition root. Use when the user wants a class to depend on an interface rather than a concrete implementation so the two can vary independently; the user wants collaborators injected through the constructor instead of being created with `new` inside a class; the user wants to substitute a fake or mock for a dependency in a test without editing the class under test. Keywords: dependency-inversion, dip, solid, abstractions, dependency-injection, wiring.
In this repository119 other files it gives its agents.
- strategy-bitrix-d7-incrementalSkill
- strategy-explicit-over-magicSkill
- strategy-framework-conventionsSkill
- strategy-legacy-safe-editsSkill
- strategy-minimal-diffSkill
- strategy-no-untested-refactorSkill
- strategy-preserve-public-apiSkill
- strategy-readonly-immutabilitySkill
- strategy-security-firstSkill
- strategy-test-drivenSkill
- openapiSkill
- paginationSkill
- rest-designSkill
- versioningSkill
- cqrsSkill
- hexagonalSkill
- repository-patternSkill
- service-layerSkill
- bitrixSkill
- findings-reportSkill
- laravelSkill
- performanceSkill
- phpSkill
- project-analysisSkill
- refactoringSkill
- roi-prioritizationSkill
- securitySkill
- stack-detectionSkill
- cache-layersSkill
- component-engineSkill
- d7-eventsSkill
- d7-loaderSkill
- d7-orm-queriesSkill
- iblock-elementsSkill
- kernel-main-moduleSkill
- localizationSkill
- controllersSkill
- ormSkill
- validationSkill
- controllersSkill
- query-builderSkill
- routingSkill
- autoload-psr4Skill
- dependency-managementSkill
- scriptsSkill
- connection-poolingSkill
- mysql-indexingSkill
- redisSkill
- transactionsSkill
- ci-github-actionsSkill
- deployment-strategiesSkill
- docker-phpSkill
- env-configSkill
- associationsSkill
- entities-mappingSkill
- orm-basicsSkill
- entity-apiSkill
- module-developmentSkill
- routing-controllersSkill
- formsSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 3 are distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 64
Do not add an interface for a small internal helper that has one caller, no I/O, and no realistic second implementation.
- Distinctive: line 65
Do not invert a dependency on a stable standard-library type or a pure value (a string, a DateTimeImmutable, an array helper).
- Distinctive: line 122
Not applicable — no I/O, auth, or untrusted input.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
<!-- generated: do not edit — source: knowledge/atoms/architecture/dependency-inversion/ --><!-- schema: atomVersion=3 skillVersion=2 graphVersion=1 --><!-- compile: @php-skills/compiler v1.0.0 (deterministic build) -->Dependency Inversion Principle
When to use
- The user wants a class to depend on an interface rather than a concrete implementation so the two can vary independently.
- The user wants collaborators injected through the constructor instead of being created with
newinside a class. - The user wants to substitute a fake or mock for a dependency in a test without editing the class under test.
When NOT to use
- Do not add an interface for a small internal helper that has one caller, no I/O, and no realistic second implementation.
- Do not invert a dependency on a stable standard-library type or a pure value (a string, a DateTimeImmutable, an array helper).
Core guidance
- Depend on abstractions, not concretions — Type constructor parameters and properties against interfaces, not concrete classes, so a class collaborates with a contract rather than a specific implementation.
- Let the high-level policy own the interface — Define the interface in the high-level module that needs it and phrase it in that module's terms; the low-level detail then implements the policy's contract.
Quick recipe
- Name the role the consumer needs and declare an interface for it in the consumer's module,
e.g.
interface Mailerwithsend(Message $message): void. (verify: The interface is phrased in the consumer's terms, not a backend's API) - Replace any
new Concrete()with a constructor parameter typed to the interface, e.g.public function __construct(private Mailer $mailer) {}. (verify: The class references only the interface and never constructs the concrete class) - Register the binding once in the composition root, e.g.
$container->bind(Mailer::class, SmtpMailer::class), so consumers receive the concrete type. (verify: Rebinding to a fake mailer in a test injects it everywhere with no consumer change)
Best practices
- Receive every collaborator as a constructor parameter typed to its interface rather than
calling
newon a concrete class inside a method - Shape each abstraction around the role the consumer needs (e.g
- Map each interface to its concrete implementation in a single composition root (a service provider or container config), never scattered across the codebase
Anti-patterns
| Anti-pattern | Why it fails | Do instead |
| Injecting the container into domain classes | A class holding the container can pull any dependency at runtime, hiding its real needs and coupling the domain to the container. | Inject only the specific abstractions the class uses, and keep container access confined to<br>the composition root. |
| A mirror interface for every class | Blanket interfaces double the type count and imply flexibility that does not exist, adding indirection without a real seam. | Introduce an abstraction only where an implementation genuinely varies or a test seam is<br>needed; otherwise depend on the class directly. |
| Type-hinting the concrete class everywhere | Consumers bound to a concrete class cannot accept an alternative or a fake, so the class becomes a hard dependency scattered across the codebase. | Extract an interface the consumers depend on and type-hint that, binding it to the concrete<br>class once at the composition root. |
See references/anti-patterns.md.
Common mistakes
- An interface shaped around one implementation — Designing the interface around the quirks of one implementation (Redis-specific methods on a cache abstraction) leaks that detail to every consumer. Fix: Define the interface from the consumer's need in neutral terms, and hide backend-specific behavior inside the implementation.
- Newing up a concrete dependency inside the class — Calling
new ConcreteMailer()inside a class hardwires that implementation and defeats the inversion the interface was meant to provide. Fix: Accept the dependency as a constructor parameter typed to its interface, and let the composition root pass in the concrete instance. - Pulling dependencies from a global container — Reaching into a static container (
app(Mailer::class)) from inside a class hides its real dependencies and re-introduces global coupling. Fix: Inject the dependencies you need as explicit constructor parameters and let the container resolve them at the composition root only.
Version notes
- Pure interfaces sharpened by PHP 8.1 types (PHP >=8.1) — PHP 8.1 enums,
neverreturn types, and readonly promotion let role interfaces and their injected implementations be expressed precisely without framework helpers. - New in initializers eases default wiring in PHP 8.1+ (PHP >=8.1) — From PHP 8.1,
newin parameter default values lets a class declare a sensible default implementation while still allowing the composition root to override it.
Security
- Not applicable — no I/O, auth, or untrusted input.
Testing
- Activation and decision behavior are verified in
eval/scenarios/architecture/dependency-inversion.yaml.
Additional resources
- references/anti-patterns.md
- references/core.md
- references/examples.md
- references/pitfalls.md
- references/recipes.md
- references/version-differences.md
Knowledge graph
Related: architecture/hexagonal, laravel/service-container
<!-- graph-fragment: graph/fragments/architecture/dependency-inversion.yaml -->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
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.

