Codex 改写老代码实战:Legacy 项目升级 1 周搞定
Codex 改写老代码怎么操作?讲透 Codex 重构 Legacy 项目的 6 步法、PLANS.md 模板、批量改造节奏,含真实命令和踩坑速查
30 秒了解:Codex 改写老代码到底强在哪
Codex 改写老代码这件事,最适合的场景是「重复模式特别多、人工改无聊但容易出错」的批量改造——比如 callback 改 async/await、class 组件改 Hooks、Java 8 升 Java 21、Python 2 残留代码全清。这类活给人做累且枯燥,给 Codex 做正好——AI 不烦、模式识别强、能批量保证一致性。
但「让 AI 改老代码」也是 AI 编程里最容易翻车的场景之一:它会自作主张改无关地方、会丢测试覆盖、会偷偷改业务逻辑。关键不在 AI 多强,而在你拿它的节奏对不对。
这篇按 OpenAI cookbook 的 code modernization 例子 + 实际项目经验,给你一份「Codex 改老代码 6 步法」。完整跑下来一个 2-3 万行的 legacy 模块大概 1 周能升级完。
如果你还没用过 Codex CLI,先看 Codex CLI 怎么用 再回来。
准备工作:先确认 3 件事
1. 你的项目得有测试
没测试别让 Codex 改老代码。 Codex 不会自己造测试覆盖率,它只会改代码——改对没改对你怎么知道?
如果测试覆盖率低于 40%,先用 Codex 帮你补测试再开始重构。让 AI 改 untested code 是给自己挖坑。
2. git 状态干净
每次让 Codex 改之前必须 git status 干净。理由:改坏了 git reset --hard HEAD 一键回来。
3. 改造方向有明确目标
不要「让代码现代化一点」这种模糊指令。要:
- ❌ 「重构一下这个模块」
- ✅ 「把 src/api/user/ 下所有 callback API 改成 async/await,外部签名不变」
模糊目标 = AI 自由发挥 = 你修不完。
第 1 步:先用 grep 摸清「改造规模」
让 Codex 帮你扫描,但别让它一上来就改:
任务:评估 src/api/legacy/ 下 callback 风格 API 的改造规模
请按以下步骤做:
- 用 grep -rn “function.*callback” src/api/legacy/ 找出所有可能的 callback 风格函数
- 把结果按文件聚合,列一个表:文件路径 / callback 函数数量
- 找 3 个有代表性的文件,每个完整读一遍
- 估算总改造规模:总文件数 / 总函数数 / 复杂度(简单 / 中等 / 复杂)
- 给我 3 个分批方案:按文件分 / 按模块分 / 按复杂度分
不要改任何代码。只输出评估报告。
跑完你会拿到一份「这次改造大概多大、怎么分批合理」的清单。
第 2 步:写 PLANS.md 把任务拆 N 步
Codex 改老代码最致命的失败模式是「一次改太多,跑测试一片红,分不清哪里错了」。用 PLANS.md 强制分小步:
# 任务:src/api/legacy/ callback → async/await 改造
## 总体目标
- 改造范围:src/api/legacy/ 下 25 个文件
- 不变事项:外部函数签名、错误处理语义、测试通过率
- 完成标准:所有原测试绿 + 至少 3 个新增 async 测试
## 分批计划
### 批次 1:3 个最简单的(独立、无依赖)
- [ ] src/api/legacy/health.ts
- [ ] src/api/legacy/version.ts
- [ ] src/api/legacy/ping.ts
### 批次 2:5 个用户相关
- [ ] src/api/legacy/user/get.ts
- [ ] src/api/legacy/user/list.ts
- [ ] src/api/legacy/user/create.ts
- [ ] src/api/legacy/user/update.ts
- [ ] src/api/legacy/user/delete.ts
### 批次 3-5:...(同模式)
## 每批的标准流程
1. git checkout -b refactor/batch-N
2. Codex 改文件
3. pnpm test 必须绿
4. git commit -m "refactor: batch N - ..."
5. 我手动 review 一遍 diff
6. merge 回主干,进下一批
## 不要做的事
- 不要顺手「优化」逻辑(改 callback 就只改 callback)
- 不要重命名变量
- 不要改注释(除非注释错了)
- 不要碰 src/api/legacy/ 之外的文件
PLANS.md 是 Codex 长任务的核心抓手,详见 Codex PLANS.md 写法(待发布)。
第 3 步:让 Codex 跑第一批,先看 diff 不写文件
第一批是最关键的——AI 的「改法」要在第一批就定调。别一开始就允许写文件:
现在开始 PLANS.md 的批次 1:3 个简单 callback → async/await 改造
要求:
- 一次只改 1 个文件
- 改之前先把 diff 贴给我审,不要写文件
- 我说 OK 你再写
- 写完跑 pnpm test 验证只跑相关测试不全跑
- 测试绿 → git commit -m “refactor: …”
- 进下一个文件,重复
第 1 个:src/api/legacy/health.ts,请开始。
第一个文件 review 仔细——这次 AI 的改法决定了后面 24 个文件的标准。如果 AI 偷偷改了无关代码、变量名、注释,马上让它重做,并把规则加进 PLANS.md。
第 4 步:批次间「校准」一次
每 5 个文件让 AI 自查一次,确认走在正轨:
批次 1 已完成 3 个文件。在进入批次 2 之前做一次自查:
- 跑 git log —oneline -10 列出这批的 commit
- 跑 git diff HEAD~3 src/api/legacy/ 看完整改动
- 自我审计:
- 有没有改 callback 之外的东西?
- 函数外部签名是不是真的没变?
- 错误处理语义是不是保留了?
- 跑 pnpm test 完整一遍确认全绿
- 如果发现任何问题,说出来等我决定要不要回滚
通过自查再进批次 2。
这种「中间校准」省下后面发现大问题再翻车的成本。
第 5 步:测试挂了怎么排查
改批量代码最常见的卡点是「某个文件改完测试挂了」。给 Codex 一个排查模板:
批次 2 的 src/api/legacy/user/update.ts 改完测试挂了。报错:
[贴报错完整内容]
请按这个顺序排查:
- 看完整报错堆栈(不要只看第一行)
- git diff src/api/legacy/user/update.ts 看你刚才的改动
- 读原文件历史版本 git show HEAD~1:src/api/legacy/user/update.ts
- 形成 3 个假设:
- 假设 A:可能是 X
- 假设 B:可能是 Y
- 假设 C:可能是 Z
- 按可能性排序,给我看,等我决定方向
别上来就改回去。
「先诊断再开药」的 prompt 模式在 Claude Code 高级技巧 那篇也有更详细的版本。
第 6 步:全部完成后让 Codex 写「升级报告」
收尾很重要。让 AI 自己总结:
PLANS.md 的所有批次已完成。请写一份「升级完成报告」:
- 总览:改造了多少文件 / 多少函数 / 总 diff 行数
- 测试结果:升级前 vs 升级后的测试通过率、覆盖率变化
- 风险点:哪些改动你不 100% 有把握,标出来要团队额外 review
- 后续建议:基于看到的代码,建议接下来该清理或重构什么
格式:markdown,5-10 节,发出去能直接当 PR description 用。
这份报告贴进 GitHub PR description,团队 review 时一目了然。
5 个真实场景的 prompt 模板
场景 1:Java 8 → Java 21
任务:把这个 Java 8 模块升级到 Java 21
改造范围:
- 匿名内部类 → lambda
- 旧 stream 用法 → toList()
- Date → java.time.LocalDateTime
- @Nullable 注解统一用 jspecify
不要碰:业务逻辑、外部 API 签名、配置文件
场景 2:React class 组件 → Hooks
任务:src/components/legacy/ 下所有 class 组件改成函数组件 + Hooks
映射规则:
- componentDidMount → useEffect(() => , [])
- componentDidUpdate → useEffect(() => , [deps])
- this.state → useState
- this.setState → setState
- this.props → 函数参数解构
不要:改 props 接口、改 CSS、改测试除非必须
场景 3:Python 2 残留 → Python 3.12
任务:清理 lib/legacy_py2/ 里的 Python 2 残留
清理项:
- print 语句 → print() 函数
- xrange → range
- dict.iteritems → dict.items
- unicode → str
- raise X, msg → raise X(msg)
跑 pytest 必须保持绿
场景 4:jQuery → 原生 DOM
任务:把 web/legacy.js 里的 jQuery 调用改成原生 DOM API
映射规则:
- $(‘#id’) → document.getElementById
- $(el).addClass(x) → el.classList.add(x)
- $.ajax → fetch
- $(el).on(‘click’, …) → el.addEventListener
改完 web 端手动验证 5 个主要交互
场景 5:旧 CSS 改 Tailwind
任务:components/Card/ 的 .css 文件改成 Tailwind 类
要求:
- 视觉效果不变(颜色、间距、字号、圆角都要对上)
- 改完删原 .css 文件,从 import 里也删
- 如果有响应式断点,用 sm: md: lg: 对应
- 不知道怎么映射的颜色,先查 tailwind.config.js 自定义色
每个文件改完截图前后对比给我看
常见坑 + 解决办法
| 现象 | 原因 | 解决 |
|---|---|---|
| AI 顺手改了无关代码 | PLANS.md 没写「不要做的事」 | 加进 PLANS.md,git checkout 回滚 |
| 改完测试挂一片 | 一次改太多文件 | 分小批,每批 3-5 文件 |
| AI 「优化」了业务逻辑 | prompt 没说「只改 X 不改 Y」 | 明确分离改造维度 |
| diff 看着对,跑起来错 | 测试覆盖不够 | 先补测试再重构 |
| Codex 上下文塞满了 | 改造文件太多累计 | 定期 /compact 或 /clear |
改老代码 vs 从零写新代码
| 维度 | 改老代码 | 写新代码 |
|---|---|---|
| AI 友好度 | 高(模式重复多) | 中(需要架构判断) |
| 风险 | 中(有测试兜底) | 高(错的设计可能流到生产) |
| 速度提升 | 10-50 倍 | 2-5 倍 |
| 适合 AI 包办 | ✅ | ❌(人定方向 AI 写细节) |
对比详情看 AI 编程趋势。
下一步
- Codex 整体介绍 → Codex 是什么
- Codex CLI 上手 → Codex CLI 怎么用
- Codex 自动修 CI → Codex 修 CI(待发布)
- Codex 长任务规划 → Codex PLANS.md 怎么写(待发布)
- 用 Claude Code 改老代码 → Claude Code 怎么用
- AI 编程入门 → AI 编程入门
- 全景 → AI 写代码完全指南
常见问题
Q:Codex 改老代码会不会改坏业务逻辑? A:会,所以铁律是「先 commit 当前状态、看 diff 再批、跑测试验证」。这三步执行到位,最坏也就是回滚一次的成本。最大的风险是「测试覆盖不够」——AI 改对了你也不知道是不是真的对。
Q:一个 10 万行的 legacy 项目,Codex 能搞定吗? A:能,但要按模块拆。一次给它「重构整个项目」必败。拆成 20-50 个独立模块,每个模块按本文 6 步法走,1-2 个月能搞完。
Q:Codex 跟人工改老代码相比,质量怎么样? A:模式重复的场景(callback → async)Codex 比人工还稳,因为不会累、不会漏。但「业务逻辑也要顺手优化」这种场景必须人主导——AI 不懂你的业务语境。
Q:能用 Codex 改没看过的开源项目吗? A:能。开源项目通常测试覆盖率不错,给 Codex 一份贡献者文档 + AGENTS.md 描述项目约定,它能干得不比新贡献者差。但 PR 描述自己写,别让 AI 编。
Q:跟 Claude Code 比,改老代码哪个强? A:两个都行,差异主要在「你已经付了谁的钱」。Claude 在复杂代码理解上略占优,Codex 在批量模式改造的速度上略占优。手头有哪个用哪个,没必要为这个换工具。