让AI重写旧代码前,先设行为等价门禁
文章建议先建立行为等价验证,再让代理替换旧系统实现,以检查用户及依赖系统依赖的行为是否保留。内容是基于公开报道的工程指南,并非Cisco重写项目的实测复盘。
AI 深度解读
AI 已经能很快写出一套替代实现,但团队未必能同样快地确认:它还是原来的产品吗?作者借 Cisco 大规模重写遗留代码的报道,提出一个更实用的顺序:先建新旧行为对照门禁,再让 Agent 动手。
这是一份基于公开资料的工程指南,没有 Cisco 项目的内部实测。它值得读的地方,是把「看起来功能一样」拆成了可保存、可复现、可审核的证据,尤其适合准备让 AI 接手旧业务代码的团队。
重写了多少代码,不能证明保住了多少行为
原文引用的消息来自 Fortune 报道,经 Tom's Hardware 转述:Cisco 总裁 Jeetu Patel 给一名 27 岁工程师提供不限量 AI tokens,目标是在 24 个月内重写 98% 的遗留代码,概念验证窗口为四周。
这里的 98% 是目标,不是完成率。公开报道没有证明任务已经完成,也没有证明 token 供应就是项目瓶颈。
遗留系统的麻烦,在于需求并不完整地写在文档里。数据库约束、线上数据、下游接口、错误处理,甚至一个没人记得来历的默认值,都可能属于实际契约。新代码更整洁,也可能悄悄改变金额舍入、结果顺序或失败方式。
因此,普通测试和行为对照测试回答的是两个问题。前者检查新代码是否返回预期结果;后者让相同输入分别经过新旧实现,比较可观察的结果。原文把后者称为 parity test,可以理解为「新旧行为对照测试」。
重写的验收依据,应当是行为证据,而不是生成代码的数量。
作者引用 Microsoft 的迁移验证建议:提前定义验收门禁,保存旧系统基线,让两个版本使用相同数据和环境假设,并逐项调查回归。VS Code 的测试指南还要求保留断言、报告实际执行的测试,没运行的部分仍然未经验证。
先选一个能独立回退的小行为
起点可以是定价规则、日期转换、权限判断或序列化函数。它需要边界清楚、结果可控,而且能在受控条件下同时调用新旧实现。
下面的脚本解决最小对照问题:逐行读取 JSON 用例,交给两个命令执行,保存结果差异,并把进程错误视为失败。
#!/usr/bin/env python3
"""Compare legacy and candidate commands on newline-delimited JSON cases."""
import json
import subprocess
import sys
from pathlib import Path
CASES = Path(sys.argv[1])
OLD_COMMAND = ["python3", "legacy_impl.py"]
NEW_COMMAND = ["python3", "candidate_impl.py"]
def run(command, case):
completed = subprocess.run(
command,
input=json.dumps(case),
text=True,
capture_output=True,
check=False,
timeout=10,
)
if completed.returncode != 0:
raise RuntimeError(
f"{command[1]} exited {completed.returncode}: "
f"{completed.stderr.strip()}"
)
return json.loads(completed.stdout)
def main():
cases = [json.loads(line) for line in CASES.read_text().splitlines() if line]
mismatches = []
for index, case in enumerate(cases, start=1):
old = run(OLD_COMMAND, case)
new = run(NEW_COMMAND, case)
if old != new:
mismatches.append({"case": index, "input": case,
"legacy": old, "candidate": new})
print(json.dumps({
"cases": len(cases),
"mismatches": len(mismatches),
"details": mismatches,
}, indent=2, sort_keys=True))
return 1 if mismatches else 0
if __name__ == "__main__":
raise SystemExit(main())
要看清 if old != new::这里比较的是 json.loads 解析后的值,JSON 对象键顺序不同不会触发差异;数组顺序、字段缺失与 null 等差异则会保留。因此,如果调用方依赖原始序列化的字段顺序,还要补充相应检查。
将输入存入 cases.jsonl,每行一个 JSON 对象,执行 python3 parity.py cases.jsonl。两个命令数组应替换为真实且隔离的入口。脚本设置了 timeout=10,非零退出码会抛错,中途错误也不会被静默跳过。
这只是起点。涉及数据库、服务依赖和消息队列时,还需要快照、服务替身与更强隔离。时间戳等非确定字段可以规范化,但每一项处理都要说明理由,避免把业务回归一起抹掉。
用例要揭露契约,差异要有人裁决
只收集正常输入,门禁很容易变成形式。原文要求覆盖真实常用输入、边界、非法输入、失败行为、兼容细节和历史缺陷。
具体包括空字符串、最小与最大值、时区切换、舍入临界点、大载荷、缺失字段、下游超时、依赖不可用及重试上限。错误码、稳定标识符,以及 null 和字段省略的区别,也可能是调用方依赖的行为。真实数据需要脱敏,或安全地合成。
预期结果不能完全交给 Agent 编造,再让它拿自己的答案判自己正确。依据应尽量来自已有测试、已批准需求、历史行为或业务负责人。
旧实现同样不是真理。它能告诉你系统现在怎么做,却不能证明这样做合理。已知缺陷需要明确决定保留还是修复。
报告也不能只写「17 个测试失败」。每个差异应附输入、新旧输出和稳定用例标识,再按舍入、校验、授权、序列化等行为归类。原文区分三种处理结果:
- 等价:受支持的调用方观察不到差别,并记录判断理由。
- 批准变更:行为确实改变,由负责人确认,并配套迁移或兼容计划。
- 尚未解决:契约还没确定,阻止候选版本晋级。
99% 的通过率,也可能掩盖最关键的授权或计费用例。
Agent 可以梳理调用点、建议用例、解释差异并实现替代代码,但不应在同一个未经审核的步骤里修改验收标准、批准自己的输出。原文建议先让人审核行为地图和用例,再让 Agent 在不改用例及预期值的条件下实现;通过仓库权限或独立审核边界保护这些文件。
运行时还要保存命令、版本标识、环境和报告。合并前检查最终 diff,运行相关现有测试;上线前定义回滚触发条件,通过可逆开关逐步放量。
我的判断
我认同这个顺序。审查一大片生成代码,很难直接判断业务是否等价;把输入和两份输出摆在一起,讨论会具体得多。
但作者没有提供亲手迁移的结果,也没有与人工重写、普通单元测试或其他迁移方式的对照数据。因此,这是一套有工程依据的方法建议,不能当作已验证的效率结论。「稀缺资源可能是可信用例和业务负责人时间」也只是工程推断,不能套成 Cisco 的内部事实。
它还有一个现实前提:旧实现必须能稳定运行,输入、数据和依赖状态可以控制。强依赖外部服务或包含大量副作用的组件,往往要先付出隔离成本。
与只测试新实现相比,对照测试更容易暴露历史兼容行为;与基于需求编写的测试相比,它又容易继承旧缺陷。两者需要互补。功能对齐也不能替代性能、安全、隐私和运维检查。
示例脚本还有简化之处:用例标识是行号,调整文件顺序后并不稳定;进程异常会直接终止,无法留下完整的结构化错误报告。将它接入持续集成前,需要补齐这些证据保存能力。
能拿来做什么
- 让 AI 重写前端日期格式化、权限判断或 Node 序列化逻辑前,先选一个能独立回退的函数,固定输入语料并保存旧实现输出。
- 从已有测试、历史故障和调用方约定中补充边界用例,明确检查缺失字段、
null、错误码、舍入与数组顺序,别只验证正常路径。 - 给 Agent 的任务明确写出「不得修改对照用例和预期值」,让它提交差异解释,由负责业务契约的人决定哪些变化可以接受。
- 把逐项差异审核、相关测试和回滚条件放进发布流程;先确认门禁确实能抓住关心的错误,并演练回退,再扩大重写范围。