← All skills
Rewrite · 5 sources citedProtocolSoftware & games

codebase-design

Design deep modules - a lot of behaviour behind a small interface, placed at a clean seam and tested through that interface. Gives a precise shared vocabulary (module, interface, depth, seam, adapter, leverage, locality), the deletion test, rules for when a seam is real, testability patterns, how to deepen a cluster of shallow modules given what it depends on, and how to compare several different interfaces before choosing.

Use it when
Designing or reshaping a module's interface, deciding where a seam or boundary goes, making code easier to test or navigate, or when the user says "deep module", "shallow wrapper", "pass-through layer", "where should this boundary go" or "design it twice". Not for hunting refactor targets across a whole codebase (see improve-codebase-architecture), reviewing a diff, or choosing infrastructure.
What kind it is
A written method: it lays out how the work is done and what to check along the way.
Size
1,242 words. The whole text loads when the skill fires; only the 127-word description is always loaded.
Starts with
Ask for the job it describes.

How it is organised

What’s inside

The skill’s top-level sections, in order. The bar is each section’s share of the 1,242 words, so you can see where the weight sits before reading.

  1. 1Vocabulary334 words
  2. 2Principles184 words
  3. 3Designing for testability88 words
  4. 4Design checks155 words
  5. 5Deepening a cluster of shallow modules189 words
  6. 6Design it twice132 words
  7. 7Output69 words

The skill itself

Read it before you install it

codebase-design/SKILL.md · 1,242 wordsDownloadSkill on GitHub ↗
---
name: codebase-design
description: Design deep modules - a lot of behaviour behind a small interface, placed at a clean seam and tested through that interface. Gives a precise shared vocabulary (module, interface, depth, seam, adapter, leverage, locality), the deletion test, rules for when a seam is real, testability patterns, how to deepen a cluster of shallow modules given what it depends on, and how to compare several different interfaces before choosing. Use when designing or reshaping a module's interface, deciding where a seam or boundary goes, making code easier to test or navigate, or when the user says "deep module", "shallow wrapper", "pass-through layer", "where should this boundary go" or "design it twice". Not for hunting refactor targets across a whole codebase (see improve-codebase-architecture), reviewing a diff, or choosing infrastructure.
license: MIT
---

# Codebase design: deep modules

Design **deep modules**: a lot of behaviour behind a small interface, placed at a clean seam, testable through that interface. The payoff is **leverage** for callers, **locality** for maintainers, and testability for everyone.

This skill is advice and design work. It does not change code unless the user asks for the change. Looking at a codebase is not permission to restructure it.

## Vocabulary

Use these words precisely. Consistent language is the point, so do not drift into "component", "service", "API" or "boundary" when the question is about these things. Keep the project's own names for its domain concepts; this vocabulary describes the shape of the code, not what the code is about.

| Term | Meaning | Avoid |
|---|---|---|
| **Module** | Anything with an interface and an implementation, at any scale: a function, class, package, or a slice across tiers | unit, component, service |
| **Interface** | Everything a caller must know to use the module correctly: the type signature, plus invariants, ordering rules, error modes, required configuration and performance behavior | API, signature (both name only the type-level surface) |
| **Implementation** | What is inside the module | |
| **Depth** | Leverage at the interface: how much behaviour a caller (or a test) can exercise per unit of interface they must learn. Deep: much behind a small interface. Shallow: the interface is nearly as complex as the implementation | |
| **Seam** | A place where behaviour can change without editing that place; where a module's interface lives. Where to put it is a design decision separate from what goes behind it | boundary (collides with the domain-driven-design "bounded context") |
| **Adapter** | A concrete thing that satisfies an interface at a seam. It names a role, not a size: a thin adapter can wrap a large implementation, and a large adapter can sit on a small one | |
| **Leverage** | What callers get from depth: one implementation pays back across many call sites and tests | |
| **Locality** | What maintainers get from depth: change, bugs, knowledge and verification concentrate in one place instead of spreading across callers | |

The deep-module idea is John Ousterhout's and the seam is Michael Feathers's. Depth is measured here as leverage, not as a ratio of implementation lines to interface lines, because a line ratio rewards padding the implementation.

## Principles

