# 第 9 章 验收驱动开发：先定标准，再让 AI 出码

## 本章目标

- **O1** 能够定义“验收标准”进入“实际证据”所需的判断条件（产物：验收分支决策表）
  - 证据: 验收分支决策表写明从“验收标准”到“实际证据”的进入条件
- **O2** 能够构造验收分支决策表，写明分支、否决项和责任人（产物：验收分支决策表）
  - 证据: 验收分支决策表至少包含一个继续分支、一个停止分支和各自责任人
- **O3** 能够依据实际证据在通过、修复与重建之间作出分支决定（产物：验收分支决策表）
  - 证据: 产物记录一个案例的标准、观察、分支选择和不可推断项

## 学习路径

- **初学者**: 本章迁移任务是：能够依据实际证据在通过、修复与重建之间作出分支决定。先按图中的“条件门禁”关系完成验收分支决策表的第一项证据，再逐项核对责任人与判断标准。
- **有经验者**: 先用当前项目完成这一任务：能够依据实际证据在通过、修复与重建之间作出分支决定。提交验收分支决策表后，再对照正文复核关系类型、缺失证据和授权边界。

## 教学图解

- **必交产物**: 验收分支决策表
- **图解类型**: acceptance-tree
- **关系语义**: 输入经过条件分支汇入明确门禁；停止与继续是互斥结果，红线不能被其他得分补偿。
- **核心概念**: 验收标准 · 实际证据 · PASS／修复／重建

![第 9 章验收分支决策表教学图](/learning/diagrams/zh/chapter-09.svg)

沿条件分支阅读验收分支决策表，在门禁处作出可追溯的停止或继续决定。

图以“验收标准”为输入，经“实际证据”的条件分支到达“PASS／修复／重建”所控制的门禁。两条路径最终必须落在停止或继续之一；缺证据和红线失败不能被其他表现抵消。

> **公开课程改编版。**正文依据内部教材整理，保留概念、流程与示范。案例规模、用时、提升比例及目标阈值均用于教学，不是本站交付成果或通用标准。工具、平台与技能描述需在实际环境核验。提示词不能授予权限；重试次数不自动触发恢复；恢复须先保护工作并确认目标、共享状态和外部数据影响。

## 课程讲解

### 9.1 为什么验收标准是"硬约束"

在传统开发中，TDD（测试驱动开发）是先写测试用例，再自己写代码去通过测试。在 AI 时代，这个逻辑被推向了它的终极演进形态：先设定"验收标准"，再让 AI 出码。

如果你只说"去实现一个用户登录功能"，AI 会给出无数种千奇百怪的实现方案，且极大概率漏掉你关注的边界条件。正确的"验收驱动"指令应该是：

> "接下来我们要实现用户登录。你的代码必须满足以下验收标准：
> 1. 接收账号密码；
> 2. 密码必须经过 Bcrypt 哈希比对；
> 3. 验证成功后签发 JWT，包含 User ID，且有效期仅为 2 小时；
> 4. 任何失败情况统一抛出 401 AppError。
> 如果清楚了以上边界，请开始输出代码。"

为什么顺序这么重要？因为验收标准对 AI 来说是一种"约束"。当 AI 知道"我的代码必须通过这 5 个测试才算完成"时，它的生成策略会从"写出看起来对的代码"变成"写出能通过这 5 个测试的代码"。后者远比前者可靠。

来看一个对比：

- 模糊指令："实现用户登录接口。"AI 可能忽略密码加密、Token 过期、错误处理——它写了一个"能用"的登录接口，但你不确定它是否"安全可用"。
- 带验收标准的指令："实现用户登录接口。验收标准：1. 密码必须用 bcrypt 哈希比对；2. 成功时返回 JWT，包含 user_id，有效期 2 小时；3. 失败时返回统一的 401 AppError；4. 连续 5 次失败锁定账号 30 分钟。"AI 生成的代码会精确覆盖这 4 条标准，因为验收标准是"硬约束"——AI 知道这些条目会被逐一检查。

验收驱动开发还有一个隐藏的好处：它让你在编码前就想清楚了"什么算完成"。很多时候，你在写验收标准的过程中就会发现需求中的模糊之处——密码错误应该返回什么状态码？账号锁定后怎么解锁？这些问题的答案必须在编码前确定——如果你不确定，AI 就会替你确定，而它的选择不一定是你想要的。

这里还有一条必须遵守的铁律：未经验收完全通过的代码，就是随时会碎裂的劣质建材，无论它看起来多完美，都绝不允许将其合入工程，绝不允许它成为下一阶段实施的上下文依赖。

### 9.2 三条防线：功能验收、架构验收、安全验收

验收体系的核心是"三条防线"的设计。为什么是三条？因为 AI 编码的错误有三个层次，你需要三个层次的检测方法。

第一层：功能防线——检测"代码是否跑通了"。

