用 AI 复现缺陷:从报错到最小复现

「偶尔报错」「压测时才出现」「重启就好了」——这类缺陷不是靠读代码猜出来的,而是靠逐步缩小范围逼出来的。AI 在这条链路里的价值很具体:把它当成一个有耐心的假设生成器,让它把「可能的原因」穷举出来,但每一次验证都必须由你亲自跑出来。

第一步:先把事实固定下来

动手之前先保住现场:日志、版本、配置、请求参数、时间窗口、同期变更。喂给 AI 的材料质量,直接决定它给出的假设有没有用。

【错误】贴完整栈,不要只贴最后一行;带上错误码与关键变量的值
【环境】Go 1.22.3、PostgreSQL 16、依赖 X v1.3.2、容器 2C4G、并发 50
【复现步骤】1) …… 2) …… 3) ……;标注是否稳定复现,大约几次出现一次
【期望 vs 实际】期望产生一条退款流水,实际产生两条
【日志片段】保留请求 ID 与前后 20 行,去掉无关的中间件噪声
【最近改动】git log --oneline -10 的输出,以及可疑提交的 diff

第二步:要假设清单,不要修复方案

最容易被跳过、也最容易踩坑的一步:直接问「怎么修」,它会给你一段看起来合理的补丁,而你没有验证过任何东西,真因也还在。

下面是线上报错与相关代码。规则:
1) 先只列根因假设,每条写成一行:假设 | 支持证据(引用日志原文)| 能证伪它的最小实验 | 该实验的预期输出与失败输出,按可能性排序,最多 5 条;
2) 这一步不要给修复代码,也不要一次给多个假设的补丁;
3) 严格区分「日志里已经出现的事实」与「你的推断」,推断要标注置信度,并说明什么信息能让置信度上升;
4) 指出完成复现我还缺什么(版本、配置、数据形态、并发条件、缓存状态);
5) 最后给出一个最小复现骨架:单文件、单命令、不依赖外网与真实数据,失败时打印关键中间变量。
【报错/代码/复现步骤/环境】

拿到假设清单后逐条做实验,一次只改一个变量——同时改三处再跑,无论结果如何你都不知道是哪一处起了作用。

第三步:两种二分定位

手段做法适合成本
输入二分把触发数据或参数集合对半砍,看错误是否还在,只保留仍然失败的那一半数据相关、批量相关每次迭代要能重跑
提交二分git bisect 加自动化判据,定位引入缺陷的提交曾经正常、最近才坏需要一个确定性的判据
依赖二分逐个回退第三方库或基础镜像版本升级后才出现依赖多时迭代慢
环境二分对比两套配置、两台机器的差异(缓存、连接池、超时、DNS)只在某个环境复现需要保留原始现场
# 提交二分:run 的退出码 0=good、非 0=bad、125=跳过(判据不稳定时用 125)
git bisect start
git bisect bad HEAD
git bisect good v1.8.0
git bisect run go test ./internal/order -run TestRefundIdempotent -count=1
git bisect reset

# 输入二分:每次砍一半,保留仍然触发错误的那一半
total=$(wc -l < data.jsonl); head -n $((total / 2)) data.jsonl > half.jsonl
go run ./cmd/repro -in half.jsonl   # 仍失败 → 目标在前半段;否则在后半段

git bisect run 的前提是判据确定:偶发用例会让它一路给出错误答案,此时先用退出码 125 跳过不稳定提交,或把判据换成「日志里是否出现某个错误码」。

第四步:什么算最小复现

最小复现(Minimal Reproduction)的验收标准是可检查的,不是「感觉差不多了」:

检查项合格标准
复现方式单文件、一条命令、30 秒内跑完
外部依赖无外网、无私有数据、无手工前置步骤
稳定性连续 20 次复现同一错误;偶发问题要标出出现概率
最小性删掉其中任何一行,错误就不再出现
断言精度断言具体错误类型/消息/错误码,而不是「报错了」
可回归已固化为测试,并验证过「修复前红、修复后绿」

第五步:固化成回归测试

复现的价值一半在「找到原因」,另一半在「不让它回来」。

把上面的最小复现固化为回归测试:
1) 测试名包含 issue 编号与行为描述,例如 TestRefundTwice_OnlyOneRefund_Issue2143;
2) 断言具体的错误/状态,不要断言「返回了错误」;
3) 内联最小数据,不依赖外部库与执行顺序;
4) 先在当前代码上运行,确认它确实是红的;如果需要改动被测代码才能跑起来,先说明改了什么、为什么;
5) 给出验证步骤的命令:删掉修复代码应当变红,恢复后应变绿(命令请写全)。

常见坑

坑后果规避
直接问「怎么修」拿到未经验证的补丁,真因仍在先要假设清单,再逐条实验
一次改多处无法归因,问题反复一次只改一个变量
把 AI 的推断当事实修错地方,浪费一天要求标注事实与推断,推断必须实验证伪
没保现场就重启服务日志与内存状态丢失,只能等下次复现先留证据再操作
断言「有错误」就算复现换成别的错误也算通过断言具体类型、消息或错误码
拿偶发问题做二分bisect 结论错误判据不确定时用退出码 125 跳过
复现依赖线上数据本地无法重跑,无法回归脱敏后内联成最小样本

小结:从报错到最小复现是一条收敛链路——固定事实、要假设清单、一次验证一个假设、用输入二分或提交二分缩小范围,最后用「删掉任何一行就不再复现」验收最小性,并固化成一个修复前必红的回归测试;AI 负责穷举假设与生成骨架,而「哪个假设成立」只能由运行结果回答。

笔记加载中…