第 6 章:设计与深模块
出处
来源:codebase-design · improve-codebase-architecture · 状态:已读
一句话
深模块是小接口后面坐着大量行为,接口就是测试面;架构 skill 是调查能加深的候选,不是泥球救援。 深度按调用者得到的杠杆衡量,不按实现有多少行。
核心概念
一套不准换词的词汇
codebase-design 是底下那层参考。用词必须精确,不要改口说 component、service、API、boundary——一致的语言就是全部意义。
| 词 | 是什么 | 不要说 |
|---|---|---|
| 模块 | 有接口和实现的任何东西,刻意不分尺度:函数、类、包、跨层的一刀 | unit、component、service |
| 接口 | 调用者用对它必须知道的一切:类型签名,加上不变量、顺序约束、错误模式、必配、性能 | API、signature(太窄,只指类型面) |
| 实现 | 模块里面的代码。和适配器不同:适配器说的是槽,实现说的是里面有什么 | |
| 深度 | 接口上的杠杆:调用者(或测试)每学一单位接口能行使多少行为 | 实现行数 / 接口行数 |
| 接缝 | 你可以改行为却不必改「那个地方」的位置;模块接口住在哪。接缝放哪是单独的设计决定 | boundary(和 DDD 的限界上下文撞车) |
| 适配器 | 在接缝上满足接口的具体物。说角色,不说内部 | |
| 杠杆 | 调用者从深度得到的:学一份接口,N 个调用点、M 个测试一起赚 | |
| 局部性 | 维护者从深度得到的:改、缺陷、知识、验证集中在一处 |
深 vs 浅
深:小接口 + 大量实现。浅:大接口 + 薄实现(透传)。设计接口时问三句:方法能不能更少、参数能不能更简单、复杂度能不能再藏进去。
几条原则:
- 深度是接口的性质,不是实现的性质。 深模块内部可以由小、可 mock、可换的零件组成;它们不是接口的一部分。模块可以有对内接缝(实现私有,给自己的测试用)和对外接缝(接口上的)。
- 删除测试。 想象删掉这个模块。复杂度消失了,它是透传。复杂度在 N 个调用者上重新出现,它才配得上存在。
- 接口就是测试面。 调用者和测试走同一条接缝。你想测穿接口,模块形状大概不对。
- 一个适配器是假想接缝;两个才是真接缝。 接缝对面没有真实变化,就不要引入接缝。
刻意拒绝的框架:
- Ousterhout 把深度当实现行数对比接口行数:奖励把实现写胖。这里用深度当杠杆
- 把「接口」当成 TypeScript 的
interface关键字或类的公开方法:太窄 - 「边界」:和 DDD 撞车,说接缝或接口
可测试性三条:依赖传入、不要内部 new;返回结果、不要靠副作用;表面积小——方法少则测试少,参数少则夹具简单。
接口形状还在问时,走 design-it-twice:并行子代理用几种差很远的办法设计接口,再按深度、局部性、接缝位置比。
调查,不是救援
/improve-codebase-architecture 把摩擦面出来,提议加深机会:把浅模块变成深模块。目标是可测试、对 AI 好找路。
先定范围再扫:YAGNI(You Aren't Gonna Need It,现在用不上的别做)。加深的回报是让以后改它更容易,所以权重放在最近在改的部分。用户点了方向就走那个,跳过推断;否则看提交历史找热点。没有热点再放大网。
先读 CONTEXT.md 和相关决策记录。然后派子代理有机地走,记下摩擦:理解一个概念要在许多小模块之间跳;模块浅,接口几乎和实现一样复杂;纯函数被抽出来只为了好测,真缺陷藏在调用方式里(没有局部性);紧耦合从接缝漏出去;哪部分没测,或经当前接口难测。对可疑的浅模块做删除测试:删掉是集中复杂度,还是只是挪走?「是,集中」才是你要的信号。
候选写成自包含 HTML,放到系统临时目录,不进仓库。每张卡片:文件、问题、方案、按局部性和杠杆写的收益、加深前后对照图、推荐强度(Strong / Worth exploring / Speculative)。文末给出首选。领域词走 CONTEXT.md,结构词走 codebase-design。和已有决策记录冲突的,只在摩擦大到值得重开时才出现,并标明。
先不要提议接口。 文件写好后问:这些里你想探哪一个?
你点了候选,才调 grilling 走决策树:约束、依赖、加深后的模块形状、接缝后面坐什么、哪些测试能留下。边走边调 domain-modeling:加深后的模块按词汇表里没有的概念命名,就加词;你用站得住的理由拒绝候选,才提议写成决策记录,免得以后调查再推荐同一件——「这会儿不值得」这种短暂理由不算。
Matt 建议隔几天跑一次。它是调查:真老的仓库能找到候选,不会替你拆泥球。点中的候选变成一个想法,从 /grill-with-docs 进主链。
关键洞察
- 「对 AI 好找路」和「对人好维护」在这里是同一个性质。 局部性让改动、缺陷、验证待在一处;agent 少跳文件,人也少跳文件。深模块不是为模型发明的,是模型让浅模块的利息变得付不起。
- 调查产出候选,不产出重构。 把 HTML 报告当成开工令,等于跳过追问。形状仍然要人拍。
- 一个适配器不够开接缝。 Agent 特别爱为「将来好测」先抽一层。这条规则把假想变化挡在接口外面,直到第二份真实适配器出现。
个人思考
对照第 1 章第 4 个失败:泥球是速度的利息。这一章给的不是「大重构项目」,是每隔几天的调查节奏,外加一套不准漂移的词。和 09-十条原则「制度比聪明重要」对得上——深度是制度(接缝、删除测试、两个适配器),不是一次灵感。下一章处理这套纪律在一会话装不下时怎么扩展:探路图和外来请求的分拣。