用 Claude Code 做遗留系统迁移:四步法把 20 年老代码变成能维护的样子
遗留系统迁移不是「让 AI 重写一遍」,而是一场需要地图、试点、护栏和验证的系统工程。本文给出可以直接照做的四步流程,以及各步骤中 AI 该做什么、不该做什么。
遗留系统迁移是这类任务的典型代表:代码能跑、没人敢动、文档为零、原作者已离职。过去的标准答案是”重写”,成功率众所周知。
AI 编程助手改变了一部分游戏规则——它读代码的速度比人快得多,能大规模地补齐注释、生成测试、做模式替换。但它也带来了新的失败模式:自信地重写出一份看起来对、实际漏掉边界情况的代码。
下面是实践下来比较稳的四步法。
第一步:让 AI 生成”系统地图”,而不是改代码
不要一上手就让 AI 改。先让它读,产出结构化的理解。
具体做法:把代码库按模块切分,每个模块跑一遍这样的任务:
阅读 src/billing/ 下的全部代码。输出一份模块说明:
1. 这个模块的职责边界(它做什么、不做什么)
2. 对外暴露的接口与它们的调用方
3. 关键数据结构及其生命周期
4. 隐含的业务规则(从代码逻辑反推,不要臆测)
5. 可疑的地方:没被调用的代码、被注释掉的分支、明显的 bug
每条结论标注来自哪个文件与行号。
几个要点:
- 必须要求标注出处。没有行号的结论一律不采信,很容易是幻觉
- 分批做,不要一次塞整个仓库。上下文有限,塞不下就会丢信息
- 人工交叉验证。挑三个模块,自己读一遍,看 AI 的说明准不准。不准的模块重做,这说明切分粒度或提示词有问题
这一步的产出是一份可读的系统地图,价值本身就很大——很多团队在这一步才发现,系统里有 30% 的代码从来没被调用过。
第二步:用”行为快照”锁住现状
迁移最大的风险是悄悄改变行为。在动手改之前,要先能证明”改完之后行为一致”。
对每个要迁移的模块,做行为快照:
输入输出对采集。 从生产日志里抽取真实请求与响应,或者用 AI 分析代码路径、构造覆盖各分支的输入。目标是几百到上千组 (输入, 输出) 样本。
边界值专门补。 AI 很擅长列出边界情况:空值、超长字符串、负数、时区边界、并发同一记录。让它列清单,人来筛掉不现实的,然后补成样本。
非确定性处理。 涉及时间、随机、外部依赖的模块,需要把这些做可注入的,才能做确定性比对。
这份快照就是后面的验收标准。没有它,迁移只能靠”看起来对”。
第三步:小步迁移,每步都有护栏
关键原则:一次只迁一个函数或一个类,并且每次迁移都必须通过快照比对。
给 AI 的任务应该尽量窄:
按以下规则迁移 src/billing/calc_tax.py 里的 calculate_tax 函数:
- 保持函数签名与返回值结构完全一致
- 保持所有边界行为一致,包括抛出的异常类型与消息
- 允许替换内部实现,允许使用更清晰的写法
- 不允许修改同文件的其他函数
- 迁移后输出:改动说明、你识别到的风险点、需要额外验证的分支
几个护栏:
禁止范围扩散。 明确告诉 AI 不许改其他代码。实际使用中,助手经常会”顺手优化”附近代码,这会污染 diff,让审查失效。
用 diff 审查,不用原文审查。 每次提交必须是小 diff。看到大规模改动就退回重做。
测试必须真的跑起来。 生成的测试如果不跑,等于没有。迁移完成后立即跑快照比对,失败就停下。
保留旧代码直到验证通过。 新实现在旁路运行一段时间,结果与旧实现比对,一致后再切流。
第四步:让 AI 做”敌对审查”
迁移完成后,让另一个 AI 会话(或不同的模型)做对抗性检查。提示词方向:
以下是某函数的旧实现与新实现。你的任务是找出两者在什么输入下行为不同。
对每一个你能想到的差异,给出具体的触发输入和预期不同的输出。
不要评价代码质量,只找行为差异。
让”写”和”审”由不同的会话执行,能显著提升发现率。同一个会话往往对自己的实现有偏见。
再补一轮人类审查,重点看三处:异常处理路径、并发与状态、外部依赖的失败处理。这三处是 AI 最容易遗漏的。
常见失败模式
一上来重写整个模块。 结果是一份 800 行的 diff,没人能审,只能”信了”。这是最常见的翻车方式。
没有快照就迁移。 迁移后无法证明行为一致,只能靠上线后监控。风险不可控。
过度信任 AI 的”我已完成” 。它会说自己处理好了所有边界,实际可能漏了三个。只有跑测试才算数。
忽略性能。 语义一致不等于性能一致。旧代码可能有奇怪的写法,恰恰是为了性能(比如避免 N+1 查询)。迁移后要跑性能基线对比。
值得做这件事的判断标准
不是所有遗留系统都值得迁移。值得的信号是:改动频繁但每次都很痛苦、有明确的合规或依赖风险、团队里已经没人敢碰。
不值得的信号是:系统稳定、改动极少、业务价值低。这种情况最理性的选择是加一层封装把它隔离起来,而不是迁移。让 AI 做这层封装的适配器和文档,性价比高得多。
遗留系统的核心问题从来不是代码老,而是没人理解它。AI 的最大价值不是替你重写,而是帮你把理解的成本降下来——理解了之后,要不要重写、怎么重写,判断会清晰很多。