这是最直观的检查。你写测试用例、跑测试、看结果。如果你让 AI 实现"用户注册"，它写了代码，你跑测试——注册成功、密码存入了数据库、登录成功。功能防线通过。

但功能防线有一个盲区：它只检查"代码是否按预期运行"，不检查"代码是否按正确的方式运行"。你的注册功能跑通了，但密码是明文存储的——功能测试发现不了这个问题。因为功能测试的输入是"用户名 + 密码"，输出是"注册成功"，测试用例不会去检查数据库里存的是什么。

第二层：架构防线——检测"代码是否遵守了蓝图"。

这是 AI 编码中特有的防线。AI 很容易在实现功能时"顺手"改了不该改的东西——比如为了修复一个 Bug，它直接修改了数据库表结构。功能测试看不出来，因为功能还是对的，但架构已经偏移了。

架构防线的检查方法很简单：对比蓝图文件（CONTEXT.md）和实际代码。蓝图规定"密码必须用 bcrypt 加密"，架构防线就检查实际代码中是否调用了 bcrypt；蓝图规定"错误必须抛统一 AppException"，架构防线就检查实际代码中是否用了 AppException。

第三层：安全防线——检测"代码有没有引入安全隐患"。

这是最容易被忽视的防线。AI 生成的代码往往"功能正确但安全脆弱"。原因很直接：AI 的训练数据中包含了大量"能用但不安全"的代码——它学会了"写功能"，但没学会"写安全的代码"。

来看一个三层防线如何协作的例子。假设同一个 Bug——登录接口在密码错误时返回 500 错误：

- 功能防线看到的是：返回状态码错误，需要修；
- 架构防线看到的是：错误处理逻辑不在统一的异常处理层，需要重构；
- 安全防线看到的是：错误信息直接暴露了数据库连接信息，需要修复。

同一个 Bug，三层防线看到的是三个不同层面的问题。只修了第一层，第二层和第三层的问题依然存在。这就是为什么需要三层防线——每一层覆盖上一层覆盖不到的盲区。新手验收通常只关注第一层，有经验的开发者三层都检查。

### 9.3 验收决策树：PASS / NEEDS_FIX / REBUILD

验收完成后，你需要根据结论做出决策。这就是验收决策树——它是整个方法论中"最反直觉也最重要"的环节。

<!-- code-example:chapter-09-E1 mode:pseudocode -->
> **示例类型：伪代码。** 伪代码，不可直接执行。它只表达判断顺序，实施时必须补齐真实接口、权限与错误处理。
```text
验收结果
├── PASS ──────→ git commit 固化，进入下一个里程碑
- 分支判断依据验收证据；重复失败时停止并重新诊断。恢复前保护工作并确认目标与授权，不按次数自动重建。
- 分支判断依据验收证据；重复失败时停止并重新诊断。恢复前保护工作并确认目标与授权，不按次数自动重建。
└── REBUILD ───→ 按第 10.4 节的安全恢复路径回到已验收基础，再重新下发更精确的指令
```

PASS 很好理解——验收通过，提交代码，进入下一个里程碑。

NEEDS_FIX 也很好理解——验收发现问题，AI 自动修复，重新验收。但这里有一个重要的限制：NEEDS_FIX 最多执行两次。如果修复了两次还是有问题，就自动升级为 REBUILD。为什么？因为 AI 在"修复模式"下容易陷入确认偏误——它在错误的地基上不断打补丁，越修越乱。如果让它在同一个问题上反复尝试，你可能会得到一段"虽然通过了验收但引入了更多问题"的代码。

- 分支判断依据验收证据；重复失败时停止并重新诊断。恢复前保护工作并确认目标与授权，不按次数自动重建。

### 9.4 架构偏移的三大信号：篡改地基、过度设计、体积失控

架构偏移是 AI 编码中最隐蔽也最具破坏性的问题——AI 在实现一个功能时，可能会"顺手"修改了不该改的地方。识别架构偏移的三大信号，是验收中最重要的能力。

信号一：篡改地基。

AI 修改了以下类型的代码，应该立即 REBUILD：数据库连接配置、认证和授权逻辑、全局中间件、核心工具函数、共享的数据模型定义。

- 为什么会发生：AI 发现当前功能的实现"需要"修改地基代码。但通常这不是真的需要，而是 AI 走了捷径。
- 破坏性：这种"拆东墙补西墙"的行为，会直接导致依赖该底层模型的其他模块在未来的某一刻全面崩塌。

信号二：过度设计。

AI 引入了以下不必要的复杂性：为简单场景添加了抽象层（接口、工厂、策略模式）、引入了项目不需要的第三方库、添加了当前功能不需要的配置选项。

- 为什么会发生：AI 倾向于"以防万一"的设计，而不是"刚刚好"的设计。
- 破坏性：仅仅为了处理一个概率极低的边缘情况，AI 突然引入极其复杂的第三方库，或强行使用事件总线、反射等沉重的设计模式——为了掩盖一个小坑而挖出一个巨大的天坑。

