超越Vibe Coding——理解生成代码:审查、精炼、负责到底

152 阅读15分钟

你已经学会了如何通过提示让 AI 生成代码,而且此时多半也用这些技巧产出了些代码。接下来进入关键阶段:确保这些代码正确、安全、且可维护

作为开发者,你不能拿到 AI 的输出就稀里糊涂地上线。你需要审查它、测试它,必要时改进它,并将其集成进你的代码库。本章聚焦于如何读懂 AI 给你的代码,如何迭代式地编辑与调试它,并如何把这段代码真正纳入你的名下,成为项目的一部分。

本章涵盖:

  • 按你的原始意图来解读 AI 的代码
  • 多数解”现象:为何 AI 生成的代码常像一种常见解法
  • 评审代码的技巧:清晰度与潜在问题
  • 当 AI 写的代码不如预期时如何调试
  • 为了风格或效率对代码进行重构
  • 编写测试以验证代码行为

掌握这些技能,你就能更有底气地把 AI 的贡献融入你的项目。

从意图到实现:理解 AI 的“解读”(From Intent to Implementation)

拿到 AI 的代码后,第一步应当是把它与你的意图(你给出的提示)进行对照:它是否满足了你提出的需求? 有时 AI 会略有误解,或只实现了你要求的一部分。

仔细读代码。用脑中(或纸上)单步推演

  • 追踪它在一个典型输入下会做什么。
  • 如果你的提示包含多个部分(“做 X 并且做 Y”),核对 AI 是否完成了。
  • 确认 AI 没有加入你没要求的功能——有时它会“自作聪明”地加上日志或额外参数,这可能有用,也可能多余。

就像审同事的代码一样,若有不清楚的地方,标记下来。先尝试找找它存在的正当理由;如果找不到,就追问或考虑删掉

例如,你要求写一个素数检测器,而 AI 的代码还会对每个数字打印“Checking 7…”。这可能是你提示方式的产物,或来自训练数据中的教程模式(教程常打印进度)。如果你不需要,计划移除它,或提示 AI 帮你移除。

同时,确认边界情况按你的预期处理了:你希望它处理空输入吗?如果输入可能是 None 或负数,AI 是否考虑了?

如果你的提示里存在歧义而迫使 AI 做了选择,把那些地方找出来。也许你没指定输出格式,而它选择了打印而不是返回。现在你要决定接受还是修改。

这个理解阶段至关重要,别跳过。即便你准备写测试,阅读理解仍然重要,因为测试可能覆盖不到所有问题(而且对一些显而易见的点,读更快)。

最后,考虑 AI 的假设。AI 往往倾向于“多数/常见的解读”(引出下一节)。

“多数解”问题:最常见 ≠ 最合适(The “Majority” Problem)

在大量代码上训练的模型,经常产出训练集中最常见(或最简单可行)的方案——我称之为“多数解效应”。它对一般场景是对的,但未必最适合你的具体情境。

比如,你要求一个查找算法却未给上下文,AI 可能输出线性查找,因为它直观且常见。可你其实需要二分查找,只是没强调性能关键。线性查找在中等规模下可以,但性能优先时就不行了。

类似地,AI 可能使用全局变量,因为不少入门示例都这么做;但在你的项目里,这也许是不被接受的风格。

要意识到:AI 的方案往往为通用场景做了优化。而作为人类开发者,你拥有 AI 缺乏的上下文洞见

应对建议:

  • 识别假设:如果代码假设列表已排序或输入总是有效,这假设合理吗?你指定过吗?若没有,可能应该加检查。
  • 比较替代:你知道有多种做法(不同算法/结构)吗?AI 选了哪一个?这正是你想要的吗?若不是,要么重提要求,要么直接改
  • 关注边界:若 AI 代码只覆盖“常规情况”,却忽略了你在意的边界(如整数溢出),那就需要修补——训练示例可能没管这些,但你的场景很重要。

理解 AI 倾向于给出“通用解”,会让你更擅长审查它的代码。它不是魔法、也不是量身定制;它是对解法的高明猜测量体裁衣由你来做。

可读性与结构:常见模式与隐患(Code Readability and Structure)

AI 生成的代码常有一些特征

  • 注释偏多或表述古怪(源自教程风格,教程往往“高注释”)
  • 变量名习惯性地用 i/j/k 之类
  • 为覆盖通用情形而显得冗长

检查这些并评估是否符合你的项目风格。功能也许没问题,但需要一次可读性体检。你可能需要:

  • 重命名变量,让它们更有意义,或与代码库保持一致。
  • 删改注释:像 # 检查是否为素数 这种在“自解释”代码上方的注释可删;但解释复杂逻辑的注释要保留或强化。
  • 通过 linter/formatter(如 Python 的 Black、Go 的 gofmt)统一格式与空白、括号风格。

也要留意异常结构:本应只有一个函数,AI 却定义了多个类/函数?有时它按训练示例的拆分方式来组织。如果这显得过度,可内联(反之亦然)。代码是不是过于巧过于直?AI 有时会给出一行流(one-liner) ,这是否契合团队偏好?不合就调整。

