从 Vibe Coding 到可交付工程,中间差一套刹车系统

AI 写代码能更快跑起来,但能跑不等于可交付。用双速工程、护栏实验和最小刹车清单,拆清 Vibe Coding 进入生产链前该补什么。

封面

AI 让代码更快跑起来,这件事没什么可争的。

但团队很容易把"跑起来了"误读成"可以交付了"。从这一刻开始,风险不再只藏在代码里,也藏在判断标准里。

一段代码能通过 demo,能响应一个 happy path,能让产品同学说"看起来可以了"——这些都很诱人。尤其当这段代码是 AI 几分钟生成的,人会天然倾向于:既然已经这么快了,就别再慢下来了。

可软件工程里最贵的部分,往往是从"能跑"之后才开始。

测试、错误处理、配置、日志、审查记录、回滚路径、幂等、权限边界、责任归属——它们不一定让 demo 更好看,却决定这段代码能不能被放进一个真实系统。

我也不想把这篇文章写成"停止 Vibe Coding"——这个判断太粗糙。我更想说的是另一件事:团队需要一套"双速工程"。探索期让 Vibe 放大速度,交付期用规格、测试、审查、溯源和回滚把不确定性吸收掉。

刹车不是为了让车慢下来。

刹车是为了让你敢快。

一、Vibe Coding 的价值,在探索而非交付

先把概念边界划清楚。 本文所说的 Vibe Coding,指以自然语言快速驱动 AI 生成候选实现、主要用于探索反馈的写法,不涵盖所有 AI 辅助开发——比如有明确规格、测试和评审介入的 AI 编码,不属于本文讨论的 Vibe Coding。

承认一点:Vibe Coding 的效率收益是真实的。

GitHub Copilot 的早期对照实验里,使用者完成一个 JavaScript HTTP server 任务的速度快了 55.8%(Peng et al., The Impact of AI on Developer Productivity, arXiv:2302.06590;任务为 JavaScript HTTP server)。这个数字不能泛化到所有工程任务,实验任务也不是复杂生产系统。但它至少说明一件事:AI 能把"第一版代码"的到来时间大幅提前。

这就是它最有价值的地方。

它让你更快试错,更快看见一个想法有没有形状,更快把模糊需求变成可运行的东西。原型、黑客马拉松、内部脚本、一次性数据清洗、低风险工具,这些场景天然适合 Vibe。

在这些地方,你追求的不是长期演进,而是反馈速度。

Vibe Coding 的探索反馈速度

如果你只是想验证一个想法,完整的生产护栏会显得笨重。你不需要一开始就写完审计日志、回滚方案和权限矩阵。你只需要确认:这个方向值得继续吗?这个接口能不能串起来?这个页面交互是不是有感觉?

问题出在下一步。

很多团队没有把"探索代码"和"可交付变更"分开。代码一旦跑起来,就会沿着最短路径进入主干、进入内部工具、进入线上系统。这个过程有时不是某个人拍板决定的,而是一连串小让步叠加出来的。

“先合进去,后面再补测试。”

“这个只是内部用,不用那么严格。”

“AI 写的看起来还行,改起来也快。”

“这次需求急,先上线再说。”

每句话单独听都说得过去。连起来看,就是交付标准被悄悄改写了。

我把同一类 AI 生成代码放进三个场景里看,差异会非常明显。

原型验证里,代码只要能支撑一次讨论就够了。它可以没有完整测试,可以没有漂亮错误提示,也可以带着临时变量名。只要大家知道它是一次性代码,它就没有冒充生产资产。

内部工具里,标准要高一档。它服务真实同事,可能处理真实数据,至少要有基础校验、配置外置、错误提示、日志和明确负责人。否则今天省下的时间,明天会变成维护成本。

生产系统里,标准又变了。它对用户和业务负责,需要自动化测试、审查记录、监控、回滚、幂等、审计和故障预案。这里的代码不是"能不能跑"的问题,而是"出了事谁能解释、谁能恢复、谁能负责"的问题。

这三个场景可以都使用 AI。

但它们不能使用同一套放行标准。

三种场景的放行标准差异

二、从能跑到可交付,中间到底差什么

为了把这个差距说具体,我做了一个很小的实验。

我让 AI 写一个结账计算函数:输入购物车商品和优惠券,输出总价。这个需求足够简单,简单到几行代码就能跑起来。

AI 初版大概是这样:遍历商品,把 price * qty 加起来;如果优惠券是 VIP,打八折;最后返回 total。

非空代码 5 行。

正常路径确实能跑。一个商品,价格 10,数量 2,VIP 优惠,输出 16。demo 通过。

如果你只看 demo,这段代码已经"完成"了。

但我把它放进一张可交付检查清单里,结果完全不同。

