Home
avatar

.Sam

【AI Native开发-08】Implement:让 AI 按计划改代码

摘要

Plan 评审通过,AI 终于可以改代码了。但”可以动手”不等于”可以放开手脚”。Implement 阶段是 SDD 里唯一真正改动生产代码的阶段,也是所有前期约束接受考验的阶段。

核心论点:

  1. 实现阶段的失控,几乎都源于”步子太大”和”边界太软”。 AI 一次拿到整个功能、没有明确的文件红线,就会自由发挥、顺手重构、越界修改。
  2. 小步 + 每步验证 + Diff 审查,是把”AI 改代码”变成可控操作的三个杠杆。每一步都要小到能一眼看完、能独立验证、出错能单独回滚。
  3. 遇到 Spec 与代码冲突,正确动作是暂停,不是自作主张。 AI 在实现中最危险的行为,是遇到矛盾时”猜一个最合理的”继续往下写——这个猜测会绕过所有闸门。

本篇给出 Implement 的执行纪律、指令模板、每步检查清单、冲突处理协议,以及”禁止改测试来掩盖错误”这条铁律。

一个典型场景

Plan 通过后,你对 AI 说:“按 Plan 实现删除文章功能吧。“然后去开了个会。

回来发现,AI 确实做完了,但 Diff 里有 14 个文件被改动:

  • 核心删除功能做了,没问题;
  • 但它把 ArticleList.astro 里列表加载逻辑整个重写了(Plan 明确说”不改列表加载”),理由是”原来的写法和新按钮不太搭”;
  • 它”顺便”给文章详情页也加了个删除入口(Spec 非目标:只在列表页删除);
  • 它发现 auth.ts 里一个函数”看起来有 bug”,顺手改了——而那个函数被发布流程依赖;
  • 有个测试红了,它把断言改宽松了,让测试通过。

14 个文件里,只有 5 个在 Plan 的清单上。你无法判断这 9 个计划外改动哪些安全、哪些埋了雷。code review 变成考古。

这不是 AI 能力问题,是执行方式问题。你给了它一个大任务、没有中间检查点、没有文件红线,它就用”看起来最完整”的方式填满了整个空间。

核心问题:为什么实现阶段最容易失控

Plan 是纸面的,Implement 是动真格的。这个阶段有三个固有风险:

风险一:改动空间是开放的。 Spec 和 Plan 阶段只产出文字,写错了改字就行;Implement 阶段 AI 能触碰代码库里的任何文件,一次越界就可能破坏无关功能。

风险二:AI 倾向于”把事情做完”而不是”只做要求的事”。 它被训练成提供完整、周到的解决方案。当它发现代码里有”更好的写法”或”顺手能修的问题”时,会倾向于一起改掉——哪怕你没要求、哪怕 Plan 禁止。

风险三:错误在这个阶段开始变贵。 Plan 阶段改方案成本是 3,代码写下去再发现问题,成本是 8(回滚)到 20(测试阶段才发现)。实现阶段的每一步越界,都在给后面积累排查成本。

对策不是”别让 AI 写代码”,而是把一次大爆炸式的实现,拆成一连串受约束的、可检查的小步

正确流程:小步、红线、每步验证

原则一:一次只执行 Plan 里的一小步

不要说”按 Plan 全部实现”。要说”只完成 Plan 的第 1 步”。

当前阶段:Implement

请只完成 Plan 中的第 1 步:
"写 src/lib/article-delete.ts 的权限与状态校验函数"。

允许修改:
- src/lib/article-delete.ts(新增)

禁止修改:
- 任何其他文件
- 不要写接口、不要写前端、不要写测试(那是后面步骤)

完成后请报告:
1. 新建 / 修改了哪些文件
2. 关键校验逻辑是什么
3. 有没有遇到 Plan / Spec 里和代码对不上的地方
   (如果有,停下来报告,不要自己决定怎么处理)

一小步的判定标准:Diff 小到能在 2 分钟内看完,且只对应 Plan 里的一个步骤。 这一步只写校验函数,就绝不碰接口和前端。