其他常见隐患:

  • 一位偏差(off-by-one)错误
    循环边界尤其要当心。可用简单样例在脑中走一遍循环。
  • 未处理的异常
    代码是否默认文件总能打开、输入总是正确格式?需要则补上错误处理
  • 性能陷阱
    比如在大数据集里用内层循环做成员检查,其实用集合(set)更好。AI 的方案可能正确但不优
  • 库的使用
    确认用的是你想要且可用的库。有时它会为一个简单求和拉进 numpy(因为见过类似示例)。若不值得引入依赖,就改成纯原生或你预计的库。
  • 不一致
    偶尔会出现文档字符串(docstring)与实现不符的情况(逻辑改了,注释没改)。修正之。
  • 小语法问题
    罕见,但在某些语言里仍可能混淆。
  • 过时 API
    可能调用了库的旧函数。见到不熟的调用,快速查官方文档确认与你的版本匹配。
  • 占位符
    若输出里有 “Your code here” 这类模板残留,记得填上

一句话:把 AI 的代码当作实习生写完就下班的作品对待——你需要把关质量并正确集成

调试策略:定位并修复错误(Debugging Strategies: Finding and Fixing Errors)

假设你运行了代码(或写了测试,我们稍后会讲),有些地方不工作。调试 AI 生成的代码与调试你自己的或他人的代码并无二致——唯一不同是这段代码不是你写的,你可能更不熟。但因为你已经认真读过(见图 5-1),你其实已做好准备。

image.png

六步调试法(Six-Step Approach to Debugging)

  1. 复现问题。
    用会失败的输入运行该函数或代码,观察输出或报错。
  2. 定位问题源。
    使用常规调试技巧(如打印日志)或调试器单步执行。若是逻辑错误(输出不对),就手动或通过打印追踪逻辑,找出与预期分叉的位置。
  3. 对照提示与代码。
    有时 bug 仅仅是没有完全实现需求——比如你要求排序但并未正确排序。这可能意味着 AI 的逻辑有缺陷,或边界情况(如空列表)未被处理。
  4. 借助 AI 来调试。
    你可以把有问题的代码喂回给 AI,并说明:“这段代码在 X 场景下结果不对,能帮忙找 bug 吗?”它常会像代码评审那样分析并指出问题。例如,它也许会发现循环本应遍历到 len(arr),却只到 len(arr)-1。要注意不要盲目信任——就像请同事协助调试一样看待它。
  5. 修复代码。
    你可以选择手动修,或提示 AI 生成修正版。若修复一目了然,直接改;若不明显,可以这样提示:“上述函数在输入 X 上失败(期望 Y,实际 Z)。请修正。”AI 可能会据此调整代码。
  6. 再次测试。
    确认问题已解决,且没有引入新的问题
    我推荐测试驱动式调试:尽可能为关键函数编写一些测试(本章后面测试部分会详述)。任何失败的测试都会直接指明问题所在;除最简单的函数外,这通常比手动检查更快。

最后,调试时别只问 what,也要问 why。试着理解 AI 为何出错:是否因为提示里该点不清楚?这会影响你下次如何提示,或者提醒你今后总要针对该方面做二次检查。比如,如果你发现 AI 不特别说明就不处理空输入,你就会开始在提示中总是写明,并在审查时重点关注。

为可维护性而重构:让 AI 代码成为“你的代码”

(Refactoring for Maintainability: Making AI Code Your Code)

当代码在功能上正确后,考虑重构,使其符合项目规范并便于未来维护。AI 的工作是尽快给你代码;你的工作是把它打磨好

以下是另一套六步重构流程

  1. 对齐风格规范。
    用代码格式化器或 linter 处理一遍,修复如“变量名应小写”“行过长”等警告。这能迅速让代码看起来像你们代码库的风格。多数 AI 工具在风格上还行,但通常还需微调。

  2. 改进命名与结构。
    若 AI 在类中起了 _helper1_helper2 之类的函数名,而你偏好语义化命名,就重命名。若它拆出一堆仅被使用一次的小函数,且并未增加清晰度,可考虑内联(反之亦然)。

  3. 移除不必要部分。
    例如输出里可能带了你没要求main 区块或测试代码——不需要就删。反过来,如果它把所有逻辑都塞进单个函数,而你希望为清晰度拆分成若干小块,就现在拆开。

  4. 补充文档。
    若这段代码将作为库或模块提供给他人使用,请按规范添加docstring/注释。AI 可能写了一些注释,但你要确保符合项目标准(比如你们要求在 docstring 中记录参数与返回值)。

  5. 必要时优化。
    代码虽能跑,但是否够高效?若它会在紧凑循环或大数据上频繁调用,检查其复杂度。AI 可能没有采用最优方案(“多数解”往往是简单循环而非更优解)。若有性能顾虑,改用更好的算法。你可以再次让 AI 参与:

    “请把这段代码优化快一些,比如把查找从 list 改用 set。”
    当然,作为开发者,你通常知道自己想要的模式,也可直接实现。

  6. 必要时简化。
    有时 AI 的代码会过度冗长。例如,本可用单个条件表达式的地方写了多层 if-else。虽说“显式”未必是坏事,但适度化繁为简能提高可读性且不失清晰。

