规范优先开发
用 DDD 命名事物,用 BDD 规范它们,用最小变更满足它们,用证据证明它们。技术栈无关。
1. 编码前思考
- 陈述假设。如果请求不明确或有多种解读,请明确指出并询问。切勿默默选择。
- 如果存在更简单的方法,请说出来并推回。
- 阅读任务涉及的代码。列出变更必须触及的所有内容(调用方、测试、测试夹具、配置、导出),以及可能破坏的内容(数据被销毁或暴露、停止工作的调用方)。这就是作用域;额外功能不算。用一行列出跳过的额外功能。
- 在第一次写入前,从主分支创建一个名称与工作匹配的分支(例如
feat/<feature>、fix/<bug>)。只读工作不需要分支。
2. 建模领域 (DDD)
在编码前,用几行写下:
- 限界上下文及其通用语言:领域专家使用的术语。重用代码库中已有的术语;仅在需要时添加新术语。
- 聚合及其不变量:必须始终为真的条件。
- 代码、测试和消息中的名称使用这些术语。拒绝通用名称(
handle、process、manager、utils)。 - 领域逻辑对纯数据是纯函数。I/O(数据库、网络、时钟、随机性)位于边界处的端口后面,通过注入方式提供。
保存到 docs/specs/<feature>.md。
对于不添加任何领域概念的琐碎变更,跳过此步骤。
3. 规范行为 (BDD)
用领域语言编写 Given/When/Then 场景,每个行为一个,包括边界和失败情况。每个场景描述可观察的行为,而非实现。对不明显的场景征求用户点头确认;它们就是契约。保存到 docs/features/<feature>.feature。
4. 计划
对于多步骤工作,向 docs/plans/<feature>.md 写入一个简短计划:每个步骤包括其验证检查。当从现有规范继续时,仍然先编写计划。然后停下来请用户审查;在用户批准前绝不执行。选择第一个完全有效的选项:
- 是否需要存在?如果不需要,跳过。
- 是否已在此代码库中?像周围代码一样使用它。
- 标准库或平台特性?使用它。
- 已安装的依赖?使用它。永远不要为几行代码添加一个新依赖。
- 读者一眼能理解的一行代码?使用它。
- 否则,编写能工作的最小代码。
5. 执行
编排
当你要执行任务时,无论是否有规范或计划,请检查它们是否适合:几个可分离的项目(例如 3 个以上修复)、独立的步骤,或明显不同难度的步骤。如果是这样,请阅读 orchestration.md(此文件旁边)并在首次编辑前询问其两个选项(通过 AskUserQuestion):如果你写了计划,则与计划审查在同一轮中;否则立即询问。始终询问;没有其他批准(计划、规范、“好的”)算作选择。如果任务不适合,则内联执行。
红、绿、重构,每个场景
- 将一个场景转化为一个失败的测试。运行它;看到它因正确原因失败。
- 编写通过测试的最少代码。运行它。
- 仅重构此变更造成混乱的部分。
- 缺陷修复:先通过失败场景复现,grep 函数的每个调用方,在共享代码中一次修复根本原因。
6. 保持差异外科手术式
- 只触及必须触及的部分。匹配现有风格;不要改进相邻代码。每个变更行都源自请求。
- 只移除你创建的孤立代码;提及其他死代码,不要删除。
- 没有无人要求的抽象、包装、选项、配置或“为将来”的代码。删除优于添加。
- 移动的代码保留其错误处理和验证。
- 注释只解释“为什么”,一行即可。已知的限制写为
shortcut: <限制>, <何时升级>。 - 绝不削减:信任边界处的验证、防止数据丢失的错误处理、安全性、可访问性、用户要求的任何内容。
7. 验证后声明完成
运行测试以及任何构建或类型检查。读取输出。报告实际结果;如果某些内容失败或未运行,请说明。没有“应该可以工作”。
8. 回复
用一两行结束:你跳过或未检查的内容,以及用户必须知道的任何风险。