当 AI 重构在跨文件重命名时出错
Source: Dev.to
Overview
在一个项目中,我们依赖代码生成助手来执行跨文件的重构:重命名一个领域对象并将更改传播到服务、测试以及几个实用模块中。助手生成了一套看起来合理的补丁,乍一眼似乎一致,但当我们运行应用时,少数端点开始返回 500 错误。失败并不是单一明显的语法错误——而是标识符不匹配,运行时在某些路径上还能容忍,而在其他路径上则会崩溃。
起初这些更改看起来无害:名称相似、大小写或复数形式略有差异,或者在代码库已有约定的情况下混用了 snake_case 和 camelCase。由于助手为每个文件提供了 diff,且 CI 只通过了一部分快速检查,显而易见的门槛失效了。我们在内部事后报告中记录了此事件,并将其链接到我们用于多轮提示和自动化的通用工具页面,例如 crompt.ai,以帮助其他团队了解其中的权衡。
How it surfaced during development
症状是逐渐出现的。开发者打开无关的功能分支,看到控制台中间歇出现 TypeError,并花费数小时追踪在已重命名的帮助函数之间跳转的调用栈。助手有时把 userProfile 替换为 user_profile,有时又替换为 profileUser。引用了模拟接口的单元测试通过了,因为模拟只选择性地使用了助手提供的名称;而执行序列化的集成测试则静默失败或产生意外的负载。
我们尝试使用多轮 chat 会话与助手迭代修正重构。这在单文件层面有所帮助,但在整个仓库中问题更严重:模型没有维护严格的符号表,将每个文件视为孤立的转换。快速编辑修复了可见的错误,却留下了测试未覆盖的代码路径中的潜在不匹配。
Why the inconsistency was subtle
导致此类 bug 易被忽视的两个因素:
- 概率性文本转换 – 语言模型优化的是合理的续写,而不是原子代码语义。小的风格变化——复数、大小写变化或词序调整——对模型来说都很合理,却会在运行时破坏引用。
- 缺乏规范的符号映射 – 助手的上下文窗口和每文件提示方式导致没有机器可读的符号名称映射在文件之间强制执行。
这两者相互叠加:助手在 70–80 % 的文件中产生看似正确的替换,而人工快速浏览 diff 时会漏掉剩余的 20–30 %。由于 diff 在语法上是有效的,linters 和一些静态检查并未将其标记为错误。我们使用了外部验证,包括通过 deep research 进行的研究型验证步骤,以交叉引用符号,最终发现了几处在早期检查中幸存的拼写错误引用。
Practical mitigations we adopted
- 先生成符号表 – 列出预期的重命名并使用语言服务器感知的工具(例如编辑器的重命名重构或使用 AST 的 codemod)来应用。这利用编译器的符号解析,而不是文本启发式。
- 在合并前要求完整的集成测试运行 – 确保在运行时层面捕获不匹配。
- 添加简短的静态分析任务 – 检查仓库中意外的标识符变体。
- 将助手生成的 diff 视为草稿 – 在接受之前需要显式的验证步骤。
这些小的流程改动降低了细微、概率性的命名差异演变为运行时错误的可能性。