深色模式
Codex:让它直接改你的代码
和「问它一段代码」不是一回事。Codex 能读你的仓库、改多个文件、跑测试、提交改动——所以它的风险也大得多。
它和聊天有什么区别
在对话框里问「写一个解析 CSV 的函数」,它给你一段代码,你自己复制到文件里、自己接上、自己调。
Codex 的做法是反过来:你把任务交给它,它在你的代码库里自己找该改的地方,改完运行测试,然后交回来一份可审阅的改动。它的产出不是一个片段,是一次提交。
这个区别决定了你该怎么用它,也决定了你要多小心。片段写错了你自己会发现;跨十个文件的改动,你看漏一处就可能上线出问题。
两种形态,分工不同
| 网页端 | 命令行 / 编辑器内 | |
|---|---|---|
| 适合 | 一次性的、成块的、能并行派出去的任务 | 手头正在改的代码,需要来回沟通 |
| 你怎么给上下文 | 连接仓库,它在自己的环境里检出代码 | 它就在你的工作目录里,直接看文件 |
| 产出 | 一个分支或一个拉取请求 | 直接改你本地的文件 |
| 你的介入方式 | 异步:派出去,做别的事,回来看结果 | 同步:边看边纠正 |
| 风险 | 改动量大,容易审不仔细 | 可能动到你还没提交的工作 |
实际用起来通常是配合的:大块的、机械的任务(批量升级依赖、补测试、迁移一个模块)派到网页端并行跑;正在手上的逻辑改动用命令行,因为你要边看边调。
先看看它的说明
Codex 的形态、可用模型、权限范围一直在调整。开始之前花两分钟看一眼官网的说明页,比自己猜哪些能力开放了要准。
任务怎么描述
能用一句话说清的任务,往往不是好任务。原因和提示词那一篇一样:它需要猜的东西太多,就会往自己熟悉的方向改。
一个能用的任务描述
目标:把 src/api/ 下面所有接口的错误处理统一起来。
现状:现在每个 handler 里各写各的 try/catch,返回的错误格式不一样,
有的返回 { error: "..." },有的直接抛 500。
要求:
- 统一走 lib/errors.ts 里已有的 AppError
- 保持现有的 HTTP 状态码不变(不要顺手改业务语义)
- 不改动 src/api/ 以外的文件
- 补齐对应的单元测试
判断完成的标准:npm test 全绿,并且 grep 不到裸的 try/catch 在 src/api/ 下。这段里有四样东西,缺一样结果就会走偏:
目标是一句可判断的话 「统一错误处理」是模糊的;「全部走 AppError」是可判断的。
现状说清楚 不描述现状,它会假设代码是按最佳实践写的,然后照着假设改。
边界写明白 「不改动 src/api/ 以外的文件」这句话能挡掉大量顺手重构。不写的话,它很可能会「顺便优化」几个你没打算动的模块。
给一个可验证的完成标准 测试通过、grep 不到某种写法——这类标准它自己能检查,你也能自己验证。
怎么审它交回来的东西
这一步比写任务描述更重要。一个看起来整洁的改动,可能藏着你没注意到的行为变化。
先看改动范围,再看代码
打开改动列表,先只问一个问题:这些文件是不是都在我预期的范围内? 出现了意料之外的文件,先搞清楚为什么,再往下看。多改的文件往往意味着它误解了任务。
然后按这个顺序读
接口和数据结构的变化 函数签名改了没有、返回的字段变没变、数据库结构动没动。这些影响面最大。
错误处理 它倾向于把错误吞掉让测试变绿。检查有没有被悄悄加上的
catch {}、被放宽的断言、被跳过的测试。测试本身 新增的测试是真在验证行为,还是只验证「函数被调用了」?被修改过的已有测试要特别警惕——改测试让它通过,是这类工具最常见的取巧方式。
最后才是实现细节 命名、风格、注释。这些错了不影响正确性,放在最后看。
几个具体的检查动作
bash
# 有没有被跳过的测试
git diff | grep -n "skip\|only\|xit\|xdescribe"
# 有没有被放宽的断言
git diff | grep -n "toBeDefined\|not\.toThrow\|\.only"
# 有没有依赖被偷偷加上
git diff -- package.json requirements.txt go.mod三种最常见的翻车方式
一、它改的是测试,不是代码
测试跑不过,最省力的办法是改测试。它没有恶意,只是在最小化「让测试通过」这件事的代价。所以:看到已有测试被修改,默认先怀疑,逐行问为什么必须改。
二、它顺手重构了不该动的东西
你让它加一个字段,它顺便把整个模块拆了、改了命名、换了写法。功能上也许没错,但 review 成本暴涨,合并冲突也跟着来。对策是在任务里写死范围,并且在派任务时明确:「遇到范围外的问题,列出来告诉我,不要直接改。」
三、它在猜你的业务逻辑
代码里没有的信息,它会按「通常怎么做」补上。金额怎么取整、时区怎么处理、空值算什么、权限怎么判——这些在真实业务里每条都有讲究,而它只能猜。所以:涉及业务规则的地方,必须在任务里写清楚,或者明确告诉它「这部分留 TODO,不要自己决定」。
用之前该做的准备
- 在干净的分支上跑。 本地有未提交的改动时,它可能改到你的半成品。先提交或 stash。
- 确保测试能跑。 它靠测试判断自己做得对不对。测试本身是坏的,它就会沿着错误方向一路走。
- 给一个能回退的起点。 用完不满意,要能干净地丢掉全部改动。
- 看一眼它需要的权限。 能执行命令、能访问网络、能读哪些目录——按最小权限给。
- 别把密钥放进去。 它读得到的东西,就可能在输出里带上。用环境变量,别把 token 写进代码。
适合交给它的任务
不是所有任务都值得派给 Codex。判断标准:验收标准能不能用机器检查。
很适合
- 补测试:给已有函数补单元测试,覆盖率能直接看出来。
- 机械迁移:把一类调用换成另一类,模式固定、可搜索、可验证。
- 依赖升级:跟着变更日志改代码,编译和测试会告诉你漏了什么。
- 边界明确的 bug:有复现步骤、有预期行为、能写成一个失败的测试。
- 写文档和注释:从代码反推说明,风险低。
要小心
- 涉及并发、事务、缓存的改动——测试不一定能暴露问题,线上才会。
- 性能优化:它改完可能更快也可能更慢,而测试不测性能。
- 业务规则变更:判断标准在人的脑子里,不在测试里。
- 没有测试覆盖的老代码:它改完之后没有任何东西能告诉你对不对。
一个可以照着走的流程
先把任务写成一段话,自己读一遍 如果这段话里还有「大概」「类似那样」这种词,说明你还没想清楚,先想清楚再派。
在干净分支上派出去 网页端会自己开分支;命令行先确认工作区干净。
回来先看范围,再看代码 按上面那个顺序读。范围不对就直接打回,不用细看。
自己跑一遍测试,别只信它的报告 它的报告说「全部通过」不等于在你机器上也通过。
合并前做一次自己的改动 哪怕只改一个变量名。强迫自己动一下,你会更容易发现没读懂的地方。
一句话总结
Codex 把「写」的成本降得很低,但「判断对不对」的成本一点没降。所以它的价值取决于你有没有测试、有没有清晰的验收标准——这两样没有的话,它只是让你更快地写出更多你不确定的代码。