增量交付变更。适用于实现任何涉及多个文件的特性或变更。当你即将一次性编写大量代码,或某个任务感觉太大而无法一步完成时使用。
增量实现
概述
以薄垂直切片的方式构建——实现一小块,测试它,验证它,然后扩展。避免一次性实现整个特性。每个增量应使系统处于可工作、可测试的状态。这是使大型特性可管理的执行纪律。
何时使用
- 实现任何多文件变更
- 根据任务分解构建新特性
- 重构现有代码
- 任何你想在测试前编写超过约100行代码的时候
何时不使用: 单文件、单函数变更,且范围已经很小。
增量循环
┌──────────────────────────────────────┐
│ │
│ 实现 ──→ 测试 ──→ 验证 ──┐ │
│ ▲ │ │
│ └───── 提交 ◄─────────────┘ │
│ │ │
│ ▼ │
│ 下一个切片 │
│ │
└──────────────────────────────────────┘
对于每个切片:
- 实现 最小的完整功能块
- 测试 —— 运行测试套件(如果没有测试则编写一个)
- 验证 —— 确认切片按预期工作(测试通过、构建成功、手动检查)
- 提交 —— 用描述性消息保存进度(参见
git-workflow-and-versioning了解原子提交指南) - 进入下一个切片 —— 继续前进,不要重新开始
切片策略
垂直切片(首选)
构建一条贯穿整个栈的完整路径:
切片 1: 创建任务(数据库 + API + 基本 UI)
→ 测试通过,用户可以通过 UI 创建任务
切片 2: 列出任务(查询 + API + UI)
→ 测试通过,用户可以看到他们的任务
切片 3: 编辑任务(更新 + API + UI)
→ 测试通过,用户可以修改任务
切片 4: 删除任务(删除 + API + UI + 确认)
→ 测试通过,完整的 CRUD 完成
每个切片交付可工作的端到端功能。
契约优先切片
当后端和前端需要并行开发时:
切片 0: 定义 API 契约(类型、接口、OpenAPI 规范)
切片 1a: 根据契约实现后端 + API 测试
切片 1b: 根据匹配契约的模拟数据实现前端
切片 2: 集成并端到端测试
风险优先切片
先处理风险最高或最不确定的部分:
切片 1: 证明 WebSocket 连接有效(最高风险)
切片 2: 在已证明的连接上构建实时任务更新
切片 3: 添加离线支持和重连
如果切片 1 失败,你会在投入切片 2 和 3 之前发现它。
实现规则
规则 0:简单优先
在编写任何代码之前,问:“最简单可行的方法是什么?”
编写代码后,对照以下检查点进行审查:
- 能否用更少的行数完成?
- 这些抽象是否值得其复杂性?
- 高级工程师看到这个会不会说“你为什么不直接...”?
- 我是在为假设的未来需求构建,还是为当前任务?
简单性检查:
✗ 为单个通知使用带有中间件管道的通用 EventBus
✓ 简单的函数调用
✗ 为两个相似组件使用抽象工厂模式
✓ 两个直接的组件加上共享工具函数
✗ 为三个表单使用配置驱动的表单构建器
✓ 三个表单组件
三行相似的代码胜过过早的抽象。先实现朴素、明显正确的版本。只有在通过测试证明正确性后才进行优化。
规则 0.5:范围纪律
只触及任务所需的内容。
不要:
- “清理”你变更旁边的代码
- 重构你未修改文件中的导入
- 删除你不完全理解的注释
- 添加规范中没有的特性,因为它们“看起来有用”
- 在你只读取的文件中现代化语法
如果你注意到任务范围之外值得改进的地方,记下来——不要修复它:
注意到但未触及:
- src/utils/format.ts 有一个未使用的导入(与此任务无关)
- auth 中间件可以使用更好的错误消息(单独的任务)
→ 需要我为这些创建任务吗?
规则 1:一次只做一件事
每个增量只改变一个逻辑上的东西。不要混合关注点:
糟糕: 一个提交同时添加新组件、重构现有组件并更新构建配置。
良好: 三个独立的提交——每个变更一个。
规则 2:保持可编译
每个增量之后,项目必须能构建,且现有测试必须通过。不要在切片之间让代码库处于损坏状态。
规则 3:不完整特性的特性开关
如果某个特性尚未准备好给用户使用,但你需要合并增量:
// 进行中工作的特性开关
const ENABLE_TASK_SHARING = process.env.FEATURE_TASK_SHARING === 'true';
if (ENABLE_TASK_SHARING) {
// 新的共享 UI
}
这允许你将小增量合并到主分支,而不会暴露未完成的工作。
规则 4:安全默认值
新代码应默认采用安全、保守的行为:
// 安全:默认禁用,选择加入
export function createTask(data: TaskInput, options?: { notify?: boolean }) {
const shouldNotify = options?.notify ?? false;
// ...
}
规则 5:易于回滚
每个增量应可独立回退:
- 添加性变更(新文件、新函数)易于回退
- 对现有代码的修改应最小且集中
- 数据库迁移应有对应的回滚迁移
- 避免在一个提交中删除某些内容并在同一提交中替换它——将它们分开
与智能体协作
当指示智能体增量实现时:
“让我们从计划中实现任务 3。
先从数据库模式变更和 API 端点开始。
暂时不要碰 UI —— 我们将在下一个增量中处理。
实现后,运行 `npm test` 和 `npm run build` 以验证
没有破坏任何东西。”
明确说明每个增量的范围和不包括的内容。
增量检查清单
每个增量后,验证:
- [ ] 变更只做了一件事,并且完整地完成了
- [ ] 所有现有测试仍然通过(
npm test) - [ ] 构建成功(
npm run build) - [ ] 类型检查通过(
npx tsc --noEmit) - [ ] 代码检查通过(
npm run lint) - [ ] 新功能按预期工作
- [ ] 变更已提交,并附有描述性消息
注意: 在可能影响某个验证命令的变更后运行该命令。成功运行后,除非代码已更改,否则不要重复相同的命令——在未更改的代码上重新运行不会增加任何信息。
常见合理化借口
| 合理化借口 | 现实 |
|---|---|
| “我最后再一起测试” | 错误会累积。切片 1 中的错误会使切片 2-5 都出错。测试每个切片。 |
| “一次性做完更快” | 它感觉更快,直到某些东西坏了,而你无法在 500 行变更中找到原因。 |
| “这些变更太小,不值得单独提交” | 小提交是免费的。大提交隐藏错误并使回滚痛苦。 |
| “我稍后再添加特性开关” | 如果特性不完整,它就不应该对用户可见。现在就添加开关。 |
| “这个重构很小,可以包含进来” | 重构与特性混合会使审查和调试都更困难。分开它们。 |
| “让我再运行一次构建命令以确保” | 成功运行后,除非代码已更改,否则重复相同命令不会增加任何信息。在后续编辑后再次运行,而不是作为 reassurance。 |
红旗警示
- 编写了超过 100 行代码而未运行测试
- 单个增量中包含多个不相关的变更
- “让我也快速添加这个”的范围扩展
- 跳过测试/验证步骤以加快速度
- 增量之间构建或测试失败
- 大量未提交的变更累积
- 在第三个用例要求之前就构建抽象
- 触及任务范围之外的文件,“既然我在这里”
- 为一次性操作创建新的工具文件
- 连续两次运行相同的构建/测试命令,中间没有代码变更
验证
完成一个任务的所有增量后:
- [ ] 每个增量都经过单独测试和提交
- [ ] 完整测试套件通过
- [ ] 构建干净
- [ ] 特性按规范端到端工作
- [ ] 没有未提交的变更残留
参见
每个增量的验证是局部检查。在宣布任务完成之前,应用项目范围的“完成定义”作为最终关卡,这是每个增量无论任务如何都必须达到的常设标准。参见 references/definition-of-done.md。






