← 技能图谱 · DESIGN-IT-TWICE · DEEPENING
namecodebase-design
descriptionShared 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.
触发词用于设计深模块的共享词汇表。当用户想要设计或改进模块的接口、寻找加深机会、决定接缝位置、使代码更可测试或更易于 AI 导航,或其他技能需要深模块词汇时使用。

codebase-design

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.

Design deep modules: a lot of behaviour behind a small interface, placed at a clean seam, testable through that interface. Use this language and these principles wherever code is being designed or restructured. The aim is leverage for callers, locality for maintainers, and testability for everyone.

设计深模块:在小接口背后承载大量行为,放置在清晰的接缝处,通过该接口可测试。在任何设计或重构代码的地方使用这套语言和原则。目标是调用方的杠杆收益、维护方的局部性,以及所有人的可测试性。

Glossary

词汇表

Use these terms exactly — don't substitute "component," "service," "API," or "boundary." Consistent language is the whole point.

严格使用这些术语——不要替换为"组件"、"服务"、"API"或"边界"。统一的语言是整个要义所在。

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).

接口——调用方正确使用模块所需知道的一切:类型签名,还包括不变性、顺序约束、错误模式、所需配置和性能特征。避免:API、签名(过于狭窄——它们只指类型层面的表面)。

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.

实现——模块内部的东西,它的代码体。区别于适配器:一个东西可以是小适配器大实现(Postgres 仓库)或大适配器小实现(内存 fake)。当话题是接缝时用"适配器";否则用"实现"。

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).

接缝 (Michael Feathers)——可以在不编辑某处的情况下改变行为的地方;模块接口所在的位置。把接缝放在哪里本身就是一个设计决策,与接缝背后放什么不同。避免:边界(与 DDD 的限界上下文过载)。

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.

杠杆收益——调用方从深度中获得的好处:每学习一单位接口获得更多能力。一个实现在 N 个调用点和 M 个测试中回馈。

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
│                     │
└─────────────────────┘
┌─────────────────────┐
│   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
└─────────────────────────────────┘
┌─────────────────────────────────┐
│       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.
  • 删除测试。 想象删除这个模块。如果复杂性消失了,它只是一个透传。如果复杂性在 N 个调用方之间重新出现,那么它在赚取它的价值。
  • 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:

好的接口让测试变得自然:

  1. Accept dependencies, don't create them.
// Testable
function processOrder(order, paymentGateway) {}

// Hard to test
function processOrder(order) {
  const gateway = new StripeGateway();
}
  1. 接收依赖,不要创建依赖。
// 可测试
function processOrder(order, paymentGateway) {}

// 难测试
function processOrder(order) {
  const gateway = new StripeGateway();
}
  1. Return results, don't produce side effects.
// Testable
function calculateDiscount(cart): Discount {}

// Hard to test
function applyDiscount(cart): void {
  cart.total -= discount;
}
  1. 返回结果,不要产生副作用。
// 可测试
function calculateDiscount(cart): Discount {}

// 难测试
function applyDiscount(cart): void {
  cart.total -= discount;
}
  1. Small surface area. Fewer methods = fewer tests needed. Fewer params = simpler test setup.
  1. 小的表面积。 更少的方法 = 更少的测试需求。更少的参数 = 更简单的测试设置。

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.
  • 深度作为实现行数与接口行数的比率(Ousterhout):奖励填充实现。我们改用深度即杠杆收益。
  • "Interface" as the TypeScript interface keyword or a class's public methods: too narrow — interface here includes every fact a caller must know.
  • 将"接口"视为 TypeScript 的 interface 关键字或类的公开方法:太过狭窄——此处的接口包括调用方必须知道的每个事实。
  • "Boundary": overloaded with DDD's bounded context. Say seam or interface.
  • "边界":与 DDD 的限界上下文过载。用接缝接口

Going deeper

深入

  • Deepening a cluster given its dependencies — see DEEPENING.md: dependency categories, seam discipline, and replace-don't-layer testing.
  • 根据依赖关系加深集群——见 DEEPENING.md:依赖分类、接缝纪律,以及替换不堆叠的测试策略。
  • Exploring alternative interfaces — see 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.
  • 探索替代接口——见 DESIGN-IT-TWICE.md:启动并行子代理以几种截然不同的方式设计接口,然后根据深度、局部性和接缝位置进行比较。