智能工具库

遗留系统迁移中的差分测试实战

遗留系统迁移中的差分测试实战

介绍如何在遗留系统迁移中用差分测试对比新旧实现,涵盖输出归一化、容差设置、副作用比较、AI 辅助分类差异及影子流量等实用技巧。

2026-09-15 0来源:freeCodeCamp

为什么需要差分测试

遗留系统迁移中最危险的时刻,往往不是刚开始写新代码的时候,而是新实现看起来已经完成的时候。代码能编译,测试全绿,架构更干净,响应也更快,于是所有人开始问:能不能切流量了?

问题在于,新实现可以通过自己的测试套件,却仍然与被替换的系统行为不同。可能是舍入方式变了,空值处理不同,错误变成了成功响应,记录排序变了,副作用顺序变了,或者某条从未被记录的业务规则在迁移中丢失了。

差分测试的思路很直接:用相同的输入分别运行新旧两套实现,比较它们的行为差异。不是只问新系统是否通过测试,而是问:给定相同输入,新系统在哪里与旧系统表现不同?这些差异就是证据——有些是 bug,有些是有意改进,有些是无害的表示差异,还有些揭示了没人知道存在的旧行为。

差分测试能告诉你什么

目标不是证明两个实现内部完全一致,而是获得证据表明它们在关键行为上等价

假设旧系统计算订单最终价格:

type Order = {
  subtotal: number;
  customerType: "STANDARD" | "PREMIUM";
  country: string;
};

function legacyCalculateTotal(order: Order): number {
  let total = order.subtotal;
  if (order.customerType === "PREMIUM") {
    total *= 0.9;
  }
  // 可能还有其他未记录的逻辑
  return total;
}

新实现可能用不同方式处理了同一逻辑。差分测试就是把同一批订单输入两边,对比输出。

从可观测边界开始

差分测试最好在你已经为要迁移的能力划出边界之后进行。理想情况下,你了解它的输入、输出、重要业务规则、外部契约、副作用和已知的不确定区域。

不要一上来就对比整个系统,先选一个可观测的边界,比如一个函数、一个 API 端点或一个服务方法。

不要盲目对比原始输出

直接比较 JSON 字符串几乎总会失败,因为存在大量无意义差异。需要先归一化

  • 统一数字格式和精度
  • 统一空值表示(null、undefined、空字符串)
  • 统一字段顺序
  • 统一时间格式
  • 统一错误消息中的动态部分

处理时间戳和不确定性值

时间戳、随机 ID、自增主键等每次运行都不同,必须在比较前替换为占位符,例如把 createdAt 替换为 <TIMESTAMP>

比较业务含义,而不只是 JSON

两个输出结构相同不代表业务含义相同。例如金额字段在旧系统是分为单位,新系统是元为单位,数值一样但含义完全不同。要按业务语义做映射和比较。

把错误和副作用也纳入契约

错误也是行为的一部分。旧系统对某输入返回 404,新系统返回 200,这就是差异,必须记录。

副作用同样重要:数据库写入、消息发送、外部 API 调用、日志记录。差分测试要比较这些副作用是否以相同顺序、相同内容发生。

在合理的地方引入容差

浮点数计算、时间窗口、并发顺序等场景,精确相等没有意义。应设定容差,例如金额误差在 0.01 以内视为等价。

构建可复用的差分测试框架

用 TypeScript 和 Vitest 可以搭建一个可复用的测试工具,核心步骤:

  1. 准备一组真实输入样本
  2. 分别调用旧实现和新实现
  3. 对输出做归一化
  4. 按规则比较,记录差异
  5. 输出结构化报告
async function differentialTest(input: Order) {
  const legacy = normalizeLegacy(legacyCalculateTotal(input));
  const modern = normalizeModern(modernCalculateTotal(input));
  return compare(legacy, modern);
}

从真实行为生成测试用例

不要凭空造输入。从生产日志、数据库记录、API 流量中提取真实样本,覆盖边界情况和历史异常。

分类每一个差异

每个差异都需要人工或辅助分类:

  • Bug:新实现错误,需要修复
  • 有意改进:新行为更好,需确认并记录
  • 无害表示差异:归一化后应消除
  • 未知旧行为:需要业务确认

用 AI 辅助调查差异

AI 可以用来分类和解释差异,但不能让它决定正确性。可以让 AI 分析差异报告,给出可能原因和建议,最终判断仍由人负责。

安全使用影子流量

在预发布或生产环境,把真实流量同时发送给新旧系统,但只让旧系统返回结果。对比两者输出,记录差异。注意:

  • 新系统的副作用要隔离,不能写生产库
  • 敏感数据要脱敏
  • 流量比例从小开始
  • 监控差异率变化

衡量差异,而非等待完美

不要期望零差异。要度量差异率,跟踪趋势,设定可接受的阈值。差异率持续下降并稳定在低位,才是切流的信号。

何时准备切流

当以下条件满足时可以切流:

  • 差异率低于预设阈值
  • 所有高优先级差异已分类并处理
  • 剩余差异已确认可接受
  • 有回滚方案和监控

差分测试无法证明什么

它不能证明两个实现内部算法相同,不能覆盖未测试的输入空间,不能替代业务验收。它提供的是行为等价的证据,不是数学证明。

结论

差分测试把迁移风险转化为可观测、可分类、可度量的证据。它让团队在切流前有据可依,而不是仅凭“测试通过”就冒险切换。对开发者和 AI 使用者来说,这是一套值得纳入迁移流程的实用方法。

本文基于 freeCodeCamp 的公开内容,由 AI 辅助整理改写后发布。

原标题:How to Use Differential Testing During a Legacy Migration

阅读原文