我把这个函数当作"生产链路上的一段代码"来检查,护栏包含它对接系统时需要承担的部分;如果它只是一段一次性脚本,这些项不必全部强求。这组实验用 Python 3.9.6 标准库执行,没有外部依赖。我自己挑了 8 项护栏(理由是它们对应这个函数进入交付链时的常见缺口),换一组护栏数字也会变——重点不是 1/8 这个数字本身,而是缺口的存在和性质。

8 项护栏分别是:正常路径、空购物车、负价格、零数量、请求标识与幂等策略、审计事件、货币单位、折扣规则可配置。

结果是:AI 初版只通过 1/8。

它能算 happy path,但空购物车不会拒绝,负价格不会拒绝,数量为 0 不会拒绝。它没有 request_id,没有任何幂等键的入口。它没有 audit_events,出了问题无法解释计算过程。它没有 currency,跨币种场景会留下歧义。折扣规则写死,业务变更时只能改代码。

可交付版本补齐这些护栏后,通过 8/8。

行数不是质量指标——很多烂代码也很长。从能跑到可交付,真正多出来的是 7 个必须显性化的工程问题。

完整代码、测试用例和提示词作为内部实验记录保留,这是一次演示性实验,不是行业基准。

护栏项 AI 初版 可交付版本
正常路径可计算 通过 通过
空购物车被拒绝 缺失 通过
负价格被拒绝 缺失 通过
零数量被拒绝 缺失 通过
请求标识与幂等策略可定义 缺失 通过
审计事件存在 缺失 通过
货币单位明确 缺失 通过
折扣规则可配置 缺失 通过

具体到这个结账函数,差距是这样的:

AI 初版到可交付版本的护栏缺口

这张表就是 Vibe Coding 和可交付工程之间的缝。

这 7 个缺口不是"高级工程洁癖",每一个都对应一个具体故障场景。下面挑三项最有杀伤力的拆开讲,剩下四项放到一句里收尾。

负价格不拒绝,说明代码把"不可能出现的数据"当成了"调用方会保证"。这种假设在 demo 里成立,在真实系统里经常破产。只要上游多一次字段映射错误,优惠券、退款、折扣就可能被算成反方向——一笔 -50 元的"商品"会让总价直接打折,而前端可能根本不会看到任何报错。

没有 request_id,意味着这次计算没有可被稳定引用的请求标识。同一个请求重试两次,你不知道哪一次真的生效;同一个用户连续点了两次结账按钮,你也无从判断这是误触还是有意的二次提交。这里要小心一个常见误读:request_id 本身不等于幂等,它只是幂等键的入口。要做到真正的幂等,还需要一份重复请求的去重存储、明确的"重复请求该返回什么"的策略,以及配套的过期清理。我在这里要说的是,连入口都没留,后面的幂等无从谈起。

没有审计事件,说明这段代码对外是个黑盒。出问题时你只知道结果错了,不知道它用了什么优惠券、处理了几个商品、按哪套规则算出来。客服收到投诉时只能说"系统计算如此"——这在一个真实业务里是不可接受的。

剩下四项一句话收尾:空购物车、零数量、货币模糊、折扣写死,每一个都对应一类典型故障——把上游数据错误吞掉、把不可能的输入当合法、把单币种 demo 当多币种现实、把业务变更绑死在发布上。表格已经列出全部缺口,正文不必每行都讲一段。

需要补一句边界声明:这不是完整支付域建模。真实结账还要处理金额精度、舍入、税费、优惠叠加、对账等问题,本文实验只取最薄的一层。

这些问题在 demo 里都不会跳出来。

AI 初版按训练分布优先给出能跑通 happy path 的最短代码,护栏那部分天然被推迟到提示词里显式要求才会出现。它会把最有展示效果的部分先交出来,把最有责任重量的部分留在暗处。

第一版代码给你的是"功能形状",可交付工程要补的是"责任证据"。

测试证明它面对坏输入不会乱来;错误处理证明它失败时可解释;日志证明出事后能追踪;配置外置证明业务变化不必改核心逻辑;request_id 提供请求追踪线索,配合去重存储和重复请求处理策略才组成完整幂等;审计事件证明这次计算不是黑盒。

这些东西不是形式主义,它们是代码进入真实系统前必须携带的责任证据。

功能形状与责任证据的差异

如果团队只问"它能不能跑",AI 会让答案更快变成"能"。

但只要团队问"它能不能交付",速度就不是唯一答案。你还要追问:它失败时是什么样?被误用时是什么样?被审查时解释得清吗?上线后能回滚吗?下个月变更规则时会不会牵一发动全身?

严肃工程的价值,不是让探索慢下来,而是防止"探索期的轻标准"被当成交付期的合格线。

