deepen — 让模块变深
行为神圣不可变:改动前后测试都必须全绿。每一次改动都缩小一个接口或隐藏一个决策——是加深,不是折腾。
深模块用很小的接口隐藏大量内容;浅模块的接口使用成本几乎等于自己写代码。复杂度 = 依赖 + 晦涩,并且会复利式累积。deepen 用剩余用量在复杂度变成 §B 之前把它还掉。在构建全绿且有余量可消耗时运行——不要在截止日期前运行。
何时加深
构建全绿、测试通过,且还有多余的 token 预算。
某个模块的接口感觉和它的实现一样复杂(浅模块气味)。
同一个改动反复触及许多文件(变更放大)。
用户明确要求提升设计质量。
⊥ 在功能开发中途或压力下运行。deepen 是深思熟虑的一遍,不是条件反射。
五个步骤
- 挑出最浅的
扫描规格所涉及的模块。按“浅度”排序——接口表面与所做工作之比。挑出最严重的一个。迹象包括:
只转发给另一个方法的直通方法(浅层)。
调用方必须正确设置 5 个标志才能使用(配置泄漏)。
同一抽象在两个层重复出现(没有信息隐藏)。
某个 ? 或 §B 可追溯到令人困惑的接口。
每遍只处理一个模块。加深是外科手术,⊥ 不是代码库大扫除。
- 诊断
用最直白的话指出设计缺陷,并引用 file:line:
src/auth/token.go: 6 参数构造函数把轮换策略泄漏给每个调用方。浅。 只有当复杂度真实显现时才算数:变更放大、高认知负荷,或未知的未知(调用方必须知道某个隐藏事实才能正确调用)。
- 研究加深方案
更深版本长什么样?拉取一个已知模式(外部情况交给 research → §R),或从代码库自身更好的模块中推导。加深的动作包括:
把复杂度往下拉——把难的部分藏在内部,给调用方简单路径。
把错误定义到不存在——设计接口使边界情况无法发生。
信息隐藏——一个决策,一个模块;调用方无需了解它。
用通用接口取代一堆特例方法。
- 提议
把改动起草为规格编辑,而不是静默重写:
模块的新/更简单的 §I 形态。
锁定加深后不变量的 §V,使未来构建无法再把它变浅。
§T 重构行,每行引用它所服务的 §V/§I。 交给 spec 去写。展示改动前/后的接口,让用户看到缩小。
- 验证行为保持
重构 ≠ 重写。开始前和结束后完整测试套件都必须全绿。一次改变行为的加深是伪装的功能——停下,走 /spec + /build。新接口要有一个测试证明旧调用方仍然可用。
何时停止
当所选模块的接口严格变小、其隐藏决策不再泄漏、测试全绿,且 §I/§V 记录了新形态时,即完成。加深一个模块胜过折腾五个。还有预算 → 挑下一个最浅的,开始新的一遍。
边界
⊥ 改变行为。前绿后绿。纯结构。
⊥ 写 SPEC.md。提议 §I/§V/§T;由 spec 来写。
⊥ 每遍加深超过一个模块。
⊥ 在截止日期前或功能开发中途运行。这是剩余预算的一遍。
⊥ 为一次性代码添加抽象。深模块配得上它的隐藏;投机性的抽象只是更多表面。