design-principles
Skill14 starsChanged 8 months agoMITView on GitHub
Use when writing production code — enforces dependency inversion and testable design through checkpoint questions before writing function signatures and constructors
24 other projects use a version of this.
Adopted by
Most recently taken up by
- twglhk/gamedev-agent-skillstheir own version, first seen Nov 8, 2026
- stigmer/stigmertheir own version, first seen Nov 8, 2026
- dirien/yet-another-agent-harnesstheir own version, first seen Nov 8, 2026
- omidnw/my-skillstheir own version, first seen Nov 3, 2026
- skrrt-sh/skillstheir own version, first seen Nov 1, 2026
- shangyiy/life-kittheir own version, first seen Nov 1, 2026
Other versions24 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 24
- launchapp-dev/animus-cli★ 39same name
- dirien/yet-another-agent-harness★ 19same name
- HermeticOrmus/design-mastery-claude-code★ 18same name
- shangyiy/life-kit★ 10same name
- stigmer/stigmer★ 9same name
- shualippe/design-principles-skill★ 8same name
- madeinlowcode/skills-claude-code★ 7same name
- Nirusan/claude-config★ 7same name
- robconery/kolea★ 7same name
- jlengrand/my-skills★ 4same name
14 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository50 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- adr-conventionSkill
- architecture-vocabularySkill
- backlog-externalSkill
- backlog-localSkill
- backlogSkill
- checkpointsSkill
- domain-glossarySkill
- git-practicesSkill
- grounded-claimsSkill
- knowledge-checkSkill
- policy-riskSkill
- positive-framingSkill
- receiving-code-reviewSkill
- skill-authoringSkill
- skill-awarenessSkill
- subagent-driven-developmentSkill
- test-driven-developmentSkill
- token-efficientSkill
- triageSkill
- verification-before-completionSkill
- workflow-bugSkill
- workflow-checkSkill
- workflow-commitSkill
- workflow-contractsSkill
- workflow-debugSkill
- workflow-decisionsSkill
- workflow-docsSkill
- workflow-doctorSkill
- workflow-epicSkill
- workflow-featureSkill
- workflow-flow-fix-pipelineSkill
- workflow-flowSkill
- workflow-grillSkill
- workflow-handoffSkill
- workflow-ideaSkill
- workflow-implementSkill
- workflow-planSkill
- workflow-proposalSkill
- workflow-prSkill
- workflow-refineSkill
- workflow-researchSkill
- workflow-reviewSkill
- workflow-startSkill
- workflow-statusSkill
- workflow-tech-reviewSkill
- workflow-update-workflowSkill
- workflow-validateSkill
- workflow-worktreeSkill
- workflow-yoloSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
13 rules. 0 are common, 10 are distinctive, and 3 are very specific to this project.
0
Common
Most of the 21 edited versions we read keep these.
10
Distinctive
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 10
Read stack.md and check the design: field.
- Distinctive: line 30
Before writing a function signature, constructor, or method — pause and ask:
- Distinctive: line 36
If the answer is "nothing" — no checkpoint needed.
- Distinctive: line 38
If the answer is "something" — continue to question 2.
- Very specific: line 59
If you're about to write new, import a concrete client, or call a constructor for something that does I/O — you're creating, not accepting.
- Distinctive: line 82
Not every language has explicit interfaces.
- Distinctive: line 83
TypeScript/Java/C#: Use interfaces or abstract types at boundaries
- Distinctive: line 84
Python: Use protocols (typing.Protocol) or ABC at boundaries
- Distinctive: line 92
Not every function needs this checkpoint.
- Distinctive: line 105
Every application has one place where concrete implementations are wired together.
- Very specific: line 118
If you find yourself writing new ConcreteService() outside the composition root, pause.
- Distinctive: line 127
When you follow both:
- Distinctive: line 137
Before marking implementation steps complete:
“Common” is measured: kept by at least 6 in 10 of the 21 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.
Files
Design Principles
Mode Selection
Read stack.md and check the design: field. If the field is missing, default to recommended.
design: value | Behavior |
strict | Every function boundary must pass the checkpoint. Rewrite if it fails. |
recommended | Check at key boundaries (constructors, service functions, handlers). Flag skips. |
off | No enforcement. Skip the rest of this skill. |
If design: off — stop reading.
Core Principle
Accept what you need. Don't create what you use.
A function that creates its own collaborators is a function that can't be tested in isolation. A function that receives them can be tested with any substitute — mock, stub, fake, or the real thing.
This is Dependency Inversion: depend on the behavior you need, not the thing that provides it.
The Checkpoint
Before writing a function signature, constructor, or method — pause and ask:
1. "What does this function need from the outside world?"
List the external collaborators: database access, HTTP clients, file system, other services, time, randomness.
If the answer is "nothing" — no checkpoint needed. Pure logic is already testable.
If the answer is "something" — continue to question 2.
2. "Am I accepting it or creating it?"
<Good>
```
// Accepts its dependency — caller decides what to pass
function createUser(repo: UserRepository, data: NewUser): Promise<User>
```
</Good><Bad>
```
// Creates its own dependency — hardwired to one implementation
function createUser(data: NewUser): Promise<User> {
const repo = new PostgresUserRepository(getConnectionPool())
// ...
}
```
</Bad>If you're about to write new, import a concrete client, or call a constructor for something that does I/O — you're creating, not accepting. Restructure.
3. "Am I depending on a behavior or an implementation?"
The parameter type should describe what you need, not who provides it.
<Good>
```
// Depends on behavior — any implementation works
interface UserRepository {
findById(id: string): Promise<User | null>
save(user: User): Promise<void>
}
```
</Good><Bad>
```
// Depends on implementation — locked to Postgres
function createUser(repo: PostgresUserRepository, data: NewUser)
```
</Bad>Not every language has explicit interfaces. The principle still applies:
- TypeScript/Java/C#: Use interfaces or abstract types at boundaries
- Python: Use protocols (typing.Protocol) or ABC at boundaries
- Go: Interfaces are implicit — accept the interface, return the struct
- Ruby/JS: Duck typing — document the expected shape, accept any object that quacks
The key: your function's contract is with a shape, not a name.
When This Matters Most
Not every function needs this checkpoint. Focus on boundaries — where your code meets the outside world:
| Boundary | Checkpoint applies | Why |
| Service/use-case functions | Yes | These orchestrate — they need collaborators |
| Constructors / factory functions | Yes | This is where dependencies get wired |
| Route handlers / controllers | Yes | These connect HTTP to business logic |
| Pure business logic | No | No external dependencies to invert |
| Simple utilities | No | formatDate() doesn't need dependency injection |
| Glue code / composition root | No | This is where you wire concretes — that's its job |
The Composition Root
Every application has one place where concrete implementations are wired together. This is the composition root — the entry point, the main function, the DI container setup.
Concrete instantiation belongs here, not in business logic:
// composition root — the ONE place that knows about concretes
const repo = new PostgresUserRepository(pool)
const emailer = new SmtpEmailService(config)
const handler = new CreateUserHandler(repo, emailer)
app.post('/api/users', (req, res) => handler.handle(req, res))If you find yourself writing new ConcreteService() outside the composition root, pause. You're wiring in the wrong place.
Relationship with TDD
This skill and TDD are complementary:
- TDD asks: "What test proves this works?"
- This skill asks: "Can this be tested in isolation?"
When you follow both:
- TDD says write the test first
- You try to write the test and realize you can't mock the dependency
- This skill says: that's because the function creates its dependency — restructure it to accept it
- Now the test is easy: pass a mock/stub, verify behavior
On mocking: TDD's guidance to prefer "real code over mocks" is about not mocking to paper over bad design. When the design is right — when dependencies are injected as abstractions — mocking is the clean, correct approach for unit tests. Integration tests still use real implementations.
Verification Checklist
Before marking implementation steps complete:
All modes:
- Service functions accept their collaborators as parameters
- No concrete I/O clients instantiated inside business logic
- Parameter types describe behavior, not implementation (where the language supports it)
Strict additionally:
- Every boundary function passes the three checkpoint questions
- Composition root is clearly identifiable — concretes wired in one place
- Unit tests use mocks/stubs for external collaborators; integration tests use real implementations
Common Resistance
| Thought | Response |
| "Passing dependencies everywhere is boilerplate" | It's explicit. Explicit dependencies are debuggable. Hidden ones aren't. |
| "This is over-engineering for a small project" | If you're writing tests, you need testable code. Size doesn't change that. |
| "I'll refactor to abstractions later" | Later never comes. The concrete dependency spreads through the codebase. |
| "My language doesn't have interfaces" | You don't need the keyword. Accept the shape, not the name. |
| "DI containers are complex" | You don't need a container. Constructor parameters are dependency injection. |
Integration
This skill is loaded by:
/virtual-team:implement— Layer 0 (behavioral discipline), alongside TDD/virtual-team:flow— inherited through/virtual-team:implement- SDD implementer subagents — loaded when executing plan tasks
The skill self-configures by reading stack.md. Consumers load it the same way regardless of mode.
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.

