Appearance
用 Codex 复现并修复 Bug
修 Bug 的核心不是“尽快改代码”,而是先得到一个能够稳定区分修复前后的失败证据。没有复现就修改,Codex 很可能修掉一个看起来可疑但与现象无关的位置。
准备一张问题卡
把信息整理成下面五项:
- 原始现象:保留准确错误文本、状态码或用户可见行为。
- 环境:版本、分支、操作系统、浏览器、数据条件。
- 复现步骤:从干净状态开始,逐步可执行。
- 期望与实际:不要只写“失败了”。
- 最近变化:相关提交、配置或依赖变化,未知就写未知。
日志先脱敏。Token、Cookie、邮箱、手机号、订单号和生产数据不应直接粘贴。
第一步:只复现,不修改
text
Bug:[原始错误或现象]
环境:[版本和必要条件]
复现步骤:
1. ...
2. ...
期望:[正确行为]
实际:[错误行为]
先不要修改。请实际执行最小复现,保存失败命令、状态码、堆栈或页面行为。然后沿真实调用链定位第一个错误状态产生的位置。把证据、推断和未验证项分开。如果无法复现,不要直接改:
text
当前未复现。请比较复现环境与本机环境,列出最可能缺失的前置条件,并给出下一轮最小采集方案。不要提交猜测性修复。第二步:缩小问题层级
让 Codex判断错误最先出现在哪一层:
- 输入或前端状态;
- 网络与协议;
- 路由和参数解析;
- 业务规则;
- 数据库或缓存;
- 外部服务;
- 并发、时序或重试;
- 配置与环境差异。
实用追问:
text
请找“第一个错误值或错误状态”产生的位置,而不是最后抛异常的位置。列出它进入下一层前的输入和输出。这能避免只在最外层加 try/catch 或默认值,掩盖真正原因。
第三步:先写失败验证
优先级从高到低:
- 现有测试能直接稳定失败;
- 补一个最小回归测试;
- 写一个临时复现脚本;
- 保存一条可重复的 HTTP 请求;
- 记录明确的 UI 操作和可观察结果。
提示:
text
在修改生产代码前,先添加或找到一个能稳定暴露该问题的最小验证。运行它并确认修复前确实失败。测试必须验证用户可见行为或真实数据变化,不要只测试内部实现细节。第四步:实施最小根因修复
text
根据已经确认的根因实施最小修复。
约束:
- 不改变无关公开接口;
- 不通过跳过校验、吞异常、延长固定等待或删除测试绕过;
- 不顺便重构相邻模块;
- 需要数据迁移或兼容分支时先说明;
- 保留失败时可诊断的信息,但不记录敏感数据。修改后先看:
bash
git diff --stat
git diff第五步:按证据阶梯验证
从最小到最大执行:
- 新增的失败测试现在通过;
- 同模块测试通过;
- 相关集成测试通过;
- 构建、类型检查和 lint 通过;
- 原始复现步骤不再失败;
- 相邻正常路径没有回归。
让 Codex用表格报告:
text
列出每项验证的命令、结果、耗时摘要和覆盖的风险。单独列出没有运行的检查及原因。第六步:检查修复是否只是偶然
对时序、缓存和并发 Bug,单次通过不够。可要求:
text
把最小复现连续运行 20 次,记录失败次数。不要通过无限重试掩盖失败;如果结果不稳定,继续定位共享状态、竞态或时间依赖。具体次数按测试成本调整。涉及外部付费 API 时不要盲目重复。
不同 Bug 的额外检查
数据错误
检查受影响数据范围、幂等性、旧数据修复和回滚脚本。先在副本或事务中验证。
权限错误
同时测试允许和拒绝路径,确认前端隐藏不等于后端授权。
性能退化
记录修复前后相同输入、相同环境和相同统计口径。一次主观体感不是性能证据。
UI Bug
检查桌面、移动端、键盘操作、loading、空状态和错误状态。截图对比只能证明外观,不能证明交互。
常见伪修复
- 捕获所有异常后返回成功;
- 把超时从 5 秒改成 60 秒;
- 删除失败测试;
- 用随机 sleep 解决竞态;
- 给空值补默认值,却不解释为什么为空;
- 只改错误消息,不改错误状态;
- 只在开发环境验证配置问题。
完成报告模板
text
根因:
[第一个错误状态在哪里产生,为什么]
修复:
[修改文件和行为]
回归测试:
[修复前怎样失败,修复后怎样通过]
验证:
- [命令/步骤]:[结果]
剩余风险:
[未覆盖环境、历史数据或外部依赖]下一步
如果问题修复需要新增完整能力,继续学习 用 Codex 开发一个完整功能。