2292 字
约 7 分钟
15
S24-A23 · AI 与遗留代码:先理解还是先改

一个 8 年历史的模块要"加一个字段",有人直接把相关文件丢给助手,让它"加上字段并适配"。它改了 14 个文件,编译通过——而其中一处改到了"给旧版客户端返回的兼容分支",于是老版本 App 直接解析失败。

遗留代码的特殊性不在"老",而在"它有看不见的约束":兼容分支、历史数据、第三方对接、以及"某个诡异的写法为什么存在"。AI 能"读代码",但"读不出约束"——这个"读不出约束"是 AI 的结构性局限:它只看"现在这份代码",而"为什么这么写"发生在"过去的某次会议"里——于是它给出的"适配"是"按当下代码形状推的",而不是"按约束推的"。
而那些约束正是"改坏"的地方。


1. "看不见的约束"的三种来源

来源 表现
兼容分支(版本/客户端) if (version < 3) 之类
历史数据形状 某些字段"可能为空/可能是脏值"
业务约定(口头) "这个字段别动,财务对账用"

第三项是"最没法读出来"的:它不在代码里,而在人的记忆里第三项的"补救"只有一个:问人。而"问谁"要看"这模块多久没人动"——于是"找出最后一个提交者"是最快的路径。
——于是"AI 读完所有代码"也不会知道。

2. "先理解"的三种手段

手段 说明
让 AI"画出调用图与数据流" 快速建立结构
让 AI"解释这段为什么这么写" 会有推测,需核对
用"测试/日志"验证理解 最可靠

第二项的"危险"在于"解释会让人放心":"为性能考虑"听起来合理,于是不再追问——而实际可能只是"当年随手写的"。所以它的输出要当"假设",不是"结论"。
第二项要当心:它会给"看似合理的解释"("为了性能"、"为了兼容"),而真实原因可能只是"当年写错了"。

第三项是"唯一可靠"的:跑一遍、看日志、加断言——于是"理解"变成"可验证的"。

// "理解"的三个可验证动作(比"读代码"可靠)
1 找出"这个模块的入口"(谁调它、被谁调)
2 跑一次真实请求,记录"经过哪些分支"
3 对该分支加一行日志/断言,验证"我的理解对不对"
// 判据:能否说出"这段代码在什么条件下会走到"

3. "先改"的三种风险

风险 后果
改到兼容分支 旧客户端崩
改到"脏数据"路径 线上数据异常
改到"没人懂的那段" 引入更隐蔽的 bug

第一项的"识别"是"搜"version"、"apiLevel"、"old"、"legacy"这类词":它们往往标记着"兼容分支"——而 AI 未必会把这些当作"不可动"。
第一项在"多版本共存"的系统里最常见:代码里有两套逻辑,而"哪套在跑"取决于"客户端版本"。

"提示"要写"约束清单",否则 AI 会"按代码形状"改:

// 交给助手时的"约束前置"(四行,成本 1 分钟)
约束:
1 不得修改"兼容分支"(含 version/apiLevel/legacy 的 if)
2 不得修改"给旧版客户端的响应字段"(见 api-v2.md)
3 历史数据可能为空/超长,新代码必须判空
4 只允许改"新逻辑路径",旧路径保持行为不变
// 收益:它从"按形状改"变成"按约束改",diff 规模通常减半

4. "小步改"的三种做法

做法 效果
先加"观测"(日志/指标),不改逻辑 最安全
加"开关",新旧并存 可回滚
一次只改一个分支 可定位

第一项是"被低估"的:先让"这段代码的行为"可见(打点、日志),再改第一项的"具体做法"是"给每个"可能受影响的分支"加一个计数":于是"改前/改后"的分布差异立刻可见——而它不需要"理解全部逻辑"。
——于是"改坏了"能立刻看出"哪条路径变了"。

