为什么选 Bug 修复来测 Codex
代码补全写得好不算本事,能修对 Bug 才是真功夫。这是我最近两周的真实体会。
我手头有个跑了一年多的 Node.js 后端服务,技术栈不算老也不算新:Express + TypeScript + Prisma + Redis。趁着项目间隙,我翻出三个还没排期处理的真实 Bug,决定让 Codex 试试手。三个 Bug 覆盖了不同的麻烦程度:一个简单逻辑错误、一个边界条件遗漏、一个第三方库兼容性问题。我想看看 Codex 到底能扛到哪一层。
第一个 Bug:简单逻辑错误,Codex 一次过
现象:用户提现接口偶尔算出负数金额。
根因:某处折扣计算把discount和finalPrice搞反了,原代码大概长这样:
const finalPrice = order.total - discount; // 下面这行把变量用混了 const payout = discount > 0 ? finalPrice - discount : finalPrice;discount已经是扣掉的金额,第二次再减就错了。这种 Bug 肉眼扫过去很容易漏,但定位后修复 trivial。
Codex 的表现:我把报错日志、相关文件丢给它,说"提现金额出现负数,定位并修复"。Codex 读了两个文件,不到 30 秒给出修改:把第二处discount改成order.total - finalPrice,并补了一行注释说明计算逻辑。
结果:首次修复成功,编译通过,我补的单测也跑通了。
这个案例里 Codex 完全能独立解决。逻辑链条短、变量关系清晰,属于它的舒适区。
第二个 Bug:边界条件遗漏,Codex 修了但没修全
现象:分页查询在最后一页数据刚好填满pageSize时,前端会多渲染一个空状态。
根因:后端返回的hasMore判断只看了items.length === pageSize,没考虑总数据量刚好整除的情况。更隐蔽的是,这个接口还被另一个内部工具调用,那个场景需要hasMore为true来触发预加载。
Codex 的表现:我给了接口代码和前端报错截图。Codex 确实改对了主逻辑,把判断改成了items.length === pageSize && totalCount > pageSize * pageNum。但它在修改时没意识到内部工具的依赖,直接改了返回值语义。
人工 Review 时发现的陷阱:我差点就合并了。后来是看到它删掉了原有的一行注释,顺着调用链往上翻才发现还有个内部消费者。最后我保留了 Codex 的核心修改,但加了个兼容参数?legacyMode来避免 breaking change。
结论:Codex 能处理单一场景的边界条件,但跨调用链的副作用感知是它的盲区。这种 Bug 必须人工介入做影响面分析。
第三个 Bug:第三方库兼容性问题,Codex 彻底栽了
现象:升级ioredis从 v4 到 v5 后,连接池频繁报错Ready check failed。
根因:v5 默认禁用了enableReadyCheck,而项目里有个健康检查端点依赖这个行为。更麻烦的是,我们封装了一层 Redis 客户端,配置散落在三个文件里。
Codex 的表现:我给了package.json变更、报错堆栈和项目结构。Codex 的第一次尝试是在连接配置里加enableReadyCheck: true,但加错了位置——它改的是我们封装的 wrapper,而实际生效的是底层实例化参数。第二次尝试它开始乱试,甚至建议降级回 v4。
问题出在哪:Codex 对第三方库的 breaking change 有一定知识,但它搞不清我们项目里的封装层级。它读得懂单个文件的代码,却理不清"配置怎么从 wrapper 透传到实际驱动"这种架构层面的约定。这种需要深度上下文 + 领域经验的场景,Codex 基本在碰运气。
最终处理:我自己翻完ioredis的 migration guide,在正确的初始化位置加了配置,同时把散落的 Redis 配置收敛到一个文件——这部分重构 Codex 完全没提,但它其实是防止下次踩坑的关键。
三次修复的复盘对比
| 维度 | 简单逻辑错误 | 边界条件遗漏 | 第三方库兼容性 |
|---|---|---|---|
| 首次修复成功率 | ✅ 成功 | ⚠️ 部分成功 | ❌ 失败 |
| 引入新问题概率 | 0 | 高(未感知副作用) | 中(乱试方案) |
| Codex 独立解决能力 | 完全能 | 需人工补全 | 必须人工主导 |
| 人工 Review 重点 | 确认逻辑正确性 | 检查影响面 | 验证架构理解 |
开发者应该保持的审查策略
经过这三个 Bug,我总结了一套和 Codex 协作的底线:
第一,永远假设它没看全文件。Codex 的上下文窗口虽然不小,但大型项目里它未必能关联到所有相关文件。我现在的习惯是,关键修改必须自己grep一遍调用方,不能信它的"已确认无其他引用"。
第二,副作用比主逻辑更危险。Codex 修对主逻辑的概率其实不低,但它对"谁会依赖这个行为"毫无概念。边界条件、默认值、返回格式这些看似中性的改动,往往是埋雷点。
第三,第三方库升级类问题先查官方文档。Codex 的知识有截止日期,对最新版本的理解可能过时。而且 migration guide 里的"为什么这样改"比"怎么改"更重要,后者它能编,前者给不出。
第四,把 Codex 的修改当 PR 看,不是当答案收。我现在会刻意让它生成 diff 而不是直接应用,逐行过一遍再决定哪些要、哪些不要。这个仪式感虽然慢,但第二个 Bug 要是直接应用就麻烦了。
它到底能信几分
说实话,Codex 在简单逻辑修复上的表现确实让我省了不少机械劳动。但"能信"和"敢不审"是两回事。我的体感是:代码越接近纯算法、越远离业务上下文,Codex 越可靠;一旦涉及跨模块约定、第三方库版本差异、或者历史遗留的封装习惯,它的幻觉概率会陡增。
现在我的工作流变成了:让 Codex 先修,我来做那个挑刺的审查员。它出力气,我出判断力——这个分工目前看还算舒服,但前提是你得清楚哪些环节它真的靠不住。