- **Depth belongs to the interface, not the implementation.** A deep module may be built from small, swappable parts inside. They are not part of its interface. A module can have internal seams, private to the implementation and used by its own tests, as well as the external seam callers use.
- **The deletion test.** Imagine deleting the module. If the complexity vanishes, it was a pass-through. If the complexity reappears across many 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 mean a real one.** Add a seam only when something actually varies across it. Typically that is the production implementation and a stand-in for tests. A single implementation can still justify a seam when it fronts an unavailable, slow or costly external dependency, but justify it from observed need, not from habit. Do not invent interchangeable adapters, or widen a production interface, only to satisfy an internal mock.

## Designing for testability

Pseudocode, adapt to the project's language.

1. **Accept dependencies; do not construct them.**
   ```
   process_order(order, payment_gateway)      # testable: the dependency is passed in
   process_order(order): g = new Gateway()    # hard to test: it builds its own
   ```
2. **Return results rather than producing side effects** where the caller can apply them.
   ```
   calculate_discount(cart) -> Discount       # testable: returns a value
   apply_discount(cart): cart.total -= d      # hard to test: mutates in place
   ```
3. **Keep the surface small.** Fewer entry points mean fewer tests; fewer parameters mean simpler setup.

## Design checks

For any interface you are designing or reshaping:

1. Write down the caller-visible behavior and the constraints it must meet.
2. Find the current seam and list what leaks across it: ordering rules, error handling, configuration, policy each caller repeats.
3. Apply the deletion test to the module and to any wrapper around it.
4. Reduce what callers must carry: fewer methods, simpler parameters, less ordering knowledge, less configuration.
5. Hide policy and orchestration behind the interface while keeping behavior observable. A small interface should hide real complexity, not hide guarantees callers need.
6. Check that tests and callers cross the same seam.
7. Add an adapter only where something truly varies.
8. When the decision is consequential, compare at least two materially different interfaces (below).
9. Test through the interface where behavior is observable, and prefer the smallest cohesive design that meets current requirements. Preserve earlier decisions unless there is evidence to reopen them.

## Deepening a cluster of shallow modules

When several small modules are really one concept, merge them behind one interface. What you do at the seam depends on what the cluster depends on:

| Dependency | Treatment |
|---|---|
| **In-process** (pure logic, in-memory state) | Merge into the deep module. No seam needed; test through its interface directly. |
| **Local stand-in available** (a database or filesystem that can be run locally or in memory) | Test with the stand-in. Keep it as an internal seam if the module needs one. |
| **Remote but owned** (another service your team controls) | Define the interface at the seam, with a production adapter and an in-memory adapter. This is the two-adapter, real seam. |
| **True external** (a third-party service) | Hide it behind an adapter; fake it in tests at that seam. |

Replace, do not layer, the tests. When shallow modules merge, their old per-module tests mostly describe the removed internals. Cover the same behavior through the new interface, confirm it passes, and then propose removing the redundant old tests, naming each. Remove them only with the user's agreement; tests are the user's work.

## Design it twice

The first interface that comes to mind is rarely the best one. For a consequential interface, produce at least two designs that are radically different, each under a different constraint: for instance, the fewest possible entry points; the most flexibility for varied callers; optimized for the single most common caller; or a ports-and-adapters shape around the dependencies. Make each design independent, with its own sketch of a caller using it. If you can delegate to parallel workers, give each one constraint, and keep them from seeing each other's output. Otherwise write the designs one after another without building on the earlier ones.

Then compare them on depth, locality and seam placement, and on what each does to the tests. Recommend one, or a deliberate hybrid, and say what you would give up.

## Output

```
Module:
Current interface and seam:
Leaked complexity:
Proposed interface:
Hidden implementation:
Adapters:
Depth / leverage / locality:
Test surface:
Compatibility and migration:
Open decisions for the user:
```

Leave decisions that change scope, public contracts or persistence to the user. If a design choice is hard to reverse, surprising and a real trade-off, suggest recording it as a decision record, and write it only if the user agrees.

The second audit

What we found in the sources

It keeps the vocabulary and principles of the version the others copy (mattpocock/skills codebase-design) and folds in the companion material that version only pointed to: how to treat dependencies when deepening, and how to compare several interfaces. The improve-codebase-architecture skills in the same bundle are a different job (surveying an existing codebase for refactor candidates) and are written up as their own skill. Every source below was read at the commit the auditor recorded. Nothing was taken from a source without a permissive license.