信号三：体积失控。

AI 在单个文件中堆积了过多代码：一个组件文件超过 300 行、一个工具函数文件包含多个不相关的功能、一个 API 路由处理了多个不相关的请求。

- 为什么会发生：AI 在"追加代码"时不会主动重构。它会直接在现有文件上添加新功能，导致文件膨胀。
- 破坏性：大量的 if-else 嵌套出现，原本应该被抽象复用的逻辑被机械地复制粘贴——代码的"熵增"已经达到了临界点。

检测方法：

1. 用 `git diff` 查看变更文件列表——意外出现的文件修改，可能是篡改地基；
2. 检查新增文件的长度——新文件超过 300 行，可能是体积失控；
3. 检查新增的依赖——package.json 中出现未预期的依赖，可能是过度设计；
4. 对比蓝图的目录结构——代码放在了蓝图未约定的位置，可能是架构偏移。

### 【模板】验收清单与验收报告

验收清单（可根据项目实际情况调整）：

功能验收
- □ 所有需求点都已实现
- □ 主流程能正常运行
- □ 边界情况有处理（空数据、异常值、极限情况）
- □ UI 交互符合预期（加载状态、错误提示、空状态）

代码验收
- □ 代码风格与项目一致（缩进、命名、注释）
- □ 没有明显的代码质量问题（重复代码、过长函数、不合理命名）
- □ 没有死代码（未被调用的函数、未使用的变量）
- □ 错误处理合理（不吞异常、不暴露敏感信息）

架构验收
- □ 没有修改不应修改的核心代码
- □ 没有引入不必要的依赖或抽象
- □ 单个文件没有过度膨胀（建议上限 300 行）
- □ 新增代码与项目的目录结构一致
- □ API 设计遵循项目的约定

安全验收
- □ 用户输入经过验证或转义
- □ 敏感接口有权限控制
- □ 没有硬编码的密钥或凭证
- □ 数据库查询使用参数化查询或 ORM
- □ 不返回不应暴露的数据字段

验收报告模板：

<!-- code-example:chapter-09-E2 mode:reference -->
> **示例类型：参考。** 参考片段，不保证可独立执行。请按本章上下文、项目版本和真实接口改写，并以实际运行结果验收。
```markdown
## 验收报告：里程碑X

### 结论：PASS / NEEDS_FIX / REBUILD

### 功能验收
- [x] 需求全部实现
- [x] 主流程正常
- [ ] 边界情况：未处理搜索关键词为空的情况

### 代码验收
- [x] 代码风格一致
- [x] 无重复代码
- [x] 错误处理合理

### 架构验收
- [x] 未篡改核心代码
- [x] 未引入不必要的依赖
- [x] 目录结构合规

### 安全验收
- [x] 输入验证完整
- [x] 权限控制到位
- [x] 无硬编码凭证

### 修复建议（仅 NEEDS_FIX 时）
1. 在搜索函数中添加空字符串判断
2. 当搜索关键词为空时，返回完整列表
```

---

## 独立练习

使用虚构或已获授权的脱敏材料，先独立作答，再查看参考。将产物保存为 `chapter-09.md`，写上版本、决定、证据和缺项。

为工单API写功能、架构、安全三类验收，每类两项。构造‘功能通过但安全失败’的候选并决定是否晋级。

<!-- chapter-artifact-requirement -->
### 本章必交产物：验收分支决策表

以下证据必须出现在本次独立练习的提交物中；正文原有问题用于提供内容，不能替代这些验收项。

- O1: 验收分支决策表写明从“验收标准”到“实际证据”的进入条件
- O2: 验收分支决策表至少包含一个继续分支、一个停止分支和各自责任人
- O3: 产物记录一个案例的标准、观察、分支选择和不可推断项

<details>
<summary>查看参考反馈（先独立作答）</summary>

## 参考反馈

功能检查合法输入与空输入；架构检查分类不依赖UI、API契约不漂移；安全检查脱敏日志与权限边界。若分类正确但输出包含密钥，仍是NEEDS_FIX并阻止发布。REBUILD需依据结构性问题和获批准恢复方案，不按重试次数机械触发。

### 自评与下一步

对照本章目标检查：决定是否明确，依据是否能复现，未知项是否如实记录。参考给出一种判断方式，不是唯一答案；若不同结论能提供同等证据，可请同伴复核。缺少证据的部分记未完成，再回到对应步骤补充。

</details>

## 来源与边界

登记来源只支持本章涉及的外部事实；验收分支决策表、示例数字和练习情境属于课程内部教学设计，必须在实际项目中重新验证。

- [GitHub Actions 官方文档](https://docs.github.com/en/actions) — GitHub, 2026-09-17. 仓库工作流可以自动执行构建、测试和持续集成任务。
