2/3 的工程师都踩过这个坑:AI 改老代码,越改越危险!
AI Coding 正在快速进入研发流程。
对新项目来说,它可以快速生成代码、补充测试、提升开发效率。但在大量真实企业场景里,研发团队面对的并不是一个干净的新项目,而是一个运行多年、依赖复杂、文档缺失、夹杂大量历史逻辑的老系统。
每个做过老项目的工程师,都熟悉这三个瞬间:
第一个瞬间:想改一个模块,翻遍仓库找不到文档。唯一懂它的人早就离职了——或者就是半年前的你自己,你也忘了当初为什么这么写。
第二个瞬间:代码里散落着大量补丁和“看起来很奇怪”的分支。删又不敢删,留又说不清为什么要留。每次改这里都像拆弹。
第三个瞬间:上线后客户报障。排查半天发现——某个为大客户定制的特殊逻辑,在最近的重构中被“优化”掉了。没人记得它的存在,直到出事。
如果你经历过其中任何一个,恭喜你:你不是一个人。
01
2/3 的工程师,都踩过同一个坑
七牛云在内部做过一轮面向一线工程师的调研,重点了解大家使用大模型处理老代码时的真实体感。这组数据不是严格统计学意义上的行业样本,但能反映研发一线非常典型的顾虑。
三大障碍,拦在 AI 和老代码之间:
输出不稳定,不敢直接用——28.6%
没有测试,无法验证对错——21.4%
上下文装不下,看不全代码——17.9%
这三个问题加起来,接近七成的工程师在使用 AI 处理历史代码时感到“不踏实”。
但最危险的不是这三个。
最危险的是:2/3 的工程师亲历过——大模型会“悄悄丢掉”特殊逻辑。
你让它重构一段代码,它高高兴兴地给你优化得干干净净。你看了一眼,结构清晰、命名规范,完美。提交、上线。
然后在某个你完全想不到的时刻,一个特殊条件触发了——线上炸了。
原因并不是模型故意犯错,而是它缺少历史上下文。大模型倾向于把缺少解释的特殊分支,理解成重复、冗余或可以简化的逻辑。当那些为历史兼容、客户定制、临时救火而存在的非典型代码没有被显式标注时,它很容易在优化过程中把这些逻辑一起清理掉。
损伤发生在改动当下,暴露却在很久以后。这就是旧仓库治理最危险的地方——它有潜伏期。
02
问题的本质:两条恶性循环链
把这些现象拆开看,本质上是两条恶性循环:
第一条链:理解不足 → 上下文装不下 → 越不敢用 → 理解越不足
老代码没有文档,你把整个仓库塞给大模型,上下文装不下。它只能看到局部,盲人摸象。你不敢让它改大的,只能改小的。改完也不补文档,下一次还是从零开始考古。
第二条链:缺测试 → 无法验证对错 → 不敢改 → 测试更缺
老代码先天缺测试。没有测试基线,你就不知道大模型改得对不对。越不知道对不对,越不敢让它改。越不改,测试越补不上。
两条链绞在一起,就形成了一个死局:你知道 AI 写代码很快,但在你的老项目上,它就是用不起来。
某模型大厂的分享印证了同一个结论:AI 写代码的速度至少是人的 10 倍,但人均需求吞吐率只提升了 1.6 倍。
10 倍的生产力工具,只产出了 1.6 倍的效果。中间的差距去哪了?
答案是:Vibe Coding 离可交付,还差得很远。
基础正确率可能有 80 分,但可交付性——可维护性、工程规范性、兼容性——只有 40 到 60 分。差的那几十分,需要一套“安全带”来补上。
03
四层治理框架:从“不敢改”到“可验证”
七牛云在一个有着十余年历史的视频直播核心项目上,摸索出了一套四层递进式的历史代码治理框架。
这个项目的特殊性在于:虽然经过多轮架构升级,但至今仍要兼容七年前甚至十年前的播放逻辑——是典型的“业务不能停、代码不敢动”的历史负债重灾区。
整套框架的核心逻辑是:先看懂,再锁住,再动手,最后固化成机制。
第一层:理解层——先画地图,再读代码
很多人拿到老代码的第一反应是:直接让大模型读。
正确的方式是:结构优先,先画地图。
不要一上来就扎进细节。先让大模型帮你梳理:这个系统有哪些模块?入口在哪?调用链怎么走?模块之间用什么协议通信?哪些是高风险区域?
就像你去一个陌生城市,先看地图,再逛具体的街。
建图的正确路径:
工具提取骨架——用 rg/grep、ctags、依赖图等工具先把符号、入口和结构抽出来
按模块分批送 LLM—— 每次只喂一个模块,不要贪多
逐块沉淀地图——每个模块产出一份 md 格式的“地图”
汇总层用地图理解地图——上下文换成摘要,不再是源码
CLAUDE.md 变路由表——后续会话按任务加载相关地图片段
而且文档要分层:
基础层:每个模块是干什么的
入门层:模块之间的交互关系
进阶层:模块内部的实现细节
高阶层:特殊逻辑、哪些不能动、为什么不能动
分层的好处是:以后大模型再读的时候,不用每次都把所有细节吃一遍。需要什么层级,加载什么层级。上下文装不下,靠的不是更大的窗口,而是更高的信息密度。
第二层:守护层——把隐性逻辑变成显式约束
这是整个框架里最关键的一层。
如果说理解层解决的是“看不懂”的问题,守护层解决的就是“不敢改”的问题——也就是 2/3 工程师踩过的那个坑。
核心思路只有一句话:把藏在代码里的隐性逻辑,变成大模型“看得见”的显式约束。
分三步走:
第一步:识别
让大模型扫描代码,列出所有“可疑”的点:
客户定制的逻辑
历史兼容的分支
灰度开关
特殊错误码
fallback
要求很简单:标出位置、触发条件、影响字段。不确定的地方明确标「不确定」,不许脑补。
第二步:建清单
把扫描结果整理成一份“不可破坏清单”(也叫分支账本),然后逐条人工确认:
这个是客户定制吗?哪个客户?
这个是历史兼容吗?兼容谁?
这个可以清理掉吗?
这个必须保留,绝对不能动?
这一步必须有人工介入。大模型能帮你找,但它没法替你判断“这个客户重不重要”、“这个兼容还要不要”。
第三步:变测试
最关键的一步:把每一条“不可破坏”的行为,转成行为快照测试。
不是测“功能能不能跑”,而是测“关键输出字段和旧行为是否完全一致”。
为什么要这么做?因为 Prompt 声明是最脆弱的防线。
你在对话里跟大模型说“这个逻辑别动啊”,它当时点头了。下一次对话、下一个 PR、换一个模型——它就忘了。
口头声明只活在单次对话里。保护规则必须外化到代码注释和测试中,让大模型每次读上下文时都“看得见”,让 CI 每次提交时都“查得到”。
第三层:改造层——小步、可审、可回退
理解和守护都到位了,终于可以动手改了。
但别急。改造层的核心原则是:宁可慢,不能乱。
原则一:最小改动
每次只改一个点。重构就是重构,修 bug 就是修 bug,绝对不要混在一起。
大模型特别容易做“顺便优化”。改着改着发现旁边还有可以整理的地方,就一起改了。问题一旦出现,定位成本会被显著放大。
明确禁止“顺便优化”无关代码。
原则二:diff 行为审查
改完先看 diff,但看的不是“代码优不优雅”——而是“行为变没变”。
重点检查:
有没有删掉特殊分支?
有没有改默认值?
调用顺序变了吗?
重试次数、超时时间改了吗?
fallback 逻辑还在吗?
把大模型从“审美模式”拉回“行为风险控制模式”。
原则三:多视角 review
不要让同一个模型既写又审。让第二个模型专门负责挑毛病——用“攻击性评审”的思路,主动找弱点,而不是期待它自我纠错。
复杂改动,用更强的模型来审。关键路径,必须人工逐行过。
原则四:灰度兜底
本地通过 ≠ 线上安全。这是我们调研中发现的一个被严重低估的盲区——89.3% 的人依赖测试,60.7% 依赖 Code Review,但只有 10.7% 会用灰度、开关、监控。
高风险路径,一定要加开关、加日志、加灰度。先放 1% 的流量跑一跑,盯着监控没问题再全量。或者新旧入口并行,先灰度回归再替换老入口。
第四层:工作流层——把做法固化成机制
前三步做得再好,如果只存在于某几个人的脑子里,那就是个人经验,不是团队能力。
人一走,方法就没了。
所以第四层要做的,是把做法固化成机制:
文档驱动:想和干分开
先让大模型出方案文档(md),人工审核确认了,再让它动代码。
每个模块都要有一份喂给大模型的 md 文件,写清楚约束、目标、测试流程。设计用高级模型,写代码用普通模型——没关系,文档沉淀下来了,就是资产。
人工确认闭环
大模型只产出草稿。最终的文档和代码变更,必须经过人工确认节点,不直接落库。
这一步确实会慢一点,但它是必须的卡点。不审阅的话,你的“理解债”会越来越高——越积越看不懂,越看不懂越积。恶性循环就是这么来的。
资产回流
每次治理结束后,模块地图、分支账本、不可破坏清单、行为快照、灰度记录和复盘结论,都要回流到仓库或知识库里。
否则这次改完了,下次还是从零开始考古。
CI 持续守护
把“不可破坏清单”接进 CI。变更触及已标注的特殊逻辑,自动触发相关测试,并要求人工 review,产出特殊逻辑覆盖率报告。
可以参考 Danger 的做法——在 CI 里跑自定义规则脚本,将 invariants.yml 与git diff做交集,命中即触发对应快照测试并标记强制 review。
04
风险分级:什么能让 AI 干,什么必须人来
不是所有代码都值得用同一套标准对待。我们按风险等级划了四档:
这里的底线是:
没有 owner,不改。
没有验证,不改。
没有回滚,不进生产。
越接近业务契约、资金安全、权限安全、客户定制和线上副作用,越不能让模型凭“看起来等价”自行判断安全性。
05
启动指南:两周试点法
道理都懂了,从哪下手?
我们的建议是:选一个有代表性但不致命的模块,做两周试点。
不要一上来就啃最硬的骨头。先找一个有 owner、有验证入口、出问题影响可控的模块,把整套流程跑通。
第一周:完成试点启动与风险显性化
第 1 天,选模块:选择有代表性但不致命、有 owner、有验证入口的模块
第 2 天,定边界:写清本次看什么、不看什么,哪些行为绝对不能改变
第 3 天,收上下文:收集代码、文档、PR、工单、日志、配置、测试样例
第 4 天,出地图:产出入口、调用链、依赖、高风险区域、不确定点
第 5 天,列账本:记录特殊分支、触发条件、影响行为、待确认问题
第一周结束,你应该拿到:试点范围说明、模块地图、分支账本、待确认问题清单、初步的不可破坏清单。
然后问自己三个问题:范围清楚吗?owner 确认了吗?下周具备补快照的条件吗?
答案都是“是”,再进入第二周。
第二周:完成清单、快照和小步改造
第二周重点不是大规模重构,而是把第一周发现的问题转化为可验证资产,并选择一个最小改造点验证流程。
第 6 天,确认不可破坏清单:逐条找 owner、历史材料或线上证据确认,不确定项继续保留
第 7 天,补行为快照:把关键输入、输出、错误码、fallback、默认值和边界条件记录下来
第 8 天,转验证项:能自动化的写成测试,不能自动化的形成验收 checklist 或灰度观察项
第 9 天,做最小改造:只选择一个目标,不混入顺手优化,不做无关格式化
第 10 天,review 与复盘:检查 diff 行为风险,确认灰度和回滚方案,把地图、账本、清单和快照回流到仓库
第二周结束,不一定要完成一个大重构,但应该证明三件事:
团队能看清这个模块的主要风险
关键历史行为已经有保护方式。
后续改造可以按小步、可审、可回退的方式继续推进。
这样,AI 不是直接“接管老系统”,而是帮助团队建立一套可以持续复用的治理机制。
06
写在最后:AI 来了,AI 看见了,AI 还没征服
JetBrains 有一句总结很精准:“AI came. AI saw. AI hasn't conquered.”
85% 的开发者已经在定期使用 AI 工具,34% 的代码来自 AI 生成,但 99% 的开发者对 AI 仍有担忧,46% 明确不信任 AI 输出的准确性。
这不是“不用”,而是“用但不放心”。
企业级落地的瓶颈,已经不只是“能不能生成代码”,而是“能不能验证生成的代码是对的、可交付的”。
历史代码的治理,就是这场验证战的最前线。
整理文档、梳理分支、补测试、加灰度——这些“笨功夫”,才能把大模型从“看起来很厉害的玩具”,变成“真正能扛事的生产力”。
毕竟,写得快不算本事,上线不出事才算。
历史代码治理的难点,不在于让 AI 写得更快,而在于让团队更快地看清系统、更稳地保护历史行为、更可控地推进改造。
所以,我们对 AI 的期待不应该是:替我们一次性重写老系统。
更现实、也更有价值的期待是:让 AI 帮我们把隐性知识变成显性资产,把不可控风险变成工程约束,把一次次个人经验沉淀成团队能力。
当做到这一点,历史代码就不再只是负债。它会逐渐变成一套可理解、可验证、可演进的工程资产。
推荐阅读