What each source contributed

  • mattpocock/skills ↗ · Primary basis · MIT

    The eight-term vocabulary (module, interface, implementation, depth, seam, adapter, leverage, locality) with the terms to avoid and the reasons; deep versus shallow; the four principles (depth belongs to the interface, the deletion test, the interface is the test surface, one adapter is a hypothetical seam and two make a real one); the two testability patterns (accept dependencies, return results) and a small surface; and the rejected framings (depth as a line-count ratio, 'interface' meaning only a language keyword, 'boundary' colliding with bounded context).

  • dougborg/harness-kit ↗ · Added ideas · Apache-2.0

    Attributing the deep-module idea to Ousterhout and the seam to Feathers; the four dependency categories named in its pointer to DEEPENING.md (in-process, local stand-in, remote but owned, true external); 'typically production and test' as the two adapters; replacing old tests rather than layering new ones; and designing alternative interfaces each under a different constraint.

  • mascah/skills ↗ · Added ideas · MIT

    Prefer the project's own vocabulary and use these terms to explain design; a single implementation can still justify a seam for an unavailable or costly external dependency, judged from observed need; do not invent interchangeable adapters or widen a production interface only to satisfy an internal mock; compare alternatives with caller examples, error handling, constraints, test surface and migration cost; choose the smallest cohesive design; investigation alone does not authorize an unsolicited change.

  • xu91102/claude-everything-Workflow ↗ · Added ideas · MIT

    The ordered design checks (define caller-visible behavior, find the current seam and what leaks, deletion test, reduce what callers carry, hide policy behind the interface, one test surface, adapter only if something varies, compare at least two interfaces when consequential), the output template, and 'do not implement or persist an architectural decision unless authorized'.

  • rengwu/wayfinder-maps ↗ · Added ideas · MIT

    Pseudocode for the testability examples instead of one language, 'entry points' instead of 'methods', and design-it-twice worded to work with or without parallel workers.

What changed

  • The description now lists the situations and phrases that trigger it (deep module, shallow wrapper, pass-through layer, where the boundary goes, design it twice) and has a not-for clause. The upstream description ends 'or when another skill needs the deep-module vocabulary', which presumes other skills.
  • The links to DEEPENING.md and DESIGN-IT-TWICE.md are replaced by two sections written for this skill. Those files were not in the bundle and were not read. What is known of them comes from one-line pointers in mattpocock/skills, dougborg/harness-kit and the others. So the treatment for each dependency category, and the example constraints for design-it-twice, are new wording and are untested.
  • Vocabulary moved into one table; the ASCII box diagrams, the Relationships list and the TypeScript snippets are replaced by the table, the principles and language-neutral pseudocode.
  • Added 'This skill is advice; it does not change code unless asked' and 'tests are the user's work': removing redundant old tests after a merge is proposed, with each one named, and done only with the user's agreement.
  • Adopted mascah's softening of the one-adapter rule so that the rule is not applied mechanically: a single implementation can justify a seam when it fronts a costly external dependency.
  • Kept the project's own domain names and used the strict vocabulary for the shape of the code only (the sources differ on this: mattpocock insists on exact terms, mascah prefers project vocabulary).

Problems found in the sources

  • mattpocock/skills: Appears to be the origin of the codebase-design family: GitHub's history for skills/engineering/codebase-design/SKILL.md lists its first commit on 2026-05-31, and xu91102/claude-everything-Workflow states 'Origin: mattpocock/skills@2ab9580' (a mattpocock/skills commit, dated 2026-07-28). The pinned file links to DEEPENING.md and DESIGN-IT-TWICE.md, which are not in the bundle (they exist in the upstream folder, which was listed but whose files were not read). Its description ends with 'or when another skill needs the deep-module vocabulary'. Its code examples are TypeScript. It requires exact use of its terms, including avoiding 'API'; whether that is helpful on a codebase with its own vocabulary is unclear.
  • dougborg/harness-kit: A rewrite of the same content in its own words. Links to DEEPENING.md and DESIGN-IT-TWICE.md, not in the bundle. It is the only source that attributes the deep-module idea to Ousterhout in the body and explains why depth is measured as leverage. Its description lists triggering situations.
  • mascah/skills: Short and cautious. Points to references/discipline.md and an optional 'shaping' helper, neither of which is in the bundle. Its description is one sentence and does not use the vocabulary words a person would type.
  • xu91102/claude-everything-Workflow: Condensed, with a design-checks list and an output template. Points to references/deepening.md and references/design-it-twice.md, and sends unresolved decisions to skills/using-superpowers/SKILL.md; none are in the bundle. Header says it is adapted 'to this repository's routing and safety gates'.
  • rengwu/wayfinder-maps: Lives under skills/.optional/. Host-neutral variant: pseudocode and a sentence allowing sequential designs where parallel workers are unavailable. Still links to DEEPENING.md (unchanged line) and DESIGN-IT-TWICE.md, not in the bundle.