重构的目标,是让后来者看到这段代码时看不出“AI 写的”痕迹——它就应该是好代码。这往往意味着加入一些人类的小细节,让代码更干净。

重构后要验证没有被你改坏——下面就转向测试

测试的重要性:单元、集成与端到端

(The Importance of Testing: Unit, Integration, and End to End)

测试一直重要,对 AI 生成代码则格外重要,原因有二:其一,你不是从零写的,需要更强把握它在各种情况下能工作;其二,若你之后继续提示 AI 修改或引入更多 AI 代码,测试能帮助你确保既有功能不被破坏。下面快速回顾常见测试类型:

  • 单元测试(Unit tests)
    为你从 AI 得到的每个函数/模块编写测试,尤其覆盖边界情况。以“素数”示例:测试素数、合数、1(边界)、0 或负数(定义预期行为)、大素数等。若全部通过,基本可信。
    你甚至可以让 AI 生成这些测试

    “为上述函数编写覆盖边界情况的 PyTest 单元测试。”
    它通常做得还不错。但仍要亲自审查,确保有效且到位

  • 集成测试(Integration tests)
    若 AI 代码会与代码库其他部分交互(如访问数据库),写测试在上下文中调用它:是否确实写入了应有的数据?若它输出会被其他函数消费,就把它们串起来测试。

  • 端到端测试(End-to-end tests)
    若该代码是更大流程的一部分,就从头跑到尾。例如它位于某个Web 路由中,就在测试环境里对该路由发请求,检查输出格式、错误处理等是否可靠。

需要的测试强度,取决于代码的重要性与复杂度。但即便是简单脚本里的少量断言或一次手工跑通,也好过没有。记住:测试不仅能发现 bug,还能锁定行为。当你(或 AI)之后再改动时,测试能防止回归

测试也是归属感的好工具:当你通过测试发现并修复了问题,就能对这段代码建立信心。此时,说“这段代码是你的”完全合理——就像代码库里的其他任何代码:你理解它、信任它,并有测试为其护航。

关于 AI 与测试的小提示
一些 AI 编码工具开始集成测试建议。例如 CodeWhisperer 偶尔会在代码后建议一个 assert。把这类建议当作起点,不要假设它面面俱到。动动脑筋想些有创意的边界用例——这正是人类直觉仍然最有价值的地方。

摘要与下一步(Summary and Next Steps)

我们已经走过了生成—理解—调试—重构这条闭环。这个循环的耗时可能很短(小函数在几分钟内完成),也可能更长(复杂模块需要数小时到数天,并伴随间歇性的 AI 协助)。

需要明确的是:你(开发者)对最终代码负责。AI 只是加速创作的工具,一旦出问题,它不会替你背锅。另有许可证/版权风险:一些 AI 提供商提示,当输出超过一定长度时,统计上更可能包含被复制的材料。虽然这种情况很少见,且厂商已经大幅缓解,但就像你查看 Stack Overflow 的答案时会留意授权或署名一样,仍应快速检查——尤其当输出体量大、或“好得出奇”时。比如,你让它“实现快速排序”,AI 返回 20 行干净代码——这通常没问题,属于常识性实现。但若你要求的是冷门内容,而它给了你一大段代码,不妨截取其中独特的字符串上网搜索,判断是否逐字来自某处。近期已有记录在案的案例表明,一些 AI 系统会复现期刊文章等受版权保护文本。作为负责任的代码所有者,当 AI 生成内容超出通用模式与特定来源高度吻合时,应核验其来源

最后,把代码集成进你的项目:纳入版本控制,必要时在提交信息里注明“使用了 AI 辅助”。这并非硬性要求,但有些团队希望保留这类痕迹以便追溯。

随着需求变化,你很可能会不断修改这段 AI 生成的代码。像对待其他代码一样对待它:别想着“这是 AI 的代码,我再让 AI 改”。你当然可以继续让 AI 协助,但也可以直接手改——选对你来说最高效且最可维护的方式即可。

通过认真审查与测试,AI 生成的代码就会成为你项目里“再普通不过”的一部分。此时,第 10 行是 AI 写的还是你写的,已无关紧要——重要的是它是否满足项目的需求与标准

遵循上述做法,你既能利用 AI 编码的速度优势,又能保障质量;既能避免盲目信任的陷阱,又能把它正确地融入专业的开发工作流

接下来,第 6 章将讨论 AI 如何从根本上改变原型阶段。我会介绍利用 AI 助手把从想法到可用原型的周期从数天缩短到数小时的实用技巧;讨论具体的 AI 原型工具(包括 Vercel v0screenshot-to-code 工具),以及在 AI 引导下进行迭代打磨的策略。

我还会探讨从 AI 生成原型过渡到生产就绪代码这一关键过程:当 AI 成为开发工作流的核心组成时,会出现哪些机会挑战。通过真实案例,我将展示开发者如何在快速验证想法的同时保持代码质量,并规避从概念到落地过快推进时常见的坑。