别急着重写!一个老程序员眼中的遗留系统现代化"攻守道"
前阵子,一位老朋友给我打电话,声音里透着焦虑:”我们那个十年的老系统,性能越来越差,代码像意大利面,老板终于批了预算,让我们重写。你觉得用微服务+云原生怎么样?”
我笑了笑,这场景我太熟悉了。过去二十年里,我参与过十几个遗留系统的改造,有的成功了,有的成了”世纪烂尾工程”。最让我心痛的一次,我们花了18个月重写,新系统上线那天,老系统欢快地跑着我们死活搞不定的复杂业务逻辑,而我们精心设计的微服务架构却在第一波真实流量下就趴窝了。
所以今天想聊聊,面对遗留系统,我们到底该怎么玩这场”现代化”的游戏。
战略篇:先当考古学家,再当建筑师
1. 别被”技术债务”这个词骗了
每次听到”技术债务”就想笑,这词太有迷惑性了,好像欠了钱还清就行。但真实的遗留系统更像是一座活着的城市——里面有错综复杂的地下管线(业务规则),有居民的生活习惯(用户操作路径),还有无数你不知道的”违建”(临时补丁)。
我现在的习惯是,接手一个遗留系统,前两周不写一行代码,就干几件事:
- 埋点监控:在关键接口埋上监控,看看真实流量长什么样。有一次我们发现某个”即将废弃”的接口每天被调用300万次,是一个第三方合作伙伴的核心依赖。
- 数据考古:把生产数据库的表结构、数据分布、慢查询全扒一遍。最震撼的一次,发现某个varchar(255)字段里塞了JSON、XML、逗号分隔值三种格式,就因为有三个不同的业务方在复用。
- 找人聊天:请业务方的老员工喝咖啡,听他们讲系统里的”都市传说”。相信我,比看代码管用。
2. 重写还是重构?这是个伪命题
早年我是个”重写派”,觉得老代码看一眼都脏眼睛。直到2015年,我主导重写了一个电商订单系统,用了当时最潮的DDD+Event Sourcing。结果上线后发现,老系统里有个300行的”垃圾”函数,完美处理了十几种促销叠加的边界情况。我们新系统花了三个月才复现这个逻辑,还bug频出。
现在我信奉一个原则:能抽象就不重写,能绞杀就不爆破。
决策树很简单:
- 业务模型稳定吗?不稳定 → 绞杀者模式
- 团队对领域理解深吗?不深 → 抽象分支+防腐层
- 老板给的时间超过2年吗?不超过 → 别想了,渐进式改造
战术篇:绞杀者模式与抽象分支的实战
绞杀者模式(Strangler Fig Pattern)
这个名字太形象了——像榕树一样,新系统慢慢缠绕老系统,最终绞杀它。核心就一句话:所有新功能走新系统,老功能逐步迁移。
去年我们改造一个老旧的会员系统,就是这么玩的:
1 | // 防腐层:新老系统之间的翻译官 |
这个Facade就是我们的”绞杀藤”。新功能我们只在NewMemberService里实现,老功能逐步迁移。最妙的是,我们可以通过配置随时回滚。
抽象分支(Branch by Abstraction)
这招更温和,适合那种”牵一发而动全身”的核心业务。基本思路是:先抽象接口,再替换实现。
记得改造一个价格计算引擎,老代码是这样的:

1 | // 老代码,遍布业务逻辑 |
我们没动它,而是这样做:
1 | // 第一步:抽象出接口 |
用了六个月,我们把折扣、促销、会员价一个个抽出来,老系统纹丝不动,新逻辑已经跑起来了。最后切换开关那天,紧张地盯着监控,结果流量平滑过渡,连业务方都没察觉。
那些年我踩过的坑
坑1:数据迁移的”最后一公里”噩梦
第一次做系统拆分,我们花了三个月把订单数据从单表拆成订单主表+扩展表+明细表。迁移脚本跑了整整一夜,第二天自信满满地切流量,结果瞬间被投诉淹没——原来有个报表系统直接连我们数据库用SQL做统计,新表结构它完全不认。
教训:数据迁移不是技术问题,是生态问题。现在我的 checklist 里必有这一项:列出所有直连数据库的”野生动物”——报表、BI、数据仓库、甚至某些领导的Excel直连。
坑2:过度设计的中台陷阱
2018年,我们团队迷上了中台概念,想把用户、订单、商品全抽象成”能力”。结果用户中台上线后,一个简单的注册流程要调七八个服务, latency 从200ms飙到2s。更惨的是,不同业务线对”用户”的定义完全不一样,中台团队天天在协调需求,成了瓶颈。
教训:抽象是有代价的,别为了技术理想主义牺牲业务灵活性。我现在坚持:先让业务跑起来,再谈复用。重复代码有时候比错误的抽象好一万倍。
坑3:双写 consistency 的隐形炸弹
双写(同时写老库和新库)是绞杀者模式的标配,但也是个雷区。有一次,新系统写入失败,我们记录了日志准备补偿,结果运维误删了日志文件,导致2000多条订单数据在新系统里缺失。虽然老系统有数据,但两边的ID对不上,修复花了整整一周。
解决方案:我们现在用消息队列+对账模式:
1 | public void updateMember(MemberInfo info) { |

这套组合拳下来,数据一致性从”尽力而为”变成了”可验证、可补偿”。
技术之外:团队与文化才是胜负手
说了这么多技术,其实最关键的往往是人。
1. 给团队”安全感”
改造遗留系统最怕团队人心惶惶,老代码不敢动,新代码怕背锅。我现在每启动一个改造项目,第一件事就是和团队约法三章:
- 老代码的锅,我背:出问题了不追责,先解决问题
- 新代码的实验,容错:允许试错,给20%的技术探索时间
- 功劳,大家分:每个小里程碑都庆祝,让所有人感受到进展
2. 让业务方成为盟友
技术人常犯的病,就是觉得业务方不懂技术,懒得解释。但遗留系统改造,没有业务支持就是死路一条。
我现在的做法是:把业务方变成产品经理。让他们参与优先级排序,让他们看到新系统带来的好处(比如更快的响应速度、更灵活的活动配置)。当他们觉得”这是我的项目”,而不是”技术部的自嗨”时,阻力就变成了推力。
3. 文档是写给三个月后的自己
改造过程中会产生大量临时方案、兼容逻辑、特性开关。这些东西不记下来,三个月后你自己都忘了为什么这样设计。
我的团队现在有个规矩:每个PR必须附带决策记录(ADR),简单几句话就行:
1 | ## ADR-007: 用户服务双写方案 |
这些ADR存在Git里,成了我们的”考古日志”,后来接手的人一看就明白。
总结:遗留系统是一场马拉松
干了二十年,我最大的感悟是:遗留系统不是技术问题,是演化问题。它像一棵老树,形态扭曲是因为经历了风雨,你不可能一夜之间把它变成笔直的杨树。
所以我的终极建议:
- 慢即是快:用绞杀者模式,小步快跑,每次只改一个点
- 数据是底线:保证数据一致性,比用什么框架重要一万倍
- 团队是核心:技术方案再牛,执行的人没信心也是零
- 业务是裁判:改造的价值,最终由业务增长来定义
最后,如果你老板非要重写,记得把我的故事讲给他听:那个花了18个月重写的项目,最后因为业务变化太快,新系统上线即过时,而老系统还在稳定地赚钱。
遗留系统就像家里的老家具,看着不顺眼,但坐起来最舒服。现代化改造不是把它扔了,而是换个沙发套,修修弹簧,让它再陪你十年。
毕竟,我们写的代码,总有一天也会成为别人眼中的”遗留系统”。善待它们,就是善待未来的自己。