三、双速工程:探索期要快,交付期要慢下来

“双速工程"听起来像一个管理词,其实它只是在承认两个目标不同。

探索期要缩短反馈周期;交付期要让变更可审查、可回滚、可追责。这两个目标都重要,但不能用同一套规则同时最大化。

分界点别放在"这段代码是不是 AI 写的”。更该问的是:这段代码是不是要承担真实责任。

只要还在探索期,你可以快一点——允许临时实现,允许手动验证,允许先不抽象。关键是给它贴上标签:这是候选实现,不是可交付变更。

一旦它要进入交付链,标准就要切换。要补的不是"让代码看起来更专业",而是把责任边界写清楚。重点是这四个问题:

第一,谁定义验收标准? 模糊的"看起来可以"不算验收。

第二,谁确认异常输入? 边界、坏数据、并发冲突,得有人一个一个过一遍。

第三,谁审查安全边界? 权限、注入、密钥、外部依赖来源,这些不是 AI 能替你决定的。

第四,谁负责回滚? 出问题时不是"看一眼日志",而是真的有人能在五分钟内把变更撤掉。

剩下还有一些细节要回答:哪些行为打日志、哪些配置不能写死、哪些测试在合并前必须通过——这些问题在 AI 之前就存在,AI 只是把它们提前暴露了。

交付期必须回答的四个责任问题

AI 之前这些判断也常被推迟。但 AI 把推迟的时间窗压得更短,把"看起来已经完成"的错觉做得更逼真。

如果团队没有一个明确的阶段切换点,推迟就会被误认成省掉。Vibe Coding 制造的错觉就在这里:速度提升让你感觉工作少了,可很多工作只是从写代码阶段转移到了验收阶段。

中间要有一道门。

双速工程的阶段切换门

这道门可以只是一张 PR checklist,也可以是一段团队约定,或者 CI 里的必检项。但它必须存在。否则 Vibe Coding 会从一种探索方法,滑成一种交付豁免。

四、团队真正要防的,是偏差正常化

我更担心的不是 AI 生成了一段有 bug 的代码。

bug 一直都有,人写代码也有 bug。

更危险的是另一件事:团队开始习惯一种新的判断方式——只要 AI 产出够快,只要 demo 够顺,只要这次没出事,就可以把标准放低一点。

这就是偏差正常化。

我见过一类常见的路径。某个内部工具最初只是为了给运营同学跑一次性的批量数据清洗,临时实现,没有测试。两周后业务发现它跑得不错,就让它每周固定跑一次。又过一个月,产品提了一个小改动,工程师改了几行就直接合上去——反正"这个只是内部用"。再过半年,运营开始把它的输出直接接到对外的报表系统。这个时候,它已经在事实上承担生产责任,但代码层面还停在两周前的临时实现里。

回头看,没有任何一次决定是错的。每一次都看起来很合理:已经在跑了、就改一点点、反正出事了再说。但合起来,这段代码完成了一次彻底的身份转换——从一次性脚本变成生产依赖,而它的护栏一项也没补。

AI 在这里扮演的角色很微妙。

它不只是生成代码,还生成完成感。

Stanford 的一项研究(Do Users Write More Insecure Code with AI Assistants?, arXiv:2211.03622)发现,使用 AI 助手的参与者在 5 个安全任务中有 4 个更容易写出不安全代码,同时在该研究设置下,对代码安全性的自信反而更高。这个实验样本不大,任务也偏安全场景,不能直接推出"所有 AI 代码都更危险"。

但它揭示的机制值得警惕:不完整的输出,可能伴随更强的自信。

偏差正常化:完成标准被悄悄改写

这和工程现场很像。一段代码越快跑起来,团队越容易降低警觉。尤其当它结构清晰、命名还不错、注释也像那么回事,人会本能地觉得"差不多了"。

可严肃工程检查的往往不是"看起来"。它检查坏输入、失败路径、并发边界、权限边界、数据一致性、可观测性和恢复能力——这些东西在 demo 里通常不会出现。

所以 AI 生成代码进入团队流程后,最需要保护的不是代码风格,而是放行标准。

你可以让 AI 写第一版,但不能让第一版定义合格线。如果团队把"能跑"当作合格线,AI 会非常擅长满足它。它会给你越来越多看起来完整的实现,让你越来越少追问边界。

这时风险已经不是某个函数写得差,而是团队开始奖励"更快交出看起来完成的东西",不再奖励"拒绝把不完整的东西交付出去"。

严肃工程要保护的,正是这种判断力。

这里还有一个更隐蔽的组织问题:谁会因为"不合并 AI 初版"得到奖励?