为什么这么拆?因为:

  • 每步的 Diff 极小,越界一眼可见(这一步只该出现一个新文件,出现第二个就是越界);
  • 出错能单独回滚这一步,不影响其他;
  • 冲突能在最早的步骤暴露,而不是等 14 个文件改完才发现。

原则二:每一步完成后立即检查 Diff

每完成一小步,不要急着让它继续。先看 Diff:

# 每步之后必做
git status          # 改了哪些文件?是否都在允许清单内?
git diff            # 具体改了什么?有没有计划外的改动?

检查三件事:

  1. 文件范围对不对:改动的文件是否都在这一步的”允许修改”清单里?出现清单外的文件,立即追问。
  2. 有没有无关改动:即使是允许的文件,里面有没有夹带”顺手重构""顺便改格式""觉得是 bug 就修了”的改动?
  3. 有没有临时代码console.log、注释掉的代码、写死的测试数据、TODO——这些在准出前必须清掉。

发现越界,当场处理:让 AI 把计划外改动撤回,而不是”先留着回头再说”——回头你就忘了。

原则三:测试和实现同步推进,禁止用改测试来”修绿”

实现里有一条铁律:

测试红了,先判断是实现错了还是测试错了;永远不允许通过改宽松断言 / 删断言 / 删测试来让它变绿。

AI 在实现阶段有个强烈倾向:测试一红,它的第一反应是调整测试让它通过——因为”让测试通过”是它被训练出的目标。但这会彻底摧毁测试的意义。

正确处理:

测试红了。停下来,按这个顺序判断:
1. 测试断言的行为,Spec 里是怎么规定的?
2. 如果 Spec 规定的行为和测试一致 → 是实现错了,改实现。
3. 如果 Spec 规定的行为和测试不一致 → 是测试理解错了,
   回到测试设计阶段改测试,并说明为什么。
4. 如果 Spec 压根没规定这个行为 → 这是 Spec 缺口,
   回到 Spec 补规则,重过 Gate 1,而不是现场拍一个行为。

只有第 3、4 种情况才动测试,且必须说清依据是 Spec。“改完测试就绿了”永远是危险信号,不是完成信号。

原则四:冲突时暂停,不要自作主张

实现中 AI 一定会遇到”Plan / Spec 说的”和”代码实际”对不上的情况。比如:

  • Spec 说”已发布文章不能删”,但代码里文章公开状态根本不是用 status 字段表达的(本项目是 frontmatter 的 hide 布尔,hide: true 为未公开、hide: false 或缺省为已公开);
  • Plan 假设有个权限中间件可以复用,实际不存在。

这时候 AI 有两种反应:

  • 危险反应:猜一个最合理的填上去(读 article.status,值是 undefined,校验被静默绕过);
  • 正确反应:暂停,报告冲突,等人决定。

在指令里显式授权它”停下来”:

重要:如果你在实现中发现 Spec / Plan 与真实代码不一致,
不要自己选择一个做法继续。请停止修改,报告:
- 冲突是什么(Spec 怎么说 vs 代码实际怎样)
- 你认为有哪几种处理方式
- 各自的影响
然后等待决定。

为什么这个”暂停”如此重要?因为实现阶段的每一次自作主张,都是在绕过 Gate 1 和 Gate 2。Spec 和 Plan 是经过评审的契约,AI 在实现中悄悄偏离契约,等于让评审白做。前面第 01 篇讲的 article.status === undefined 事故,根源就是 AI 在”Spec 和代码对不上”时选择了猜而不是停。

经验法则(第 01 篇也提过):如果修复动作是”在代码里加一个 Spec 没提到的判断”,那真正该改的是 Spec,不是代码。

Implement 指令模板

当前阶段:Implement
关联:Spec <路径>,Plan <路径>,本次执行 Plan 第 N 步

【本步目标】
<Plan 第 N 步的原文>

【允许修改的文件】
- <路径> — <改什么>

【禁止修改的区域】
- <路径 / 目录> — <原因>
- 不做任何 Plan 之外的重构、优化、"顺手修 bug"

【执行纪律】
- 只做这一步,不提前做后续步骤
- 测试和本步代码同步写
- 测试红了:先对照 Spec 判断是实现还是测试错,
  禁止通过放宽 / 删除断言让测试变绿
