最近用 ChatGPT、Codex 接手旧项目时,我越来越不建议第一句话就是:
“帮我把这个模块重构一下。”
尤其是那种已经跑了三五年、代码风格很乱、测试又不完整的项目。
表面看起来,最值得做的是:
拆大类。
改命名。
抽公共逻辑。
调整目录。
重写接口。
把历史遗留代码全部整理一遍。
Codex做这些事情确实很快。
但真正危险的地方也在这里:
代码可以很快变漂亮,系统原来那些没人写进文档里的行为,也可能一起被改掉。
比如一个旧订单接口。
你看代码的时候觉得特别不合理:
订单不存在时居然返回200。
某个字段明明没有值,却返回空字符串而不是null。
重复提交时不报错,而是直接返回第一次结果。
某个状态明明已经废弃很多年,旧客户端却还在使用。
从“代码设计”角度看,这些都很想顺手改掉。
但问题是:
它们可能早就变成了真实系统的一部分。
所以旧项目大重构之前,我现在更关注的第一件事不是:
“怎么改得更漂亮?”
而是:
“这个系统现在实际上是怎么工作的?”
这就是:
Behavior Baseline——行为基线。
一、旧代码最危险的地方,不是乱,而是“没人知道哪些乱不能动”
一个成熟旧项目里,通常同时存在三种东西。
第一种:
真正的技术债。
比如重复代码、糟糕命名、不合理的依赖。
第二种:
历史兼容行为。
看起来不好看,但外部系统已经依赖。
第三种:
没人解释得清的特殊逻辑。
比如:
if amount == 0: return success你可能觉得这段完全多余。
但继续查才发现:
三年前有一批历史订单就是靠这条逻辑兼容。
如果Agent一上来就按照“最佳实践”重写,
最容易把第二类和第三类一起当成第一类处理。
这也是为什么:
Cleaner Code ≠ Safer Refactor
代码更干净,
不代表重构更安全。
二、为什么Agent特别容易在旧项目里“改对代码,却改错行为”?
因为 ChatGPT、Codex 最容易看到的是:
当前代码结构。
类型定义。
调用关系。
测试。
但旧项目真正的行为契约,往往散落在:
线上数据。
客户端调用方式。
历史接口响应。
数据库副作用。
消息事件。
配置。
甚至运营人员的使用习惯里。
如果这些没有明确给出来,
Agent很自然会认为:
当前代码就是全部真相。
于是它可能主动修掉一些“看起来不合理”的地方。
比如把:
200 + error message
改成标准的:
404
从HTTP设计角度看,可能更合理。
但如果旧客户端只判断200:
线上立刻出问题。
所以旧项目重构真正难的不是:
Agent不会写新代码。
而是:
旧系统的真实契约往往没有被显式记录。
三、所以第一步不是Refactor,而是Characterize
重构前我更推荐先做:
Characterization——行为刻画。
简单说就是:
先把系统现在的关键行为记录下来。
不是先判断:
“这样设计对不对”。
而是先确认:
“现在它确实就是这么工作”。
例如一个旧接口可以先记录:
- 正常输入返回什么;
- 空数据返回什么;
- 非法参数返回什么;
- 会写哪些数据库表;
- 会不会发消息;
- 重试会不会产生重复副作用;
- 历史数据怎么处理;
- P95大概多少;
- 哪些旧客户端仍然依赖。
这些东西加起来,才是真正的:
行为基线。
四、Characterization Test和普通单测不完全一样
普通单测经常表达的是:
“代码应该怎么工作。”
而Characterization Test更接近:
“代码现在实际上怎么工作。”
这两者区别很重要。
例如当前系统:
订单不存在时返回:
HTTP 200 code = ORDER_NOT_FOUND你可能觉得这不合理。
但重构前可以先把这个行为锁下来。
不是说以后永远不能改。
而是:
如果要改,必须明确知道自己正在改变兼容行为。
这样重构就从:
“无意识破坏”
变成:
“有意识变更”。
五、行为基线不只包括API输出
这是最容易缩窄的地方。
很多人想到Behavior Baseline,只想到:
接口返回值。
其实真实项目里至少还应该看几类。
API行为
状态码。
字段结构。
错误语义。
数据行为
写哪些表。
字段默认值。
事务边界。
历史数据兼容。
副作用
是否发消息。
是否调用第三方。
是否写缓存。
时序行为
同步还是异步。
先写库还是先发事件。
性能行为
某些核心链路能不能接受重构后的额外耗时。
因为有时候代码功能完全正确,
但从300ms变成3秒,
一样不能算成功重构。
六、先锁“必须保持”,再决定“允许改变”
旧项目重构最实用的一个动作是:
把行为分成两类。
第一类:
Must Keep
必须保持。
比如:
旧API兼容。
历史数据可读。
同一请求不能重复扣款。
第二类:
Allowed to Change
允许改变。
比如:
内部类结构。
方法命名。
模块拆分。
缓存实现。
这样Agent就不会把所有“现状”都当成必须永久保留。
也不会把所有“不好看”都当成可以随便改。
真正的重构自由应该是:
实现可以大改,关键行为不能偷偷变。
七、一个比较稳的重构流程
我现在更推荐让 ChatGPT、Codex 按这个顺序做:
Capture Current Behavior
先收集现有行为。
↓
Define Must-Keep Behavior
确认哪些行为必须保留。
↓
Refactor
再开始改实现。
↓
Compare
新旧结果对比。
↓
Accept
确认行为没有意外变化,再接受当前Change Set。
这个顺序和直接:
“读代码 → 重构 → 跑测试”
最大的区别是:
重构前已经有了比较对象。
否则测试一旦不完整,
你甚至不知道Agent到底改变了什么。
八、为什么旧项目测试越少,越应该先做行为基线?
很多人会觉得:
项目测试都没有,怎么做基线?
恰恰相反。
测试越少,越不能直接大改。
因为这个时候唯一能保护你的不是原来的Test Suite,
而是:
先补一批关键行为快照。
不需要一开始就把覆盖率补到80%。
只要先锁几个真正关键场景:
核心API。
核心数据写入。
关键异常。
关键副作用。
历史兼容。
就已经比裸重构安全很多。
九、可以让Agent先做“只读阶段”
复杂旧项目我比较喜欢先给Codex一个明确限制:
先不要修改代码。
先完成:
- 找核心入口;
- 列关键调用链;
- 识别外部依赖;
- 找关键副作用;
- 收集现有测试;
- 输出建议锁定的行为基线。
这样第一阶段的目标不是:
产出Diff。
而是:
产出对当前系统的理解。
确认以后再进入Write阶段。
这会明显减少:
刚读几个文件就开始大范围重构。
十、行为基线还能帮助Review
Agent一次改几十个文件以后,
单纯看Git Diff特别累。
但如果前面已经定义:
“这5个行为必须保持”
Review就简单很多。
你不再需要逐行确认所有代码。
而是优先验证:
这些关键行为有没有变化。
比如:
- 老客户端还能不能调用;
- 历史数据还能不能读;
- 重复请求是否仍然幂等;
- Event Schema有没有变化;
- P95有没有明显恶化。
这相当于把Review从:
代码中心
变成:
行为中心。
十一、什么时候应该允许行为变化?
行为基线不是让旧项目永远不能改变。
如果某个旧行为本身就是Bug,
当然可以改。
但变化最好显式化。
比如:
OLD: 订单不存在 → HTTP 200 NEW: 订单不存在 → HTTP 404然后明确:
哪些客户端需要升级。
何时切换。
如何灰度。
如何回滚。
这和Agent直接“顺手修正”是完全不同的。
前者是:
Planned Behavior Change
后者是:
Accidental Behavior Change
真正要防的是后者。
十二、给自己看一个指标:行为保持率
这篇只看一个指标:
Behavior Preservation Rate——行为保持率
可以简单理解为:
重构前确认必须保持的关键行为中,重构后仍然保持一致的数量 ÷ 必须保持的关键行为总数
例如重构前锁定10个关键行为。
完成以后:
9个保持一致。
1个在Review时发现已经变化。
那么:
行为保持率 = 90%。
对于关键兼容行为来说,
这个数字最好尽量接近:
100%。
因为这些本来就是你提前定义的:
不能被无意改变的部分。
十三、行为保持率低,先别急着继续重构
如果每改一轮:
老接口坏一个。
历史数据出一个问题。
消息格式又变化。
说明真正的问题不是:
Codex修改能力不够。
而是:
行为边界没有建立好。
这时候继续让Agent多改几个模块,通常只会增加Review成本。
更值得先回去补:
Behavior Baseline。
Characterization Test。
Must-Keep List。
再继续下一阶段。
十四、Plus和Pro怎么判断?
如果你的场景主要是:
偶尔接手一个旧项目。
做局部重构。
一次只改少量模块。
主要让 ChatGPT、Codex 帮你读代码、补Characterization Test、分析影响范围,
这种使用强度下,Plus通常已经够用。
真正值得先做的是:
把关键行为锁清楚,
而不是直接追求更大的AI容量。
如果你的实际工作已经变成:
长期维护大型旧项目。
一次重构涉及几十个文件、多个模块。
需要Agent持续分析、修改、跑测试、做行为对比、回归验证。
同时还有多个复杂Change Set排队,
这时候Pro会更适合。
因为更多任务容量可以真正用于:
长时间重构。
多轮验证。
大范围代码理解。
但前提仍然是:
Behavior Baseline先建立起来。
否则Agent改得越快,
你只是更快地失去:
“到底什么不能变”的判断能力。
最后
旧项目准备大重构,
为什么第一步反而不是改代码?
因为旧系统最危险的地方往往不是:
代码有多乱。
而是:
你已经不知道哪些行为其实不能动。
所以真正稳的重构起点应该是:
先观察。
先记录。
先锁定关键行为。
再开始改。
让 ChatGPT、Codex 先回答:
“这个系统现在实际上怎么工作?”
再回答:
“我们准备把它改成什么样?”
当Behavior Baseline存在以后,
重构才真正从:
把代码写得更漂亮
变成:
在不破坏关键行为的前提下,把系统改得更好。
这才是旧项目大规模Agent重构真正值得守住的第一条边界。
持续分享 Codex、大模型开发与 AI 编程实战内容。
长期深度使用各类代码大模型,也整理了稳定的Plus/Pro会员订阅渠道,有需要可自取。