// 第一步:不改逻辑,只加观测
if (legacyPath) {
    metrics.counter("legacy.path.used").increment();   // 先知道"它在跑"
}
// 第二步:加开关(新旧并存)
if (flags.newCalcEnabled() && !legacyPath) { ...新逻辑... } else { ...原逻辑... }
// 第三步:灰度放大,观察指标后再删旧分支

5. "旧分支何时能删"的三种判据

判据 说明
调用量为 0(连续 N 天) 最硬
客户端版本分布(旧版本 < 1%) 需产品拍板
有"数据迁移"完成证据 需数据侧确认

第一项的"门槛"要看"流量周期":"30 天 0 次"对"日活型"系统够,对"月结型"系统要"3 个月"——否则会"删掉每月跑一次的分支"。
第一项是"最可靠"的:"这个分支最近 30 天调用 0 次"——而它需要"你先加了观测"。

"调用量"这类证据要"能在删之前拿到",而不是"删完再看":

// 三步拿到"可删证据"(按周看)
1 加计数:分支进入时打一个 counter(一次提交)
2 观察:连续 4 周该 counter 为 0(且总流量无异常)
3 删除:删分支 + 保留"回滚一次"的能力(镜像/开关)
// 判据:没有第 1 步,第 2 步永远拿不到 → 于是"没人敢删"

6. 排错:五种"改遗留代码的问题"

现象 成因 处理方向
老客户端崩 改了兼容分支 加观测 + 开关
线上数据异常 脏数据路径 先摸数据形状
改完没人敢删 无"调用量"证据 先加观测
越改越乱 一次改太多 一次一分支
评审看不出风险 diff 太大 拆 PR

第五项很实际:**AI 一次改 14 个文件,评审只能"看个大概"**第五项的"修法"是"按"风险面"拆 PR":兼容相关一个 PR、新逻辑一个 PR、清理一个 PR——于是"每个 PR 都能被真正评审"。
——于是"风险"被"diff 规模"掩盖。

"理解"还有一个"低成本"的入口:让 AI"生成一份"行为清单",然后你只核对"关键几条"。

// 一份"行为清单"(用来核对,而不是用来信)
1 入口:两个(HTTP /api/order、定时任务 reconcile)
2 分支:version<3 走旧逻辑;金额>10 万走人工审核
3 外部依赖:支付网关(超时 5s)、库存服务(重试 2 次)
4 数据假设:status 可能为空字符串(历史脏值)
// 用法:逐条"验证"(跑一次/查日志),验过的才当作约束

7. 边界:什么情况可以直接改

场景 为什么 做法
纯新增(无旧路径) 无兼容影响 直接改
有新测试覆盖的模块 出错会被测出 直接改
内部工具(无外部版本) 无兼容包袱 直接改
已确认"无人调用"的代码 无影响面 直接删

最后一条与"知识"有关的边界:改完要"补记录":"为什么这个分支不能删"写进注释或 ADR——否则下一个接手的人(和下一个 AI)会再踩一次。
判断口径一句话:"这段代码的行为,会不会被'我看不到的东西'依赖"。会(老客户端、外部对接、历史数据),必须先"让它可见"再改;不会(内部、纯新增),可以直接改。


最后一个"协作"现实:遗留代码的"约束"往往掌握在"不太写代码的人"手里(运维、财务、老员工)。所以"先理解"不只是"读代码 + 问 AI",还包括"找到那个知道'为什么'的人"——而这一步"AI 永远替代不了"。

8. 自测

  1. "看不见的约束"的三种来源里,哪一种"读代码也读不出来"?
  2. 让 AI"解释这段为什么这么写"有什么风险?什么是"唯一可靠"的理解方式?
  3. "先加观测再改"为什么被低估?它让"改坏了"变成什么?
  4. "旧分支何时能删"的三种判据里,哪一种最硬?它依赖什么前提?
  5. 为什么"AI 一次改 14 个文件"会让评审失效?
  6. 判断"能否直接改"的那个问题是什么?
S24-A23 · AI 与遗留代码:先理解还是先改
https://www.clxhxhhr.top/posts/6631/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。