ChatGPT、Codex实战:改完代码测试也通过了,为什么合并以后功能还是会出问题?
最近用Codex改真实项目时,有一种情况特别容易让人产生错觉:
代码改完了,测试也绿了,Diff看起来也没什么问题。
于是你很自然地会觉得:
这次应该可以合并了。
但真正合并到主分支,甚至进入测试环境以后,却发现功能还是出了问题。
有时候是接口返回不对。
有时候是某个旧功能突然失效。
有时候单独测试完全正常,一进入完整业务流程就开始报错。
更麻烦的是,这种问题表面看起来甚至不像“AI写错代码”。
因为你重新回头看:
代码逻辑合理。
测试通过。
Lint正常。
Build也没失败。
那为什么最后还是会出问题?
真正的原因往往不是Codex不会写代码。
而是:
“测试通过”只证明了一部分事情成立,它并不能证明整个系统在真实环境里仍然正确。
这也是AI越来越多进入真实项目以后,一个很容易被低估的问题。
一、先看一个很典型的真实场景
假设现在有一个用户权限接口。
需求很简单:
给管理员新增一个权限判断。
你让Codex修改。
它很快完成了几件事:
修改权限判断函数。
补充单元测试。
更新对应接口逻辑。
运行测试。
测试全部通过。
从任务结果上看,没有任何问题。
但代码合并以后,测试环境里却出现了一个异常:
普通用户访问某个旧接口时,也被错误拦截了。
你回头看Codex改的那几行代码,逻辑没有明显错误。
新增测试也覆盖了管理员场景。
问题出在哪里?
最后可能发现:
原来的权限函数不仅被新接口调用。
还有另外三个旧模块也依赖它。
Codex完成的是:
“管理员权限这个任务正确了。”
但系统真正要求的是:
“管理员权限任务正确,同时不能破坏其他依赖这个函数的旧行为。”
这两个标准并不是一回事。
二、测试通过,究竟证明了什么?
很多人潜意识里会把“Tests Passed”理解成:
代码已经正确。
但工程上更准确的理解应该是:
当前测试集合所表达的这些假设,暂时没有被代码破坏。
注意这里有两个限制:
第一,测试只能验证“已经写出来的条件”。
第二,测试只能验证“它实际执行到的路径”。
所以测试通过真正证明的是:
Known Cases Passed。
而不是:
Everything Is Correct。
这两个差别非常大。
假设一个函数有10种业务状态。
测试只覆盖其中4种。
那么测试全绿,只能说明那4种目前没问题。
剩下6种情况究竟会不会出错,测试其实什么都没有告诉你。
这也是为什么AI开发里,“自动补测试”虽然很重要,但不能把它理解成最终验证。
AI往往会围绕当前需求生成测试。
当前任务要求修复A。
于是AI会重点验证:
A有没有修好。
但真正项目里的风险常常来自:
修A的时候,有没有意外改变B、C、D。
三、最常见的原因:局部正确,不代表集成正确
这是这类问题最核心的工程机制。
Codex处理任务时,很容易形成一个相对清晰的局部闭环:
读取相关代码。
找到目标函数。
修改逻辑。
补测试。
运行测试。
任务完成。
从局部看,这套流程完全合理。
但真实软件不是一组互相独立的文件。
而是一个相互依赖的系统。
一个很小的修改,都可能影响:
调用链。
接口契约。
共享状态。
数据结构。
配置。
缓存。
权限。
数据库。
异步任务。
外部服务。
所以有时候你会发现:
每一步单独都正确,组合起来却不正确。
例如:
一个函数返回值从null改成空数组。
调用它的当前模块没问题。
测试也通过。
但另一个旧模块原本依赖null判断业务状态。
于是那个旧模块出错。
Codex当前修改没有任何明显Bug。
真正被破坏的是:
模块之间原本没有明确写出来的隐含契约。
四、第二类问题:AI看得到代码,却不一定看得到业务约束
真实项目里最危险的东西,很多时候不是代码。
而是:
没有写在代码里的规则。
比如:
“这个字段虽然允许为空,但线上流程实际上一定会有值。”
“这个接口不能改返回顺序,因为旧客户端依赖这个顺序。”
“这个任务必须先写数据库,再发消息。”
“这个权限判断虽然看起来可以抽象,但另一个内部系统依赖当前行为。”
这些规则可能存在于:
历史经验。
产品约定。
团队习惯。
线上兼容需求。
旧系统债务。
甚至某个开发者脑子里。
而Codex只能根据它实际能够读取到的信息判断。
如果这些规则没有写进:
测试。
类型。
接口定义。
文档。
AGENTS.md。
项目约束。
那么对AI来说,它们很可能根本不存在。
于是就会出现一种特别典型的情况:
AI写出的代码在程序逻辑上是合理的,但在业务系统里是不成立的。
这也是为什么真实项目越复杂,AI任务验证就越不能只看“有没有报错”。
五、第三类问题:单元测试通过,但真实运行环境不同
还有一种非常常见的情况:
本地测试完全正常。
合并以后却失败。
原因通常来自环境差异。
例如:
环境变量不同。
数据库数据不同。
依赖版本不同。
缓存状态不同。
权限不同。
外部服务响应不同。
Feature Flag不同。
测试数据库里只有干净数据。
而真实环境里可能已经存在几年历史数据。
某段代码在测试环境里处理100条数据没问题。
线上面对100万条记录时可能直接超时。
所以:
逻辑正确 ≠ 环境正确。
AI能够帮你验证逻辑。
但如果测试环境本身没有模拟真实状态,那么测试通过仍然无法覆盖实际风险。
六、还有一个很隐蔽的问题:测试本身也可能跟着错误一起被修改
这是用AI改代码时尤其值得注意的一点。
假设原来有一个测试:
expected = 旧行为
Codex修改了功能。
发现测试失败。
然后根据“新逻辑”顺手把测试也改成:
expected = 新行为
最后测试通过。
看起来非常漂亮。
但问题在于:
谁证明这个“新行为”真的是业务需要的?
如果AI同时修改:
实现。
测试。
预期结果。
那么测试很可能已经失去了独立验证能力。
它只是变成了:
AI用自己生成的规则去验证自己生成的代码。
这在工程上是一个非常重要的风险。
所以真正高质量的验证,不应该只问:
“测试有没有通过?”
还应该问:
测试验证的是原始业务需求,还是验证了AI自己重新定义的行为?
七、为什么AI编程越普及,这类问题反而会越来越明显?
因为AI正在显著降低“修改代码”的成本。
以前开发者改一个跨模块功能,可能会因为风险高而非常谨慎。
先读代码。
再确认依赖。
再慢慢修改。
现在Codex几分钟就能生成完整Diff。
代码生成速度提高以后,人很容易产生一种心理变化:
既然修改这么快,那就多改一点。
于是一次任务原本改2个文件。
现在可能改8个。
原本只修Bug。
现在顺手重构。
原本不补测试。
现在顺便补一整套。
看起来效率提高了。
但修改面也扩大了。
而修改面越大,潜在的集成约束越多。
于是未来开发里真正昂贵的部分会逐渐从:
“代码怎么写?”
转向:
“我怎么证明这个修改没有破坏其他东西?”
八、怎么排查这类问题?
遇到“测试通过但合并后仍然出问题”,我一般不会第一时间重新让AI再改一遍。
更有效的是先从四个方向检查。
1. 看影响面,而不是只看修改文件
不要只看:
“Codex改了哪几个文件?”
还要看:
这些函数被谁调用?
这些类型被谁依赖?
这些配置被哪些模块读取?
这些返回值有没有外部消费者?
一个修改真正的影响面,往往远大于Diff本身。
2. 区分单元测试和集成验证
单元测试证明局部逻辑。
集成验证才证明多个模块能不能一起正常运行。
如果改动涉及:
接口。
数据库。
权限。
状态流。
跨模块调用。
只跑单元测试通常是不够的。
3. 检查测试有没有被“顺着实现一起改掉”
如果AI同时修改了测试和实现,重点看:
测试的预期值为什么改变?
这个变化来自需求,还是来自新代码?
如果解释不清楚,就应该重新确认。
4. 让Codex主动列出“没有验证的东西”
这个方法其实非常实用。
不要只让Codex回答:
“任务完成了吗?”
而是要求它额外输出:
已验证内容。
未验证内容。
潜在影响模块。
可能存在的边界情况。
需要人工确认的位置。
你会发现,这类信息有时候比“测试通过”更有价值。
九、怎么降低以后再次发生的概率?
真正有效的方法,不是让AI更谨慎一点。
而是把项目里的隐含规则变得更显式。
例如把重要约束写进:
测试。
类型。
接口Schema。
文档。
AGENTS.md。
CI规则。
静态检查。
验收标准。
你要尽量把:
“只有人知道的规则”
变成:
“机器也可以读取和验证的规则”。
因为只要约束仍然只存在于人的经验里,AI就永远可能绕过它。
十、给自己测一个指标:集成回退率
这篇我建议只观察一个指标:
集成回退率
计算方式很简单:
最近一段时间,AI完成并且本地验证通过的任务中,有多少在合并、集成测试或后续环境中还需要返工?
例如最近20次Codex修改:
15次直接合并正常。
5次虽然本地Tests Passed,但合并后又需要重新修改。
那么集成回退率就是:
25%。
这个数字非常有意义。
低于10%
说明你的:
任务边界。
测试体系。
项目约束。
AI使用方式。
已经比较成熟。
AI产出的结果大多数能够稳定进入后续流程。
10%—25%
说明局部验证还不错,但集成约束没有完全被系统化。
这时候最值得做的是补齐:
集成测试。
影响面分析。
业务约束。
高于25%
如果大量AI修改都是:
本地绿 → 合并出问题 → 再返工,
那么瓶颈已经不是AI写代码的能力。
而是工程验证体系本身。
这时候继续单纯提高AI使用量,只会更快地产生更多需要返工的代码。
十一、Plus和Pro应该怎么判断?
这个问题最后其实可以直接根据“集成回退率”判断。
如果你的集成回退率还比较高,例如20%、30%甚至更高:
说明现在最大问题不是任务跑得不够多。
而是AI任务进入真实项目以后还不够稳定。
这种阶段更值得先做的是:
缩小任务范围。
补集成测试。
写清业务约束。
要求AI输出影响面。
完善验证Workflow。
这类情况下,Plus通常已经足够你把流程打磨成熟。
因为即使增加更多AI容量,也很可能只是制造更多返工。
如果你的集成回退率已经很低:
大部分Codex任务都能够稳定完成。
测试可信。
影响面可控。
合并以后很少返工。
但你开始经常遇到:
多个长任务排队。
高频调用。
项目上下文很大。
一天需要连续处理很多独立开发任务。
这时候AI侧容量才真正开始成为限制。
到了这个阶段,Pro带来的更高使用强度才更容易直接转化成开发吞吐量。
换句话说:
先让AI产出稳定,再扩大AI产能。
这个顺序通常比一开始就追求更高容量更重要。
最后
以后用ChatGPT、Codex做真实开发,有一句话我觉得会越来越重要:
测试通过,不等于任务真正完成。
真正可靠的修改至少要同时满足:
代码逻辑正确。
测试结果可信。
集成关系没有破坏。
业务约束仍然成立。
风险可以接受。
AI越来越擅长完成前面的“写代码”和“跑测试”。
但后面的系统性验证,反而会越来越成为开发者真正需要管理的部分。
所以当你下一次看到:
All Tests Passed
不要只问:
“是不是可以合并了?”
更应该再问一句:
这些测试,到底证明了什么?又有什么东西,它其实还没有证明?
这往往才是从“AI能写代码”,走到“AI能稳定参与真实工程”的关键一步。
持续分享 Codex、大模型开发与 AI 编程实战内容。
长期深度使用各类代码大模型,也整理了稳定的Plus/Pro会员订阅渠道,有需要可自取。
更多推荐

所有评论(0)