老系统改造:先证明能改,再谈怎么改
改造方案写得再细,也绕不过第一步:证明这个项目是可做的。多数项目在立项那天就已经注定要烂尾,只是要到第四个月才看得出来——因为没人要求它在开工前回答几个基本问题。
我现在的做法是把立项拆成两段:先过门槛,再做盘点。门槛不过就不该有第二段。
开工前必须先过的三关
这三条不是风险项,是前置条件。缺任何一条,正确动作是先去补它,而不是带着缺口开工。
| 哪一关 | 怎么算过 | 没过先做什么 |
|---|---|---|
| 流量能不能切 | 改造范围内每一类入口,都有一个你能改配置的收口点(网关、RPC 路由、任务调度开关) | 收口。没有统一入口的系统,改造无法分批,只能大爆炸 |
| 改前改后的行为能不能比对上 | 有一种办法,能对同一个输入拿到新旧两份输出并且可比 | 先建这套比对能力(黄金样本、影子读),这件事要排在动手改造之前 |
| 数据能不能退 | 写路径退回后,新格式写下的数据旧实现还能正确读 | 设计兼容期(先扩后收),否则中途出事就是单向门 |
三条之间是「与」的关系,而且是乘法:只有前两条成立、第三条不成立的项目,会在第一次真实回滚时暴露它有多贵。
第二条评论里最容易被糊过去。常见说法是「我们有集成测试」——然后你发现测试覆盖的是新系统该有的行为,不是老系统现在实际的行为。这两者的差距就是改造期的事故来源。所以这一关真正的标准是:能对真实输入拿出两份可比对的输出,而不是「有测试」。
四类盘点,每类都要拿出一份能核对的东西
「我先把系统梳理一遍」不是任务,因为它没有完成条件。盘点的四类清单,每类都必须落到一个能对外核对的东西上。
一、资产清单。 产出物是入口表:每一行是一个真实入口,字段至少包含类型、来源证据、近期调用量、下游是谁。
类型必须列全,最容易漏的是这四类:定时任务、MQ 消费者、批处理脚本、运维手工执行的 SQL。前两类不在任何接口文档里,最后一类只能靠问和靠审计日志。
采集方式要定死:入口清单来自线上真实流量的记录,不来自人工回忆。 代码里搜出来的注解只是候选,网关和链路上统计出来的调用才是事实。两边对不上的那些就是漏项。
二、依赖清单。 产出物是方向化的调用图,加一份单独的「直连我的数据」列表。
第二份列表比调用图重要。调用图能从链路追踪里生成,直连库的关系在任何代码里都搜不到——报表平台、BI 任务、隔壁团队的取数脚本、被导出成 Excel 的定时查询,都只存在于数据库的连接日志里。这一项必须从 DB 侧采集(连接来源、账号、正在跑的语句),不能从应用侧推。改造把表结构一改,最先炸的通常是这里,而且炸的时候对方说不出这张表是谁的。
三、契约清单。 产出物是契约表:接口、字段、顺序、精度、错误码、时区、幂等键,每条后面写「谁在用」和「这个判断的证据是什么」。
第二列才是重点。「没人依赖」是一个需要证据的结论,不是需要点头的结论。证据只有三种:调用来源清单为空、字段在采样窗口内未被读取、对方明确签字确认可以动。写「看着没人用」的,等于没做这一项。
四、数据清单。 产出物是表画像:行量、日增、读写比、冷热分布、唯一键由谁生成、有没有历史脏数据、当前索引与实际查询的匹配度。
其中「唯一键由谁生成」决定了改造能不能自动补数据。如果业务主键是人手填的,那就没有任何唯一性保证可以依赖,所有去重逻辑都要按「可能重复」设计。
最容易漏的五样:没人写在文档上的约定
这一节就是上一节「契约清单」里最难盘的那部分,也是真正在改造中途杀出来的东西。它们的共同特点是:代码里看不出它是契约,改掉的那天才发现它是。
| 哪种约定 | 怎么断的 | 怎么查 |
|---|---|---|
| 靠字段名反射/序列化 | 重命名或换 DTO 即断,编译期无感 | 全量搜反射与序列化入口,再看外部对接方的样例报文 |
| 定长或分隔串按位置解析 | 加一个字段就把下游所有列错位 | 找出所有非结构化的字符串出参,逐个确认解析方 |
| 错误文案被字符串匹配 | 改一句提示,对方的重试逻辑反了 | 错误码消费方清单;只看码不看文案,是改造期的临时保险 |
| 时间与时区、精度、排序规则 | 值本身没错,比较结果变了 | 采样对比新旧两侧同一批数据的字节差异,而不只看逻辑相等 |
| 幂等键由调用方拼出来 | 换生成规则就重复下单 | 找出所有由外部拼接的请求号,它们的格式就是接口 |
这五样没有一样能靠读代码读全。 采集必须是「线上真实调用统计 + 挨个问」两条腿,只走一条的盘点结论都不可信——这条我自己也犯过,第一次做的时候只查了代码,上线当天被「按位置解析」打断了一次。
立项文档必答六个问题
评审时我只看这六条有没有答。答案质量可以商量,空着就是没立项。
- 这次改造的范围边界,用入口怎么描述?(用模块描述的,退回重写)
- 改完之后,凭什么判断它和原来等价?证据由谁看?
- 中途出事故,退回一次要动几个配置、几个发布、涉及几个团队?计时结果有吗?
- 双维护期(新旧同时跑)要多花多少人力,谁出?
- 这个项目最迟在什么时候、什么条件下中止?
- 如果不做,最坏结果是什么?发生概率和时间点?
第 6 条经常给出一个反直觉但正确的结论:不做。
「不改」是合法结论
这三种情形下,正确的方案是不改造:
- 它快被淘汰了。 生命周期不足一年、且业务能力会随新系统整体替换的模块,改造投入是纯沉没。这时该做的是冻结变更 + 加一层适配挡住上游。
- 痛不在被改造的那一环。 系统难维护的根因是流程(需求没有验收标准、上线没有回滚通道),换架构解决不了它,只会把同一个问题带进新栈。
- 改造范围收不拢。 当你发现「动哪儿都会牵连全局」,那说明真正该先做的是收口和拆依赖,改造本身要往后排。
写方案时要允许「不改」作为一个被论证过的选项出现。一份只论证「该怎么改」的方案,评审时是不完整的——它没有回答第 6 问。
怎么算盘点做完了
三条,全部可被第三方核对:
- 随机指定改造范围内的一张表,你能在十分钟内说出它被哪些来源读取,且其中至少一个来源不是从代码里来的。
- 入口清单能与采样窗口内的实际流量对上:任何出现在流量里、却没在清单上的入口都为零,或者你能逐个解释为什么排除。
- 契约表里每一条「没人依赖」的判断,后面都跟着证据种类和查它的时间。
三条都过了,才有资格进入下一件事:把改造拆成能验收的批次(这一步的切分方法另有一篇专讲)。切分粒度的错法比盘点的漏项更致命。
本文作者:Aryee 发布时间:2026-09-24 20:57
本文出处:ARYEE.cn 固定链接:https://www.aryee.cn/archives/legacy-modernization-intake.html
转载或引用请连这一行一起带走;文中的代码与结论按当时环境成立,组件升级后请以官方文档为准。