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

介绍如何在遗留系统迁移中用差分测试对比新旧实现,涵盖输出归一化、容差设置、副作用比较、AI 辅助分类差异及影子流量等实用技巧。
为什么需要差分测试
遗留系统迁移中最危险的时刻,往往不是刚开始写新代码的时候,而是新实现看起来已经完成的时候。代码能编译,测试全绿,架构更干净,响应也更快,于是所有人开始问:能不能切流量了?
问题在于,新实现可以通过自己的测试套件,却仍然与被替换的系统行为不同。可能是舍入方式变了,空值处理不同,错误变成了成功响应,记录排序变了,副作用顺序变了,或者某条从未被记录的业务规则在迁移中丢失了。
差分测试的思路很直接:用相同的输入分别运行新旧两套实现,比较它们的行为差异。不是只问新系统是否通过测试,而是问:给定相同输入,新系统在哪里与旧系统表现不同?这些差异就是证据——有些是 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 可以搭建一个可复用的测试工具,核心步骤:
- 准备一组真实输入样本
- 分别调用旧实现和新实现
- 对输出做归一化
- 按规则比较,记录差异
- 输出结构化报告
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
阅读原文