← 返回博客
工程实践遗留系统回归测试

接手遗留系统的工程方法:从代码审计到回归修复的完整流程

接手一个没有文档、原开发者已离场的遗留系统,最大的风险不是看不懂代码,而是"改一处崩三处"。本文给出一套五步工程流程:环境复原、代码审计、特征测试兜底、增量修复、知识移交,每一步都有明确的产出物。

先说结论

接手遗留系统的最大风险不是”看不懂”,而是**“改一处崩三处”——在没有安全网的情况下动刀**。对抗它的是流程而不是勇气:

1. 环境复原 → 2. 代码审计 → 3. 特征测试兜底 → 4. 增量修复 → 5. 知识移交

每一步都有明确产出物,任何一步跳过都会在后面加倍还债。

1. 环境复原:跑不起来的代码不可审计

先建”能复现生产行为的环境”,产出物是一键启动脚本 + 环境文档

  • 锁定运行时与依赖版本(从生产环境反查,不信 README——它往往过时)
  • 拿到脱敏的数据库快照(结构 + 有代表性的数据,没有数据的系统跑不出真实行为)
  • 理清外部依赖(第三方 API、消息队列、定时任务),测试环境用 stub 或沙箱替身
  • 把以上全部固化成 Docker Compose 或等价物——下一个接手的人不该再考古一遍

2. 代码审计:产出风险地图,不是读后感

审计的目标是回答三个问题:**钱从哪条路径流过?哪里最容易崩?哪里千万别碰?**产出物是一份审计报告:

  • 关键路径清单:支付、下单、核心数据写入——按业务重要性排序,后续测试与修复的优先级依据
  • 依赖与调用图:模块间真实依赖(工具生成 + 人工修正),标出循环依赖和”上帝对象”
  • 风险清单:硬编码密钥、无索引慢查询、裸奔的外部调用(无超时/重试/熔断)、被注释掉但可能还有用的代码
  • 禁区标注:看不懂且没有测试覆盖的高危区域——明确”暂不动”,比假装能驾驭诚实得多

3. 特征测试:给现状拍照,而不是评判对错

没有测试的系统,改动前先用特征测试固化现状:选定关键路径,录制当前的输入输出,写成断言。

# 特征测试的心态:不问"应该返回什么",只记录"现在返回什么"
def test_order_total_characterization():
    order = create_order(items=[...], coupon='LEGACY10')
    # 这个 8.4 是现状,不是需求——它可能是 bug,但它是生产依赖的 bug
    assert order.total == 8.4

要点:

  • 覆盖关键路径而非追求覆盖率——20% 的路径承载 95% 的业务价值
  • 现状里的”bug”也先固化——生产环境可能已有下游依赖这个行为,修它是一个独立决策(要和业务方确认),不是顺手的事
  • 特征测试是安全网不是终点,后续增量修复中逐步替换成真正的行为测试

4. 增量修复:小步、单一意图、随时可回滚

规则只有三条:

  1. 一次只做一件事——重构(不改行为)和修复(改行为)永远分开提交,混在一起时特征测试报红你分不清是哪个原因
  2. 每步都过特征测试——绿了才走下一步;红了先判断是预期修复还是意外破坏
  3. 产出修复说明 + diff——每个修复对应一份”改了什么、为什么、影响面、回滚方式”,这是交付物的一部分,也是下次接手成本的下降曲线

架构级改造用绞杀者模式:新功能进新模块,老功能逐个迁移并切流量,老核心自然萎缩。全量重写是最后手段,不是默认选项(理由见 FAQ)。

5. 知识移交:把考古成果变成资产

接手期积累的认知如果只在你脑子里,系统只是换了一个”唯一知情人”。移交产出物:

  • 环境文档 + 一键启动(第 1 步的沉淀)
  • 审计报告 + 风险清单(第 2 步,随修复进度更新状态)
  • 测试套件与运行说明(第 3 步)
  • 修复日志(第 4 步的全部修复说明)
  • 一次走查会议:带着接手方把关键路径从入口走到落库

常见误区

  • 上来就重构:“这代码太烂了先整理一下”——没有测试兜底的重构就是赌博,烂但能跑的代码比整洁但行为变了的代码值钱。
  • 相信注释和文档:遗留系统的注释平均落后代码若干年,唯一可信的是代码本身和生产行为。
  • 低估”看起来多余”的分支:那个奇怪的 if 往往对应一个你不知道的客户、一次你没经历的事故。删除前先问 git blame。

相关阅读

常见问题

接手遗留系统的第一步是看代码吗?

不是,第一步是让系统在受控环境里跑起来(环境复原)。跑不起来的代码读得再懂也无法验证任何假设。依赖版本、环境变量、外部服务连接、数据库快照——先把"能复现生产行为的本地/测试环境"建出来,之后的一切工作才有验证基准。

没有任何测试的老系统,怎么敢改?

用特征测试(Characterization Test)兜底:不判断行为"对不对",只固化"现在是什么"——给关键路径录制当前输入输出作为断言。改动后特征测试变绿说明行为未变,变红说明改动影响了既有行为(可能是修复也可能是破坏,人工判断)。这是 Michael Feathers《修改代码的艺术》的核心方法,实践中依然是最有效的。

遗留系统要不要直接重写?

默认不要。重写的真实成本几乎总是被低估:老系统里大量"看起来多余"的分支是多年踩坑的补丁,重写会把这些坑重踩一遍。推荐绞杀者模式(Strangler Fig):新功能走新模块,老功能逐步迁移,两者并行直到老核心自然萎缩。只有当技术栈已死(无人维护的框架/语言版本)且业务逻辑简单时,重写才划算。

怎么评估接手一个遗留系统要多久?

按"审计产出物"倒推:环境复原(数天到一周)、代码审计报告(一周内,含风险清单和依赖图)、关键路径特征测试(视路径数量一到两周)。到这里才能给出可信的修复排期——任何在审计之前给出的整体报价/工期,都是猜的。

本文来自 AI Enable Harness 一线交付实践。需要同类系统或优化服务?