🤖 AI 跟我学 新手入门

Codex 改写老代码实战:Legacy 项目升级 1 周搞定

Codex 改写老代码怎么操作?讲透 Codex 重构 Legacy 项目的 6 步法、PLANS.md 模板、批量改造节奏,含真实命令和踩坑速查

发布 2026/05/19 📎 参考官方文档

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 帮你扫描,但别让它一上来就改

📋 Prompt 模板

任务:评估 src/api/legacy/ 下 callback 风格 API 的改造规模

请按以下步骤做:

  1. 用 grep -rn “function.*callback” src/api/legacy/ 找出所有可能的 callback 风格函数
  2. 把结果按文件聚合,列一个表:文件路径 / callback 函数数量
  3. 找 3 个有代表性的文件,每个完整读一遍
  4. 估算总改造规模:总文件数 / 总函数数 / 复杂度(简单 / 中等 / 复杂)
  5. 给我 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 的「改法」要在第一批就定调。别一开始就允许写文件

📋 Prompt 模板

现在开始 PLANS.md 的批次 1:3 个简单 callback → async/await 改造

要求:

  1. 一次只改 1 个文件
  2. 改之前先把 diff 贴给我审,不要写文件
  3. 我说 OK 你再写
  4. 写完跑 pnpm test 验证只跑相关测试不全跑
  5. 测试绿 → git commit -m “refactor: …”
  6. 进下一个文件,重复

第 1 个:src/api/legacy/health.ts,请开始。

第一个文件 review 仔细——这次 AI 的改法决定了后面 24 个文件的标准。如果 AI 偷偷改了无关代码、变量名、注释,马上让它重做,并把规则加进 PLANS.md。

第 4 步:批次间「校准」一次

每 5 个文件让 AI 自查一次,确认走在正轨:

📋 Prompt 模板

批次 1 已完成 3 个文件。在进入批次 2 之前做一次自查:

  1. 跑 git log —oneline -10 列出这批的 commit
  2. 跑 git diff HEAD~3 src/api/legacy/ 看完整改动
  3. 自我审计:
    • 有没有改 callback 之外的东西?
    • 函数外部签名是不是真的没变?
    • 错误处理语义是不是保留了?
  4. 跑 pnpm test 完整一遍确认全绿
  5. 如果发现任何问题,说出来等我决定要不要回滚

通过自查再进批次 2。

这种「中间校准」省下后面发现大问题再翻车的成本。

第 5 步:测试挂了怎么排查

改批量代码最常见的卡点是「某个文件改完测试挂了」。给 Codex 一个排查模板:

📋 Prompt 模板

批次 2 的 src/api/legacy/user/update.ts 改完测试挂了。报错:

[贴报错完整内容]

请按这个顺序排查:

  1. 看完整报错堆栈(不要只看第一行)
  2. git diff src/api/legacy/user/update.ts 看你刚才的改动
  3. 读原文件历史版本 git show HEAD~1:src/api/legacy/user/update.ts
  4. 形成 3 个假设:
    • 假设 A:可能是 X
    • 假设 B:可能是 Y
    • 假设 C:可能是 Z
  5. 按可能性排序,给我看,等我决定方向

别上来就改回去。

「先诊断再开药」的 prompt 模式在 Claude Code 高级技巧 那篇也有更详细的版本。

第 6 步:全部完成后让 Codex 写「升级报告」

收尾很重要。让 AI 自己总结:

📋 Prompt 模板

PLANS.md 的所有批次已完成。请写一份「升级完成报告」:

  1. 总览:改造了多少文件 / 多少函数 / 总 diff 行数
  2. 测试结果:升级前 vs 升级后的测试通过率、覆盖率变化
  3. 风险点:哪些改动你不 100% 有把握,标出来要团队额外 review
  4. 后续建议:基于看到的代码,建议接下来该清理或重构什么

格式:markdown,5-10 节,发出去能直接当 PR description 用。

这份报告贴进 GitHub PR description,团队 review 时一目了然。

5 个真实场景的 prompt 模板

场景 1:Java 8 → Java 21

📋 Prompt 模板

任务:把这个 Java 8 模块升级到 Java 21

改造范围:

  • 匿名内部类 → lambda
  • 旧 stream 用法 → toList()
  • Date → java.time.LocalDateTime
  • @Nullable 注解统一用 jspecify

不要碰:业务逻辑、外部 API 签名、配置文件

场景 2:React class 组件 → Hooks

📋 Prompt 模板

任务: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

📋 Prompt 模板

任务:清理 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

📋 Prompt 模板

任务:把 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

📋 Prompt 模板

任务: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 编程趋势

下一步

常见问题

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 在批量模式改造的速度上略占优。手头有哪个用哪个,没必要为这个换工具。