Codex 修复 Bug 总是反复失败?先学会制作最小复现
摘要
很多开发者把报错日志发给 Codex 后,会遇到一种情况:代码改了一轮又一轮,当前报错暂时消失,却很快出现新的问题。原因往往不是 Codex 不会写代码,而是任务缺少稳定的复现条件。本文介绍如何整理最小复现、完整错误日志、输入数据和验收标准,让 Codex 更准确地定位根因,并通过测试验证修复结果。
使用 Codex 修复项目 Bug 时,很多人会直接输入:
页面报错了,帮我修一下。或者只提供一行日志:
TypeError: Cannot read properties of undefinedCodex 虽然可以根据错误信息推测原因,但在缺少项目上下文的情况下,通常只能做概率判断。
它可能会:
增加可选链;
添加空值判断;
使用默认值;
包一层
try...catch;修改数据初始化逻辑。
这些修改可能让报错暂时消失,却不一定解决真正的问题。
要让 Codex 稳定修复 Bug,第一步不是让它马上改代码,而是建立一个能够重复出现问题的最小复现。
一、什么是最小复现?
最小复现可以理解为:
用尽可能少的代码、数据和操作步骤,稳定触发同一个问题。
它需要回答四个问题:
在什么条件下发生;
用户做了什么操作;
实际出现什么结果;
正确结果应该是什么。
例如,一个订单详情页偶尔出现空白。
模糊描述是:
订单页面有时候打不开。更有效的复现描述是:
前置条件: 订单来自旧版系统,并且收货地址字段为 null。 操作步骤: 1. 登录后台; 2. 进入订单列表; 3. 打开一条历史订单; 4. 查看订单详情。 实际结果: 页面报错,详情区域无法显示。 错误日志: TypeError: Cannot read properties of null at OrderAddress.vue:42 期望结果: 没有地址时显示“暂无收货地址”,订单其他信息正常展示。这样的信息可以帮助 Codex 快速缩小问题范围。
二、为什么没有最小复现就容易改错?
1. 同一个错误可能有多个原因
例如:
Cannot read properties of undefined可能来自:
接口没有返回字段;
数据还没有加载完成;
数组中找不到对应元素;
字段名称发生变化;
状态初始化错误;
权限不足导致接口返回异常;
页面提前执行了计算逻辑。
如果只看错误名称,Codex 很难知道是哪一种情况。
2. 表面问题不一定是根本原因
例如页面读取:
order.address.name最直接的修复可能是:
order.address?.name但真正原因可能是:
后端不应该返回空地址;
历史订单的数据结构不同;
页面没有等待请求完成;
类型定义错误地把地址标记为必填。
如果只加可选链,页面不报错了,但数据问题仍然存在。
3. 无法判断是否真正修复
如果问题无法稳定复现,修改之后也无法确认:
Bug 是否真的消失;
只是当前数据没有触发;
是否影响正常数据;
是否出现新的边界问题。
所以,修复 Bug 的第一步应该是“先证明问题存在”。
三、整理一份完整的 Bug 输入包
给 Codex 提交 Bug 时,建议至少提供以下内容。
1. 问题现象
说明用户看到的结果。
用户打开历史订单详情后,页面显示空白。2. 完整错误日志
不要只复制最后一行。
应该包括:
错误类型;
完整堆栈;
文件位置;
行号;
请求状态;
相关警告。
例如:
TypeError: Cannot read properties of null (reading 'name') at renderAddress (OrderAddress.vue:42) at setupRenderEffect3. 复现步骤
1. 使用测试账号登录; 2. 打开订单列表; 3. 筛选“历史订单”; 4. 打开订单编号 A10086; 5. 页面立即报错。4. 输入数据
如果问题与接口有关,最好提供脱敏后的返回示例:
{ "id": "A10086", "status": "completed", "address": null, "items": [ { "name": "测试商品", "quantity": 1 } ] }5. 期望结果
地址为空时显示“暂无收货地址”,商品和订单状态仍然正常展示。6. 影响范围
新订单正常,只有部分历史订单出现问题。这些信息组合起来,就是一份比较完整的 Bug 输入包。
四、先让 Codex 分析,不要立即修改
可以使用下面的提示词:
下面是一个项目 Bug。 问题现象: 历史订单地址为空时,订单详情页报错。 复现步骤: 1. 进入订单列表; 2. 打开地址字段为 null 的历史订单; 3. 页面无法正常渲染。 错误日志: [粘贴完整日志] 接口数据: [粘贴脱敏数据] 期望结果: 地址为空时显示占位信息,其他订单内容正常展示。 请先分析,不要修改代码。 输出: 1. 最可能的根本原因; 2. 支持该判断的证据; 3. 还需要检查哪些文件; 4. 如何建立最小复现; 5. 建议增加哪些测试; 6. 最小修复方案。这一步的重点是让 Codex建立原因假设,而不是马上开始试错。
五、让 Codex 按概率排列原因
复杂 Bug 可能存在多个可能原因。
可以进一步要求:
请按照高概率、中概率和低概率列出原因。 每个原因包含: - 原因描述; - 支持证据; - 验证方法; - 如果成立,需要修改的位置。例如:
高概率原因
历史订单的address字段允许为null,但前端类型和页面逻辑都按照必填处理。
中概率原因
订单数据请求尚未完成,组件已经提前渲染。
低概率原因
请求拦截器在部分状态下改变了返回数据结构。
有了优先级之后,开发者可以先验证最可能的原因,而不是同时修改多个模块。
六、先写失败测试,再修复代码
稳定的 Bug 修复流程应该是:
复现问题 ↓ 编写失败测试 ↓ 确认测试能够触发 Bug ↓ 修改代码 ↓ 确认测试通过 ↓ 运行完整回归测试例如可以补充一个测试:
it("地址为空时仍能正常显示订单详情", async () => { mockOrderDetail({ id: "A10086", address: null, status: "completed" }); render(OrderDetail); expect( await screen.findByText("暂无收货地址") ).toBeInTheDocument(); expect( screen.getByText("completed") ).toBeInTheDocument(); });这个测试不仅确认页面不会报错,还确认其他订单信息仍然正常显示。
七、修复时坚持最小修改原则
可以明确告诉 Codex:
请按照最小修改原则修复。 要求: 1. 只处理地址为空导致的页面异常; 2. 不重构整个订单详情模块; 3. 不改变后端接口字段; 4. 不修改路由和权限; 5. 不新增第三方依赖; 6. 保留正常订单的原有展示效果; 7. 补充对应回归测试。Bug 修复最怕范围扩大。
一个空值问题,不应该顺便变成:
重写订单类型;
调整所有页面组件;
修改接口返回结构;
更换状态管理方案;
全局格式化代码。
改动越小,验证和回滚越容易。
八、不要让 Codex 用隐藏错误的方式修复
下面这些修改需要特别警惕。
1. 吞掉异常
try { renderAddress(); } catch { // 忽略错误 }页面可能不报错了,但真实问题也被隐藏了。
2. 随意使用默认对象
const address = order.address || {};这可能导致后续代码得到一个结构不完整的对象。
3. 删除失败测试
为了让测试通过而删除用例,不属于修复。
4. 大量使用忽略注释
例如:
// @ts-ignore类型错误消失了,但风险仍然存在。
可以让 Codex自查:
请检查本次修改是否存在: 1. 吞掉异常; 2. 隐藏错误; 3. 使用不合理默认值; 4. 删除或降低测试断言; 5. 使用 ts-ignore 绕过类型检查; 6. 为了修复单个 Bug 扩大修改范围。九、修复后运行完整验证
不要只运行刚增加的一个测试。
建议至少执行:
npm run type-check npm run lint npm run test npm run build验证目标包括:
当前 Bug 测试通过;
正常订单测试仍然通过;
订单模块没有新增失败;
类型检查通过;
项目可以正常构建。
如果测试失败,不要直接输入“继续修”。
应该让 Codex先判断:
下面是测试失败日志。 请先分析: 1. 哪个测试失败; 2. 实际结果和期望结果分别是什么; 3. 是否由本次修改引起; 4. 应该修改业务代码还是测试代码; 5. 最小处理方案是什么。 先不要修改文件。十、检查 Git Diff
修复完成后运行:
git status git diff --stat git diff重点确认:
是否只修改相关文件;
是否存在无关格式化;
是否改变公共接口;
是否修改正常订单逻辑;
是否补充回归测试;
是否留下调试日志;
是否出现未经允许的新依赖。
也可以让 Codex进行一次修复审查:
请根据本次 Bug 和 Git Diff 进行审查。 重点检查: 1. 是否解决根本原因; 2. 是否只隐藏了错误; 3. 是否影响正常订单; 4. 是否覆盖历史数据; 5. 是否存在无关修改; 6. 测试是否真正有效; 7. 是否达到最小修改原则。十一、整理 Bug 修复报告
一个完整的修复任务,最后可以输出:
## 问题描述 历史订单地址字段为空时,详情页无法正常渲染。 ## 根本原因 前端类型将 address 定义为必填,页面也直接读取 address.name,但历史数据允许 address 为 null。 ## 修改内容 - 调整地址展示逻辑; - 地址为空时显示占位信息; - 保留正常订单展示; - 增加历史订单回归测试。 ## 验证结果 - 地址为空测试:通过 - 正常订单测试:通过 - 类型检查:通过 - 项目构建:通过 ## 影响范围 仅涉及订单详情页地址展示。 ## 后续建议 统一前后端对历史订单地址字段的类型定义。这份报告可以直接用于:
Pull Request;
Bug 记录;
团队同步;
测试验收;
后续故障复盘。
十二、可直接复用的 Bug 修复模板
任务目标: 修复[具体问题]。 问题现象: [用户看到的结果] 复现步骤: 1. 2. 3. 输入数据: [脱敏数据] 错误日志: [完整堆栈] 期望结果: [正确行为] 允许修改: [文件或目录] 禁止修改: [高风险或无关模块] 执行流程: 1. 先复现问题; 2. 分析根本原因; 3. 编写失败测试; 4. 给出最小修复方案; 5. 修改代码; 6. 运行相关测试; 7. 运行类型检查和构建; 8. 检查 Git Diff; 9. 输出修复报告。 禁止事项: - 不吞掉异常; - 不删除失败测试; - 不降低断言标准; - 不新增无关依赖; - 不修改无关模块。总结
Codex 修复 Bug 反复失败,很多时候不是因为代码能力不足,而是缺少稳定的复现条件。
更可靠的流程是:
先整理现象和日志,再建立最小复现;先写失败测试,再修改代码;先验证根本原因,再检查 Git Diff。
仅仅让报错消失,并不代表问题已经解决。
真正完成 Bug 修复,需要证明:
原问题可以稳定复现;
修改后问题不再出现;
正常功能没有被破坏;
测试和构建全部通过;
没有无关代码变化。
当复现条件、测试用例和验收标准都足够清楚时,Codex 才能减少反复试错,更稳定地参与真实项目维护。
CSDN 文章描述
Codex 修复 Bug 总是反复失败怎么办?本文介绍如何建立最小复现、整理错误日志、分析根本原因、编写失败测试,并通过 Git Diff 和完整验证确认修复结果。
推荐标签
CodexBug修复最小复现自动化测试AI编程
参考资料
Git 官方文档
TypeScript 官方文档
Vitest 官方文档
GitHub Code Review 官方文档
OpenAI Codex 使用文档
