[转]聊聊遗留系统改造的“道”与“术”
让我们面对现实吧,我们今天所做的一切就是在编写明天的遗留系统。—— Martin Fowler
什么是遗留系统(Legacy System)?根据维基百科的定义,遗留系统是一种旧的方法、旧的技术、旧的计算机系统或应用程序,“属于或与以前的、过时的计算机系统有关” ,但仍在使用中。通常,将系统称为“遗留系统”意味着它可能已经过时或需要更换。
遗留系统改造是程序员的宿命,因为软件永远没有完成的时候。公司的业务始终在变化,软件架构和代码也只能随之不断变化,在数字化时代,这种变化更快了。
据 IEEE 报道,自 2010 年以来,全世界的公司和政府在 IT 产品和服务上的支出估计为 35 万亿美元。其中,约四分之三用于运营和维护现有的 IT 系统。至少有 2.5 万亿美元用于尝试替换旧的 IT 系统,其中约有 7200 亿美元被浪费在失败的替换工作上。
遗留系统改造和替换是一个高风险操作,开发人员应该如何对遗留系统改造做风险评估?需要遵循哪些改造原则?重构还是重写,应该怎么选择?在近日举行的 2021 ThoughtWorks 技术雷达峰会上,ThoughtWorks 首席咨询师梅雪松围绕上述话题做了演讲分享,并在演讲后接受了 InfoQ 记者的采访,与我们聊聊他在经历众多改造项目后总结出的遗留系统改造的“道”与“术”。
1. 1微服务不是遗留系统改造的首要选择
虽然当前微服务几乎已经成了软件架构的事实标准,很多开发团队在重新开发系统的时候会把微服务作为默认架构直接使用。但对于很多遗留系统来说,微服务可以作为一个选项,但并不是团队优先要解决的问题。
微服务的好处在于能够让不同的模块独立上线和运行,如果享受不到这个好处又付出了微服务改造所需要的成本,就划不来了。对于那些 IT 资产比较重的行业(如金融、保险等),他们的核心系统本身就很难快起来,通常是一批需求一起上线,模块是否能独立上线、独立演进并不太重要,微服务反而还会带来极高的运维复杂度。对于这类核心系统来说,优先级更高的是业务代码解耦,很多系统往往就是因为代码耦合度太高才变得复杂。梅雪松表示,解耦一直是遗留系统改造的重点,10 年前是如此,现在是如此,再过 10 年可能还是如此。虽然“高内聚、低耦合”是大家一直在追求的目标,但实际很难达到。系统开发的第一步必然是先完成业务需求,业务也有 deadline,于是就成了死循环,业务一直在催,IT 只能赶紧干,结果就留下了很多技术债而且没有时间修复,甚至没有意识到要去修复,最终这个系统就变成了遗留系统。
2. 2如何启动遗留系统改造?
遗留系统改造是一个长期过程,通常会持续很长时间。因此启动改造之前,需要考虑其他相关干系人的诉求,明确改造目标。如果不能得到其他部门的支持,改造工作可能会遇到比较大的挑战。梅雪松建议可以从四个方向寻找目标:
- 业务敏捷,能不能让响应力变得更快?
- 运营效率,如何通过系统改造,提升业务运营的效率?
- 客户洞见,如何让系统更好地发现客户洞见,进而更好地理解客户需求和演进产品?
- 系统本身的韧性和弹性,这是在云时代比较典型的诉求。
有了明确的改造目标后,还需要制定度量指标,在每个维度上搜集度量数据并可视化出来。这样做的好处,一是能够让团队了解改造进展和成果,确保改造朝着正确的方向走;二是能够将数据透明给相关的干系人,如业务、运营、市场等部门,以获得他们的支持。
3. 3遗留系统现代化改造的三个原则
如前文所述,遗留系统改造是一项高风险工作,失败率高达三分之一。那是什么原因让遗留系统改造获得了成功?又有哪些因素导致遗留系统改造最终失败?借鉴 Neal Ford《演进式架构》一书的内容,梅雪松总结了遗留系统现代化改造的三个原则,这些原则在遗留系统改造中可以作为指导思想使用。
第一个原则是要把演进能力作为一种架构特征。架构有很多特征,比如安全性、可靠性、应用性等等。既然业务一直在变化,代码需要一直演进,那就应该把演进的能力作为一种架构特征,在设计架构时需要考虑到这一点。
第二个原则是以适应度函数来牵引架构的演进。软件开发是一个社会化的过程,所以架构的演进以及维护需要团队来做,但团队很难自发去做这项工作,需要有一个东西来牵引着他们,并评估每一次的演进到底变得更好还是变得更坏了,这就是适应度函数。比如如果以架构分层为设计原则之一,就可以定义一个分层的适应度函数,然后把它变成一个测试放到 CI 上持续运行。
第三个原则是以增量变更作为架构演进的单元,这是在实操层面最重要的一个原则。很多失败的遗留系统改造案例几乎都有一个同样的特点,就是步子迈得太大了,有的直接就重新开发,甚至连数据库都变了,这种情况的风险显然是非常大的。如果在改造一个遗留系统的时候,不能分阶段地上线、迁移、切换,而是等到最后一刻才最终上线切换的话,风险可能就爆表了。如果能以增量变更的方式来做改造,每个增量都能够回滚,这种改造基本上风险是可控的。
4. 4重构不如小范围重写
遗留系统改造有五种常见策略,分别是封装(Encapsulate)、平台切换(Replatform)、迁移(Rehost)、重构(Refactoring/Re-architecing)、重新开发(Rebulid/Replace)。
5.
选择策略的时候,需要考虑对应的风险、收益和成本。如上图所示,封装的风险是最小的,收益也比较小,但是成本也很低。重构和重新开发的收益都比较大,但是成本也比较高,其中重新开发的风险是最高的。梅雪松强调,这些策略作用的颗粒度并不是整个系统,而可能是在很小的模块上,并且这些策略可以组合使用而非单独只选择某一个策略。
那对于遗留系统改造来说,重构和重新开发哪一个是更好的选择?
梅雪松表示,随着经历的客户改造案例越来越多,他的想法也在发生变化。最早的时候,他认为只要具备足够强的能力,重构比重写更安全,但后来他发现,很多老旧的业务系统代码质量非常糟糕,几乎可以说是“一次性”代码,并没有什么重构的价值,不如直接重写。在他看来,理解业务后再重写,其实是比较安全的,复杂的是本身不了解业务、代码又写得很乱,这时候通过代码对照去了解业务反而难上加难。当然,保证重写安全性的前提是,不能整个系统大范围地重写,而是要一小块一小块去重写。
但重构并非没有价值。梅雪松认为,在应对很多老代码的时候重构价值不大,但重构这个能力对于工程师来说依然非常重要。即便是开发新需求,也是一个不断重构的过程。写代码就像写文章一样,一开始写出来的一定不会是好代码,只有一遍一遍地修改和重构之后才能写出高质量的代码。因此重构并非只是架构师或高级别技术大牛才需要关注的事情,而是所有开发人员都应该具备的最基本技能。