用 JSON snapshot 记录纯函数的输入输出和异常作为基线,AI 审查 diff 时对比基线变化而非全文检查。
没有基线的重构是一场信任跌落。闭上眼,全凭运气。运气往往落在别处。
AI 审阅者擅长阅读 diff,但读不懂你的意图。Diff 审阅只能检查形状,无法发现行为变化。你需要一个外部记忆。表征测试就是那份记忆。
重构之前先修复契约。选一个混乱的纯函数,保持它小巧。避开时间、随机、网络和文件 I/O。纯函数能提供稳定的基线。
契约有三部分:输入进入,输出出来,异常也是输出。把三者都快照下来。
把这个脚本放在函数文件外面。它加载模块、运行命名的用例,然后写入 JSON。
# snapshot.py
import json
import sys
def run(path, cases):
ns = {}
with open(path) as f:
exec(compile(f.read(), path, "exec"), ns)
results = []
for c in cases:
item = {"name": c["name"]}
try:
item["ok"] = True
item["result"] = repr(
ns[c["fn"]](*c.get("args", []), **c.get("kwargs", {}))
)
except Exception as exc:
item["ok"] = False
item["exception"] = type(exc).__name__
item["detail"] = str(exc)
results.append(item)
return results
if __name__ == "__main__":
cases = json.load(open(sys.argv[2]))
result = run(sys.argv[1], cases)
json.dump(result, open(sys.argv[3], "w"), indent=2)
这个运行器从不导入你的生产包。它只编译一个文件。这保证了 harness 对老代码也是安全的。
给每个用例命名。无名的用例会让 diff 阅读变得痛苦。覆盖老代码容易隐藏的边界情况。
[
{"name": "two items", "fn": "calculate_total", "args": [2, 10.0]},
{"name": "zero quantity", "fn": "calculate_total", "args": [0, 10.0]},
{"name": "negative price", "fn": "calculate_total", "args": [1, -2.0]},
{"name": "missing price", "fn": "calculate_total", "args": [1, null]}
]
从五到十个用例开始。之后可以扩展。更多用例意味着更多信号。大部分价值来自边界情况。
python3 snapshot.py order.py order_cases.json baseline.json
先提交 baseline.json。提交信息可以精确说明你在保存什么。审阅者得到一份具体的产物。没有人需要相信老函数的声誉。
现在让模型重写函数。把基线文件给它,把用例文件给它,告诉它前后的输出必须完全一致。
模型可以提出新版本。基线不是建议,而是通过/失败的门槛。
python3 snapshot.py order_new.py order_cases.json after.json
diff baseline.json after.json
干净的 diff 意味着行为存活了下来。嘈杂的 diff 精确显示哪个用例坏了。这把模糊的审阅意见变成了可复现的测试。
每次重构前用这张表判断。在错误的函数上做廉价快照会浪费维护时间。在正确的函数上不做快照会付出事故代价。
披露:本文是 MonkeyCode 产品推广的一部分。
这个循环中的生成步骤需要一个 AI 后端。我在那一步使用了 MonkeyCode 的免费模型访问。它适合这个场景,因为重构迭代是可能花钱的部分。免费层消除了重试的焦虑。免费服务器选项对另一方面很重要。有些代码库不能离开私有网络。本地服务器把基线、候选版本和 diff 留在正确的地方。
这个工作流的价值不依赖 MonkeyCode。脚本、用例和 diff 都是纯 JSON。这才是重点。
快照保留 bug。如果原始输出是错的,基线就是错的。快照捕捉行为变化,不捕捉设计问题。它们不是真正测试的替代品。它们是老行为的包装纸。
只在纯函数上先使用。对于有状态的代码,同时快照输入对象。对于 I/O 密集的函数,建立明确的 fixture。否则基线会撒谎。
拥有完整分支覆盖的团队应该跳过快照。经过良好测试的函数已经有基线了。对它做快照只是添加了同一份测试的第二个版本。
想要完全自动化重构的人也intoshould 也跳过这个。模型仍然需要做选择。你仍然需要阅读 diff。这个循环只是消除了猜测。
永远不要把 PII 写入快照文件。对遗留函数做表征不能成为泄露用户数据的理由。
下次你让模型清理一堆烂代码时,先记录行为。这样审阅就是一次 diff。重掏不再是信任跌落。
你可能需要考虑屏蔽这个人或举报滥用行为。