--- 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**: a lot of behaviour behind a small interface, placed at a clean seam, testable through that interface. Use these concepts when a real interface or responsibility seam needs design; retain the project's established names. The aim is leverage for callers, locality for maintainers, and testability for everyone. > Selectively vendored and adapted for Project Blueprints; see [project adaptations](../../references/engineering.md#curated-source-adaptations) and [provenance](../../provenance.json). ## Glossary These terms are a design lens, not a renaming mandate. Use the project's service, API and domain vocabulary where it is already precise. **Module**: anything with an interface and an implementation. Deliberately scale-agnostic: a function, class, package, or tier-spanning slice. **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. The type signature alone is narrower than the caller contract. **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. Distinguish this meaning from a DDD bounded context when needed. **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 + lots of implementation: ``` ┌─────────────────────┐ │ Small Interface │ ← Few methods, simple params ├─────────────────────┤ │ │ │ Deep Implementation│ ← Complex logic hidden │ │ └─────────────────────┘ ``` **Shallow module** = large interface + little implementation (avoid): ``` ┌─────────────────────────────────┐ │ Large Interface │ ← Many methods, complex params ├─────────────────────────────────┤ │ Thin Implementation │ ← Just passes through └─────────────────────────────────┘ ``` 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. - **Prefer observable interface tests.** Tests crossing the caller seam protect behavior. Internal tests can still be necessary for codecs, malformed input, cancellation, shutdown and other safety invariants; keep their coverage when reshaping a module. - **Justify the seam by a real responsibility or substitution need.** Production plus a fake can demonstrate variation, but no fixed adapter count is required; a single implementation may still need an I/O, lifecycle or safety boundary. ## Designing for testability Good interfaces make testing natural: 1. **Accept dependencies, don't create them.** ```typescript // Testable function processOrder(order, paymentGateway) {} // Hard to test function processOrder(order) { const gateway = new StripeGateway(); } ``` 2. **Separate pure decisions from necessary side effects.** Keep I/O in its existing owner with explicit lifecycle/error behavior. ```typescript // Testable function calculateDiscount(cart): Discount {} // Hard to test function applyDiscount(cart): void { cart.total -= discount; } ``` 3. **Small surface area.** Fewer methods and parameters can simplify setup; required behavior and safety coverage, not method count, determines tests. ## Relationships - A **Module** presents an **Interface** to its callers; distinct consumer surfaces may be justified by existing contracts. - **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. - **Ambiguous vocabulary**: distinguish a substitution seam from a bounded context without renaming established APIs or services. ## Going deeper - **Deepening a cluster given its dependencies**, see [DEEPENING.md](DEEPENING.md): dependency categories, seam discipline, and coverage-preserving test consolidation. - **Exploring alternative interfaces**, see [DESIGN-IT-TWICE.md](DESIGN-IT-TWICE.md): compare meaningfully different alternatives, using native sub-agents only when independent work merits them, then compare on depth, locality, and seam placement.