Skip to content

用 Codex 做可回退的重构与迁移

嘿,朋友,来聊聊重构这个事儿。

你有没有过这种体验:让 Codex 一把梭把整个模块重构完,结果跑起来之后发现——登录挂了、接口返回变了、莫名其妙少了个字段。然后你盯着几千行的 diff,完全不知道是哪一步搞坏的。我踩过这个坑,不止一次。

核心问题其实就一句话:改动的行数多不可怕,可怕的是你没法证明"行为没变"。Codex 很能干,但你如果一次性把结构调整、行为改动和顺手优化全塞给它,它自己也分不清边界在哪。所以这一章我想跟你聊一套打法:分阶段迁移,每一步都可验证、可回滚


先把规矩说清楚:什么不能动、什么可以动

动手之前,先把边界写下来。Codex 需要你明确告诉它你的底线在哪,不然它很容易放飞自我。

示例:

text
目标:拆分 auth 模块中的令牌解析、会话加载和权限判断,消除循环依赖并提高可测试性。

必须保持:
- 所有公开接口、错误码和响应结构;
- 登录态过期和刷新行为;
- 现有数据库结构;
- 线上配置字段名称。

允许变化:
- 内部目录和私有类型;
- 模块依赖方向;
- 测试组织。

不在范围:
- 更换认证方案;
- 升级框架;
- 修改 UI;
- 性能优化。

你把"必须保持""允许变化""不在范围"三栏列清楚,Codex 就不容易把重构搞成重写。我有个习惯:每次开始重构前把这三栏直接贴给 Codex,出问题的概率肉眼可见地下降。


第一步:建立行为基线

别急着改代码,先搞清楚"现在到底是什么样的"。

让 Codex 帮你做一次全面摸底:

text
先只读分析 auth 模块。列出公开入口、调用方、状态变化、错误类型、配置字段、数据库访问和现有测试。找出行为没有测试保护的区域,并提出最小特征测试,不要开始重构。

我当时第一次用这个 prompt 的时候还挺惊讶的——Codex 给我列出来三个完全没有测试覆盖的关键路径,其中一个涉及 token 刷新逻辑,线上要是挂了根本没人能第一时间发现。后怕。

拿到这份清单之后,给那些裸奔的关键行为补上特征测试。注意,这时候不评价代码好不好看,只做一件事:把当前的外部行为钉住。后面任何一步跑测试,你都能立刻知道有没有东西被意外改坏了。


第二步:画依赖与迁移地图

有了基线,接下来就是把整个迁移拆成可控的阶段。

这一步输出至少包含:

  • 当前模块依赖图;
  • 目标模块边界;
  • 每个调用方迁移顺序;
  • 临时适配层;
  • 可删除旧代码的条件;
  • 每个阶段的回滚点。

提示:

text
把迁移拆成最多 5 个阶段。每个阶段必须能独立构建和测试,说明修改文件、兼容策略、验证命令和回退方式。不要在同一阶段同时迁移所有调用方并删除旧入口。

说个教训:我有一次贪快,让 Codex 在同一个阶段里同时迁移三个调用方、删掉旧入口、还顺手改了配置结构。结果一个调用方的测试挂了,我根本定位不到是哪一步的问题,最后只能全部回退重来。五个阶段的限制看着保守,实际上是救命的设计。


第三步:先建立新边界,不迁移行为

第一阶段只做四件事:

  • 新建接口或内部模块;
  • 把旧实现包在兼容层后面;
  • 增加契约测试;
  • 保持所有调用方不变。

这一步的精髓在于——你只是在画新的线,还没开始搬东西。如果这一步就跑不动了,说明你的切片还是太大。我通常把这个阶段当成"安全检查点":新边界搭好了、测试全绿、所有调用方走旧路径,这才算过关。


第四步:逐个调用方迁移

好,新边界立住了,现在一个一个搬。

text
只迁移调用方 [名称/目录] 到新边界。其他调用方继续走兼容层。完成后运行该调用方测试、auth 模块测试和类型检查,并比较公开行为。

