codebase-design
Imported from paulrberg/agent-skills/skills/codebase-design.
Install
npx skills add https://github.com/PaulRBerg/agent-skills/tree/main/skills/codebase-design
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install paulrberg-agent-skills@llmmart
git clone https://github.com/PaulRBerg/agent-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole paulrberg/agent-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Codebase Design
Design deep modules: substantial behaviour behind a small interface at a clean seam, testable through that interface. Use this vocabulary when it clarifies a design decision. The aim is leverage for callers, locality for maintainers, and testability.
Glossary
Use these terms consistently where their distinctions matter.
Module — anything with an interface and an implementation. Deliberately scale-agnostic: a function, class, package, or tier-spanning slice. Avoid: unit, component, service.
Interface — everything a caller must know to use the module correctly: the type signature, but also invariants, ordering constraints, error modes, required configuration, and performance characteristics. Avoid: API, signature (too narrow — they refer only to the type-level surface).
Implementation — what's inside a module, its body of code. Distinct from Adapter: a thing can be a small adapter with a large implementation (a Postgres repo) or a large adapter with a small implementation (an in-memory fake). Reach for "adapter" when the seam is the topic; "implementation" otherwise.
Depth — leverage at the interface: the amount of behaviour a caller (or test) can exercise per unit of interface they have to learn. A module is deep when a large amount of behaviour sits behind a small interface, shallow when the interface is nearly as complex as the implementation.
Seam (Michael Feathers) — a place where you can alter behaviour without editing in that place; the location at which a module's interface lives. Where to put the seam is its own design decision, distinct from what goes behind it. Avoid: boundary (overloaded with DDD's bounded context).
Adapter — a concrete thing that satisfies an interface at a seam. Describes role (what slot it fills), not substance (what's inside).
Leverage — what callers get from depth: more capability per unit of interface they learn. One implementation pays back across N call sites and M tests.
Locality — what maintainers get from depth: change, bugs, knowledge, and verification concentrate in one place rather than spreading across callers. Fix once, fixed everywhere.
Deep vs shallow
Deep module — small interface (few methods, simple params) hiding a lot of implementation.
Shallow module (avoid) — interface nearly as large as its thin, pass-through implementation.
When designing an interface, ask:
- Can I reduce the number of methods?
- Can I simplify the parameters?
- Can I hide more complexity inside?
Principles
- Depth is a property of the interface, not the implementation. A deep module can be internally composed of small, mockable, swappable parts — they just aren't part of the interface. A module can have internal seams (private to its implementation, used by its own tests) as well as the external seam at its interface.
- The deletion test. Imagine deleting the module. If complexity vanishes, it was a pass-through. If complexity reappears across N callers, it was earning its keep.
- The interface is the test surface. Callers and tests cross the same seam. If you want to test past the interface, the module is probably the wrong shape.
- One adapter means a hypothetical seam. Two adapters means a real one. Don't introduce a seam unless something actually varies across it.
Designing for testability
Good interfaces make testing natural:
- At a real seam, accept the dependency as a parameter instead of constructing it inside the module.
- Prefer returning results to side effects when that keeps the interface simpler.
- Keep the surface small: fewer methods and parameters mean fewer, simpler tests.
Relationships
- A Module has exactly one Interface (the surface it presents to callers and tests).
- Depth is a property of a Module, measured against its Interface.
- A Seam is where a Module's Interface lives.
- An Adapter sits at a Seam and satisfies the Interface.
- Depth produces Leverage for callers and Locality for maintainers.
Rejected framings
- Depth as ratio of implementation-lines to interface-lines (Ousterhout): rewards padding the implementation. We use depth-as-leverage instead.
- "Interface" as the TypeScript
interfacekeyword or a class's public methods: too narrow — interface here includes every fact a caller must know. - "Boundary": overloaded with DDD's bounded context. Say seam or interface.
Output Contract
When applying this vocabulary to a design or review, report the recommended module, interface, and seam; explain how the result improves depth, leverage, locality, or testability; and identify material tradeoffs or unresolved evidence. When this skill is only supporting another requested artifact, incorporate that analysis into the artifact instead of adding a separate report.
Going deeper
- Deepening a cluster given its dependencies — see references/DEEPENING.md: dependency categories, seam discipline, and replace-don't-layer testing.
- Exploring alternative interfaces — see references/DESIGN-IT-TWICE.md: spin up parallel sub-agents to design the interface several radically different ways, then compare on depth, locality, and seam placement.
Forked from mattpocock/skills.
Files (agent-skills)
-
agents
-
openai.yaml 144 B
interface: display_name: "Codebase Design" short_description: "Vocabulary for deep-module design" policy: allow_implicit_invocation: true
-
-
references
-
DEEPENING.md 2.9 KB
# Deepening How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in [SKILL.md](../SKILL.md) — **module**, **interface**, **seam**, **adapter**. ## Dependency categories When assessing a candidate for deepening, classify its dependencies. The category determines how the deepened module is tested across its seam. ### 1. In-process Pure computation, in-memory state, no I/O. A strong deepening candidate when merging reduces total complexity; test through the new interface directly. No adapter is needed. ### 2. Local-substitutable Dependencies that have local test stand-ins (PGLite for Postgres, in-memory filesystem). Deepenable if the stand-in exists. The deepened module is tested with the stand-in running in the test suite. The seam is internal; no port at the module's external interface. ### 3. Remote but owned (Ports & Adapters) Your own services across a network boundary (microservices, internal APIs). Define a **port** (interface) at the seam. The deep module owns the logic; the transport is injected as an **adapter**. Tests use an in-memory adapter. Production uses an HTTP/gRPC/queue adapter. Recommendation shape: _"Define a port at the seam, implement an HTTP adapter for production and an in-memory adapter for testing, so the logic sits in one deep module even though it's deployed across a network."_ ### 4. True external (Mock) Third-party services (Stripe, Twilio, etc.) you don't control. The deepened module takes the external dependency as an injected port; tests provide a mock adapter. ## Seam discipline - Don't introduce a port unless at least two adapters are justified (typically production + test). A single-adapter seam is just indirection. - Don't expose internal seams through the interface just because tests use them. ## Testing strategy: replace, don't layer - Old unit tests on shallow modules become waste once tests at the deepened module's interface exist — delete them. - Write new tests at the deepened module's interface. The **interface is the test surface**. - Tests assert on observable outcomes through the interface, not internal state. - Tests should survive internal refactors — they describe behaviour, not implementation. If a test has to change when the implementation changes, it's testing past the interface. ## Finding deepening candidates - Weight recently changed hot spots first: walk `git log --oneline` for recurring files and areas. Deepening pays off where change concentrates. - Look for friction signals: - Understanding one concept requires bouncing between many small modules. - Interfaces are nearly as complex as their implementations. - Pure functions were extracted for testability while the real bugs hide in how they are called; locality is missing. - Code is untestable through its current interface. - Apply the deletion test to each suspect: "Would deleting it concentrate complexity, or just move it?" Treat "concentrates" as the signal to deepen. -
DESIGN-IT-TWICE.md 2.7 KB
# Design It Twice When the user wants to explore alternative interfaces for a chosen deepening candidate, use this parallel sub-agent pattern. Based on "Design It Twice" (Ousterhout) — your first idea is unlikely to be the best. Uses the vocabulary in [SKILL.md](../SKILL.md) — **module**, **interface**, **seam**, **adapter**, **leverage**. ## Process ### 1. Frame the problem space Before spawning sub-agents, write a user-facing explanation of the problem space for the chosen candidate: - The constraints any new interface would need to satisfy - The dependencies it would rely on, and which category they fall into (see [DEEPENING.md](DEEPENING.md)) - A rough illustrative code sketch to ground the constraints — not a proposal, just a way to make the constraints concrete Show this to the user, then immediately proceed to Step 2. The user reads and thinks while the sub-agents work in parallel. ### 2. Spawn sub-agents Spawn 3+ sub-agents in parallel. Each must produce a **radically different** interface for the deepened module. Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from [DEEPENING.md](DEEPENING.md), what sits behind the seam). The brief is independent of the user-facing problem-space explanation in Step 1. Give each agent a different design constraint: - Agent 1: "Minimize the interface — aim for 1–3 entry points max. Maximise leverage per entry point." - Agent 2: "Maximise flexibility — support many use cases and extension." - Agent 3: "Optimise for the most common caller — make the default case trivial." - Agent 4 (if applicable): "Design around ports & adapters for cross-seam dependencies." Include both [SKILL.md](../SKILL.md) vocabulary and the target project's own domain vocabulary — from its `AGENTS.md`, glossary, or docs — so each sub-agent names things consistently with the architecture language and the project's domain language. Each sub-agent outputs: 1. Interface (types, methods, params — plus invariants, ordering, error modes) 2. Usage example showing how callers use it 3. What the implementation hides behind the seam 4. Dependency strategy and adapters (see [DEEPENING.md](DEEPENING.md)) 5. Trade-offs — where leverage is high, where it's thin ### 3. Present and compare Present designs sequentially so the user can absorb each one, then compare them in prose. Contrast by **depth** (leverage at the interface), **locality** (where change concentrates), and **seam placement**. After comparing, give your own recommendation: which design you think is strongest and why. If elements from different designs would combine well, propose a hybrid. Be opinionated — the user wants a strong read, not a menu.
-
-
SKILL.md 5.7 KB
--- name: codebase-design description: Shared vocabulary for designing deep modules. Use when the user wants to design or improve a module's interface, find deepening opportunities, decide where a seam goes, make code more testable or AI-navigable, or when another skill needs the deep-module vocabulary. --- # Codebase Design Design **deep modules**: substantial behaviour behind a small interface at a clean seam, testable through that interface. Use this vocabulary when it clarifies a design decision. The aim is leverage for callers, locality for maintainers, and testability. ## Glossary Use these terms consistently where their distinctions matter. **Module** — anything with an interface and an implementation. Deliberately scale-agnostic: a function, class, package, or tier-spanning slice. _Avoid_: unit, component, service. **Interface** — everything a caller must know to use the module correctly: the type signature, but also invariants, ordering constraints, error modes, required configuration, and performance characteristics. _Avoid_: API, signature (too narrow — they refer only to the type-level surface). **Implementation** — what's inside a module, its body of code. Distinct from **Adapter**: a thing can be a small adapter with a large implementation (a Postgres repo) or a large adapter with a small implementation (an in-memory fake). Reach for "adapter" when the seam is the topic; "implementation" otherwise. **Depth** — leverage at the interface: the amount of behaviour a caller (or test) can exercise per unit of interface they have to learn. A module is **deep** when a large amount of behaviour sits behind a small interface, **shallow** when the interface is nearly as complex as the implementation. **Seam** _(Michael Feathers)_ — a place where you can alter behaviour without editing in that place; the _location_ at which a module's interface lives. Where to put the seam is its own design decision, distinct from what goes behind it. _Avoid_: boundary (overloaded with DDD's bounded context). **Adapter** — a concrete thing that satisfies an interface at a seam. Describes _role_ (what slot it fills), not substance (what's inside). **Leverage** — what callers get from depth: more capability per unit of interface they learn. One implementation pays back across N call sites and M tests. **Locality** — what maintainers get from depth: change, bugs, knowledge, and verification concentrate in one place rather than spreading across callers. Fix once, fixed everywhere. ## Deep vs shallow **Deep module** — small interface (few methods, simple params) hiding a lot of implementation. **Shallow module** (avoid) — interface nearly as large as its thin, pass-through implementation. When designing an interface, ask: - Can I reduce the number of methods? - Can I simplify the parameters? - Can I hide more complexity inside? ## Principles - **Depth is a property of the interface, not the implementation.** A deep module can be internally composed of small, mockable, swappable parts — they just aren't part of the interface. A module can have **internal seams** (private to its implementation, used by its own tests) as well as the **external seam** at its interface. - **The deletion test.** Imagine deleting the module. If complexity vanishes, it was a pass-through. If complexity reappears across N callers, it was earning its keep. - **The interface is the test surface.** Callers and tests cross the same seam. If you want to test _past_ the interface, the module is probably the wrong shape. - **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a seam unless something actually varies across it. ## Designing for testability Good interfaces make testing natural: - At a real seam, accept the dependency as a parameter instead of constructing it inside the module. - Prefer returning results to side effects when that keeps the interface simpler. - Keep the surface small: fewer methods and parameters mean fewer, simpler tests. ## Relationships - A **Module** has exactly one **Interface** (the surface it presents to callers and tests). - **Depth** is a property of a **Module**, measured against its **Interface**. - A **Seam** is where a **Module**'s **Interface** lives. - An **Adapter** sits at a **Seam** and satisfies the **Interface**. - **Depth** produces **Leverage** for callers and **Locality** for maintainers. ## Rejected framings - **Depth as ratio of implementation-lines to interface-lines** (Ousterhout): rewards padding the implementation. We use depth-as-leverage instead. - **"Interface" as the TypeScript `interface` keyword or a class's public methods**: too narrow — interface here includes every fact a caller must know. - **"Boundary"**: overloaded with DDD's bounded context. Say **seam** or **interface**. ## Output Contract When applying this vocabulary to a design or review, report the recommended module, interface, and seam; explain how the result improves depth, leverage, locality, or testability; and identify material tradeoffs or unresolved evidence. When this skill is only supporting another requested artifact, incorporate that analysis into the artifact instead of adding a separate report. ## Going deeper - **Deepening a cluster given its dependencies** — see [references/DEEPENING.md](references/DEEPENING.md): dependency categories, seam discipline, and replace-don't-layer testing. - **Exploring alternative interfaces** — see [references/DESIGN-IT-TWICE.md](references/DESIGN-IT-TWICE.md): spin up parallel sub-agents to design the interface several radically different ways, then compare on depth, locality, and seam placement. Forked from [mattpocock/skills](https://github.com/mattpocock/skills/tree/main/skills/engineering/codebase-design).
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.