- 发现 Spec / Plan 与代码冲突:停止,报告冲突和备选方案,等决定

【完成后报告】
1. 新建 / 修改的文件清单
2. 本步关键逻辑
3. 本步运行了什么检查、结果如何
4. 未解决的问题或冲突(如有)

Implement 准入准出

准入:

  • Plan 已过 Gate 2 评审;
  • 实现拆成了有顺序、可独立验证的小步;
  • 每一步都有明确的”允许修改 / 禁止修改”清单;
  • 实现分支独立(feature branch),可随时整体回滚。

准出(进入 Verify 前必须满足):

  • Plan 的所有步骤都完成,每个 REQ 都有对应实现;
  • Diff 全部落在 Plan 批准的文件范围内,没有未解释的计划外改动;
  • 类型检查、静态检查通过;
  • 所有测试通过,且没有通过改宽松断言得来的”绿”
  • console.log、注释代码、写死数据、TODO 等临时残留;
  • 关键异常路径(权限失败、状态非法、重复操作)都有实现;
  • 实现中发现的 Spec / Plan 冲突都已回流修正,契约和代码一致;
  • 不可逆操作按 Plan 的缓解措施处理(如先移到 .trash 而非物理删除)。

三种 Implement 反模式

反模式一:大爆炸式交付。 一句话”按 Plan 全做了”,回来 14 个文件。无法 review、无法定位、无法安全回滚。对策:强制小步,每步单独检查。

反模式二:善意重构。 AI”贴心地”改进了它路过的所有代码——重命名变量、调整格式、重构它觉得不合理的函数。每个改动单独看都”没错”,合在一起让 review 寸步难行,还可能破坏被依赖的行为。对策:禁止修改清单 + 每步 Diff 检查,计划外改动一律撤回。

反模式三:修到绿色。 测试红了就改断言、加 try/catch 吞掉错误、甚至删掉测试。表面全绿,实际把唯一能发现 bug 的东西关掉了。对策:把”测试变红后的处理顺序”写进指令,任何断言改动都必须引用 Spec 依据。

失败回退

  • 实现发现 Spec 规则和真实代码表达对不上(如 Spec 假设 status 字段、实际是 hide 布尔)→ 暂停,回到 Spec 修正,不要在代码里猜;
  • 实现发现 Plan 的方案走不通(假设的中间件不存在)→ 回到 Plan 调整方案,必要时转 Spike
  • 实现发现需求本身没想清 → 回到 Spec
  • 测试红了且确认是测试理解错了 Spec → 回到测试设计改测试,留痕说明;
  • 越界改动已经发生且混在一起无法分离 → 整体回滚分支,重新按小步执行,不要试图在烂 Diff 上修补。

最后一条很重要:当一步实现已经失控(越界太多、夹带私货、改了测试),不要心疼”已经写好的代码”而硬着头皮继续。回滚重来的成本,远低于带着一个看不懂的 Diff 进 Verify。

人审重点

Implement 阶段人不需要逐行写代码,但必须守住几个检查点:

  • 每步的 Diff 文件范围:是不是只动了允许的文件;
  • 计划外改动:任何 Plan 清单外的改动都要追问、要么撤回要么补进 Plan;
  • 测试变红的处理:确认是改实现还是改测试,改测试必须有 Spec 依据;
  • 冲突的裁决:AI 报告的 Spec / Plan 与代码冲突,由人决定回到哪一层;
  • 不可逆操作:确认删除等操作按缓解措施执行;
  • 准出时的整体 Diff:合入前最后看一次全量 Diff,确认没有临时残留和夹带。

结语

Implement 阶段的纪律只有一句话:让 AI 在一个看得见边界、每步都能被检查的空间里行动。边界越清晰,AI 的能力越能被安全地放大;边界越模糊,它的”周到”就越容易变成事故。


小步实现走完了,代码和测试都在。但”AI 说它做完了、测试也绿了”并不等于”改动是安全的”。下一篇讲代码评审与变更控制:怎么系统性地审查一份 AI 产出的 Diff、怎么发现藏在合法文件里的越界逻辑、怎么给变更分级并让高风险改动强制经过人眼——这是代码合入主干前的最后一道防线。

AI Native SDD Implement 代码实现 软件工程