返回博客

用 AI 写代码,最贵的 bug 是过度自信

AI 写代码已经很强了,但有一种失败模式一直没变:你让它改一个按钮的颜色,它帮你把整个组件重构了一遍。或者你说「修一下这个 bug」,它给你返回一个三层抽象的新架构。

Andrej Karpathy 在推上总结了一组 LLM 编码的行为准则,我读完之后觉得——这不只是对 AI 的要求,这是一套对任何代码改动者都成立的工程协议。把它整理出来,顺带说说我自己的理解。

动手之前,先暴露假设

第一条准则:不要假设,不要隐藏困惑,把取舍说出来。

LLM 最容易出的问题不是技术能力不足,而是它永远不会说「我不确定」。它会在两种合理解释之间静默地选一个,然后按着这个理解写完整个实现,等你发现方向不对,改动已经散落在十个文件里了。

人也有一样的毛病。需求模糊的时候,很多工程师倾向于「先做,做了再说」——因为问清楚感觉像是在示弱。但其实反过来:在动手之前说出你的假设,才是高效的做法。「我理解这里需要前端校验,后端也要校验,对吗?」这句话花 30 秒,能省掉两轮返工。

不确定就停下来,点名说清楚哪里不确定,然后问。这不是弱点,是工程素养。

最小实现,不投机

第二条:用最少的代码解决问题,不写投机性功能。

我把「投机性代码」定义为:没有人要求、但「感觉以后可能用得到」而写进去的东西。这类代码最危险——它通过了 code review(因为看起来很周全),但它的行为从未被验证过,它的假设从未被确认过,它只是静静地待在那里等着成为未来的债。

检验标准很实在:如果你写了 200 行,但 50 行就够了,那剩下的 150 行就是一个问题,不是一个加分项。每一行多余的代码都是一行需要维护、理解、debug 的代码。

问自己:「一个有经验的工程师会不会说这里过度设计了?」如果答案是会,就简化。

外科手术式改动

第三条是我觉得最被低估的一条:只碰你必须碰的,只清理你自己制造的烂摊子。

这条规则在 AI 写代码的场景下尤其重要。AI 很容易在「顺手」的驱动下,把周边的代码也「改善」一遍——格式统一了,注释更清晰了,变量名更规范了。看起来都是好事,但每一行改动都是一行可能引入 bug 的变动,都是一行 code review 要看的内容,都是一条可能打断 git blame 追溯链的记录。

规则很清晰:

  • 不要「顺手」优化周边代码
  • 不要重构没有坏掉的东西
  • 保持已有的代码风格,哪怕你不喜欢它
  • 发现了无关的死代码?提一嘴,但不要删

你自己的改动产生了孤儿 import 或者用不到的函数——清理掉。但那些「本来就在那儿」的烂代码,不是你这次任务的责任范围。

每一行改动都应该能追溯到需求

检验方法很简单:逐行看你的 diff,每一行都应该能直接对应到用户的请求。如果有一行你要绕个弯才能解释「为什么改这里」,那这行改动很可能不该在这次提交里。

先定义「完成」是什么样子

第四条:在执行前把成功标准转换成可验证的形式。

「修一下这个 bug」是一个弱目标。「写一个能复现这个 bug 的测试,然后让它通过」是一个强目标。差别在于:强目标让你可以独立循环推进,不需要每走一步都来确认。弱目标在中途每个节点都可能出现「这算修好了吗」的争议。

对多步骤任务,花两分钟列一个计划,每一步后面跟一个验证动作:

1. 在路由层加参数校验 → verify: 缺参数时返回 400
2. 在 service 层加业务规则 → verify: 单元测试通过
3. 更新 API 文档 → verify: 示例请求能跑通

这不是繁文缛节,这是让你不迷路的路标。

一句话心法

改动范围的边界,就是需求的边界;越出边界的每一行代码,都是你替别人做了一个未经确认的决定。