What was dropped

  • The Relationships list, the ASCII box diagrams, and the TypeScript code blocks.
  • xu91102's pointer back to a central router skill, and its references/ paths.
  • pandastack-style frontmatter fields and the 'Native delta' line.
Read but not used (14)
  • helderberto/agent-skills: skills/codebase-design/SKILL.md is the mattpocock text with the Relationships section removed, an effort frontmatter field added, and the companion links moved to a references/ folder that is not in the bundle.
  • panda850819/pandastack: skills/engineering/codebase-design/SKILL.md is a condensed version tracking the mattpocock text closely (glossary, principles, rejected framings), without saying where it came from. Frontmatter has host-specific keys (reads, writes, domain, classification, user-invocable); two lines read 'Native delta' and 'Done when'; the description's not-for clause names skills called review and grill. Nothing was taken.
  • MokeyBytes/capstan: skills/codebase-design/SKILL.md has the same SHA-256 as CapitalCantrip/quality-gates. Compared with the pinned mattpocock file, a diff shows only punctuation changes (dashes to colons, a few colons to periods). Links to DEEPENING.md and DESIGN-IT-TWICE.md, not in the bundle.
  • CapitalCantrip/quality-gates: .claude/skills/codebase-design/SKILL.md has the same SHA-256 as MokeyBytes/capstan: the mattpocock text with dashes reworded.
  • the-inconvenience-store/skills: skills/codebase-design/SKILL.md has the same SHA-256 as the pinned mattpocock file: a byte-identical copy.
  • RollRollRoll/agent-toolkit: skills/development/codebase-design/SKILL.md is a Chinese translation of the mattpocock text. It was read and checked: it follows the same sections in the same order, including the Relationships and Rejected framings sections. It adds one clause saying a one-adapter seam is only an extra layer of indirection. Links to references/deepening.md and references/design-it-twice.md, not in the bundle.
  • mattpocock/skills: skills/engineering/improve-codebase-architecture/SKILL.md: the pinned improve file; primary basis of the improve-codebase-architecture skill.
  • rengwu/wayfinder-maps: skills/.optional/improve-codebase-architecture/SKILL.md: a host-neutral variant of the improve file; used in the improve-codebase-architecture skill.
  • xu91102/claude-everything-Workflow: skills/improve-codebase-architecture/SKILL.md: a short read-only audit variant; used in the improve-codebase-architecture skill.
  • CapitalCantrip/quality-gates: .claude/skills/improve-codebase-architecture/SKILL.md: the mattpocock improve text with GLOSSARY.md, reworded dashes and 'Call the Skill tool with' wording; belongs to the improve job, not used.
  • marinvch/Cortex: skills/improve-codebase-architecture/SKILL.md: the mattpocock improve text with project-specific wiki links and an added closing section; belongs to the improve job, not used.
  • the-inconvenience-store/skills: skills/improve-codebase-architecture/SKILL.md: same SHA-256 as the pinned mattpocock improve file: a byte-identical copy.
  • mascah/skills: skills/improve-codebase-architecture/SKILL.md: a short evidence-first variant; used in the improve-codebase-architecture skill.
  • RollRollRoll/agent-toolkit: skills/development/improve-codebase-architecture/SKILL.md: a Chinese translation of the mattpocock improve text, checked section by section; belongs to the improve job, not used.

Same text, wherever you work

Use it where you work

Copy the folder into your skills directory, then ask for the job or type its start command.

git clone https://github.com/nikbearbrown/skills
cp -R skills/skills/codebase-design ~/.claude/skills/

Pairs with