很多团队奖励的是可见产出。谁更快做出 demo,谁更快关掉 issue,谁更快把 PR 推出来,谁就显得更有效率。可严肃工程里有一类价值很难被看见:拒绝不该合并的代码,删掉看似聪明但不可维护的实现,坚持补一个没人喜欢写的边界测试。

AI 会放大这种偏差。因为它让"可见产出"变得更快、更密集、更像真的完成了。相比之下,“我没有合并这段代码,因为它缺少审计和回滚"听起来很慢,也不够有成就感。

可工程判断力恰恰藏在这里。

一个团队要真正用好 AI,需要重新奖励那些不可见的动作:谁定义了验收标准,谁发现了边界缺口,谁拒绝了不该上线的候选实现,谁把一次 demo 成功改造成了一次可交付变更。

可见产出与不可见判断力的奖励倒挂

如果这些动作没有被看见,Vibe Coding 的效率会变成单向激励:产出越快越好,审查越少越顺,标准越低越像协作。最后,团队不是被 AI 打败的,是被自己重新定义过的"完成"打败的。

五、刹车不是为了慢,是为了敢快

严肃工程更像是刹车系统。

车上装刹车,不是为了让车永远慢——恰恰相反,只有知道自己能停下来,驾驶员才敢在合适的路段加速。

软件团队也一样。如果你没有测试、没有日志、没有审查、没有回滚,你其实不敢快,只是把风险往后推,赌它不会在这次爆掉。

团队敢用 AI,靠的是即使 AI 写错了,系统也能发现、隔离、修复和回退。

所以更实用的问题不是"要不要 Vibe Coding”,而是"什么场景可以 Vibe 到底,什么场景必须切到交付标准"。

我会用一张很小的边界表来判断。这里需要先界定一下:高风险链路指支付、权限、安全、资金、批量数据变更等错误难以回滚或影响面大的场景——这样可以避免把它误读成临时补的第四档。

场景 可以怎么用 AI 必须补的护栏
原型验证 直接生成,快速试错 明确一次性代码,不进入生产链
内部工具 生成初版,人补边界 基础测试、错误提示、配置外置、负责人
生产变更 生成候选实现 自动化测试、审查记录、日志、回滚、幂等、依赖来源校验、敏感信息扫描
高风险链路 只能辅助设计和生成草稿 人主导规格、威胁建模、灰度、演练和签发、许可证/来源检查

这张表只是分场景说"AI 能做到哪一步"。真要把它落到日常,还差一份具体动作。

最小刹车清单

下面这份清单可以直接搬到 PR 模板和 CI 配置里——不需要改造既有流程,但能把"双速工程"从口号变成动作:

  1. PR 作者声明分类:每个 PR 必须勾选所属分类(探索 / 内部 / 生产 / 高风险),写清楚预期的影响面。没有勾选,Reviewer 直接打回,不进入审查队列。

  2. Reviewer 对分类有否决权:如果 Reviewer 认为变更实际影响面超过作者声明的分类(例如声明"内部"但接到了对外报表),Reviewer 可以单方面升档,要求补齐对应护栏。

  3. 生产 / 高风险分类必须触发的阻断项(任何一项不通过,CI 红灯,合并按钮锁死):

    • 自动化测试覆盖关键路径与至少 2 项异常输入
    • 依赖来源校验(锁文件、SBOM 或等价机制)
    • 敏感信息扫描(密钥、token、内部域名)
    • 回滚说明(明确"出事后五分钟内怎么撤")
    • 高风险分类额外加:威胁建模简表 + 灰度发布计划
  4. AI 生成段落显式标注:PR 描述里说明哪些段落是 AI 生成的初版、人做了哪些修改。这不是为了追责,是让 Reviewer 知道把注意力放在哪里。

  5. 不可跳过项的例外审批:任何一项阻断要绕过,需要写明理由并由另一位有 owner 权限的同事 sign-off,留在 PR 评论里可被审计。

AI 代码进入交付链的最小护栏清单

这五条不复杂,合起来不到一页 PR 模板的篇幅。它的作用不是增加流程负担,而是让"AI 负责生成候选,人负责定义合格线"这件事每次都被显式做一次。

我见过的能稳定用好 AI 的团队,都不是只挑一种速度的——他们在探索期允许粗糙,在交付期不让步。一种速度用来探索,另一种速度用来交付。探索期越快,交付期越要清楚地慢下来。这不是为了抵消效率,而是为了把效率变成可以承担责任的工程结果。

所以,Vibe Coding 真正缺的不是油门。

它缺的是刹车、仪表盘和验收口。

有了这些,团队才不是在赌 AI 写对了,而是在建立一套即使 AI 写错也能安全前进的工程系统。

原文发布于 止语Lab


关于止语Lab

一个工程师的深度技术笔记。

不写入门教程,不追热点。只写那些真正折腾过、想通了的东西。

了解更多 →