搬完一批就看一眼 Git diff。我强烈建议用独立 worktree 处理不同但互不重叠的调用方——千万别让多个任务同时编辑同一个核心文件。踩过这个坑:两个并行任务各自改 auth.go,合并的时候那叫一个酸爽。


第五步:处理数据库和公共 API 迁移

到了需要改数据表结构或者公开接口的时候,这已经不是纯重构了。必须单独设计兼容方案:

  • 先加后删;
  • 旧字段继续可读;
  • 写入幂等;
  • 迁移脚本可重复或能检测已执行;
  • 有回滚脚本或备份;
  • 新旧版本可在发布窗口内共存;
  • 通过指标确认旧路径已无流量。

这里特别提醒一句:不要让 Codex 只靠代码搜索就判断"旧字段没人用了"。我踩过一个经典的坑——Codex 搜了代码仓库说某个字段没引用了,结果那个字段是给 BI 报表用的,通过 SQL 直接查的,代码里当然搜不到。一定要去看报表、脚本、外部客户端和线上流量监控。


第六步:最后删除兼容层

删旧代码是最爽的环节,但也最容易翻车。只有同时满足以下条件才动手:

  • 所有内部调用方已迁移;
  • 外部兼容期结束;
  • 完整测试和构建通过;
  • 日志或指标显示旧入口无使用;
  • 文档和配置已经更新;
  • 回滚策略不再依赖旧实现。

删除阶段单独一个 commit,不要跟核心迁移混在一起。这样审查的人一眼就能看清你删了什么,你自己想回退也能精确找到那一刀。


使用 Codex 云任务或 Subagents 的边界

并行能提速,但不是什么都适合并行。

适合并行的:独立调用方迁移、测试补充、文档更新、静态引用盘点。

不适合无约束并行的:核心接口设计、同一迁移脚本、同一公共类型、会产生冲突的格式化。

启动并行之前,先对齐这些:文件所有权、基线分支、接口版本和合并顺序。不然两个 agent 抢同一个文件就热闹了。


验证阶梯

每个阶段至少跑这些:

  1. 新增契约/特征测试;
  2. 当前模块测试;
  3. 已迁移调用方测试;
  4. 类型检查或编译;
  5. 跨模块集成测试;
  6. 完整构建;
  7. 关键用户路径;
  8. 公开 API 或数据差异检查。

别偷懒只跑前三条,我就在第五条上翻过车——模块测试全绿、调用方测试也过了,结果集成测试挂了,因为两个模块之间的序列化格式不兼容,单测根本覆盖不到。


常见失败

重构中顺手修业务

把行为修复拆成独立任务,否则回归的时候你根本判断不了是重构改坏的还是修 bug 引入的新问题。我有一次在一个重构 PR 里"顺便"修了一个边界条件的判断,结果线上出了诡异行为,排查了两天才锁定——那个"顺手"的改动。

计划只有文件列表

光列文件不说明迁移顺序和兼容策略,跟没计划差不多。要求每个阶段都能独立跑通,你才有信心一步步推进。

过早删除旧入口

先迁移、观测、确认无流量,再删。代码搜索结果不是唯一的使用证据。

测试也被一起重写

至少保留一层从用户或公开接口视角验证行为的独立测试。不然实现和测试一起"自洽地变错",你根本发现不了。


完成报告模板

text
保持不变的行为:[列表]
迁移阶段:[完成/未完成]
当前兼容层:[位置和删除条件]
验证结果:[命令和结果]
仍在使用旧路径的调用方:[列表]
回滚点:[分支/提交/开关或明确步骤]

下一步

重构搞完了,用 代码审查、Git 与 PR 闭环 检查一下差异。需要并行代理的话,继续看 Subagents 实战


好了朋友,这套分阶段迁移的打法看着步骤多,但每一步都很短、能验证、能回退。比一把梭然后盯着几千行 diff 怀疑人生要踏实多了。去试试吧,有问题随时回来聊。