146 lines
14 KiB
Markdown
146 lines
14 KiB
Markdown
# 07 | 遗留系统现代化的五种策略:重构还是重写?这是一个问题
|
||
|
||
你好,我是姚琪琳。
|
||
|
||
从今天开始,我们正式进入模式篇的学习。这一部分我会带你学习代码、架构、DevOps、团队结构四个现代化中的各种模式,这些模式是我们实战的理论基础,希望你能牢牢掌握。
|
||
|
||
不过深入学习这些模式前,今天我们先从重构和重写的“两难”问题说起。到底是重构,还是重写?这是一个困扰着很多团队的问题。
|
||
|
||
重构吧,遗留系统积重难返,重构之路遥遥无期,三年、五年时间,可能也只是刚开了个头,还不如重写。
|
||
|
||
但重写就真的比重构好吗?遗留系统中最难获取的就是业务知识。当你问起一块业务时,得到的回答往往是:“没有文档”、“没人知道”或者“只能看代码”……没有业务,或者说没有需求,怎么可能构建出来一个新的系统呢?
|
||
|
||
那我们到底应该如何应对呢?除了重构和重写,还有没有其他方式呢?
|
||
|
||
## 遗留系统现代化的五种策略
|
||
|
||
Gartner在19年曾经有[一篇报道](https://www.gartner.com/smarterwithgartner/7-options-to-modernize-legacy-systems),提出了遗留系统现代化的七种方案。我把这七种方案做了整合,把它们整理成后面这五种策略。它们各有各的特点,而且分别对应不同场景,你要根据项目自身情况选择不同的策略或组合。然后,再应用后面要讲的模式来落地。
|
||
|
||
### Encapsulate
|
||
|
||
第一种策略是**Encapsulate**,也就是**将遗留系统中的数据或者功能封装成API,供外部调用**。
|
||
|
||
我们在[第一节课](https://time.geekbang.org/column/article/505740)里提到过,遗留系统中蕴含着丰富的数据资产,但是因为技术和工具落后,导致它难以与新系统集成,这些数据被封印在遗漏系统中,成了数据孤岛。比如早期的银行或民航软件,很多都是部署在大型机上的。企业非常希望开发手机App,这样才能更好地为客户服务,但却很难访问到主机上的这些数据。
|
||
|
||
同样地,遗留系统中还有一些功能十分重要,其他外部系统需要这些能力来构建业务。比如一些公文流转的工作流,可能构建在基于Lotus Notes的办公系统中,但如果企业想要开发移动办公App,并在App中复用这套工作流,也是困难重重。
|
||
|
||
问题虽然棘手,但事到临头,工程师们总要想办法应对。结合刚才说的情况,我们可以封装这些数据和功能,形成API,供这些移动App或其他外部系统使用。如果遗留系统本身就是基于Web的,可以在Web系统上直接构建API;如果不是,可以选择构建一个全新的Web API来部署并提供服务。
|
||
|
||
这样做的好处是,以较低的成本和风险,尽可能满足外部系统的需求。你无需对遗留系统做较大的修改,只是增加一些API而已。遗留系统本身不会被优化,但它可以通过这些API对外提供能力。
|
||
|
||
还有一种情况,我也建议你使用封装的策略,那就是当你有一个第三方系统,希望扩展它的功能,但只能访问它的数据库,却无法修改代码的时候。
|
||
|
||
这时有些团队采用的方式就是直接连它的数据库,并在已有的系统中基于这些数据构建新的功能。我不建议你这么做,直接连数据库固然简单,但由于你可以访问它所有的表和列,距离混乱也就剩一步之遥了。
|
||
|
||
我建议你基于这个第三方系统的数据库构建一个Web API,来向其他的系统提供你想提供的数据和功能,而不是暴露全部的数据。
|
||
|
||
我这里也稍微剧透下,封装的策略落到具体应用的时候,衍生出了很多相关模式,比如**数据API模式**、**功能API模式**等等。我会在后面的课里再详细展开。
|
||
|
||
### Replatform
|
||
|
||
第二种策略是**Replatform**,也就是**替换运行时平台**。这种策略不需要对代码大动干戈,只需要改动很小一部分。到了新的平台后,软件的功能和特性仍然保持不变。
|
||
|
||
比如,很多银行或民航软件还是基于COBOL的主机系统,把它们从大型机上迁移到Linux或Windows环境,就会甩掉昂贵的主机成本。
|
||
|
||
再比如,早年间开始构建的系统,由于种种原因,很多是基于商业软件的,想升级,就要花一大笔预算。很多企业为了节省这部分开支,会尽量避免升级,也导致系统最终变成了遗留系统。你可以通过Replatform策略来解除对商业软件的依赖,例如用Tomcat来替换WebLogic。
|
||
|
||
或者像.NET这种技术栈,也十分有必要从.NET Framework迁移到.NET Core或者.NET 5。而Python从2升级到3、JDK的大版本升级等等,也都属于Replatform。
|
||
|
||
在使用Replatform时,你只需要对代码做少量更改,以适配新的平台。这样,只通过较小的成本就可以降低基础设施的成本,并提高性能。
|
||
|
||
还有一种迁移我认为也可以看做是Replatform,虽然它并不是替换运行时平台。那就是迁移代码版本管理工具。比如你把代码从SVN迁移到Git中,不需要修改任何功能代码,但却可以享受新的代码管理平台带来的好处。
|
||
|
||
### Rehost
|
||
|
||
第三种策略是**Rehost**,也就是**将应用程序或组件部署到其他基础设施中**,如虚拟主机、容器或云。这种策略完全不需要修改代码,而只需要迁移部署的环境,甚至都不需要重新编译,因此这种迁移方法也有个很形象的别名,叫做“lift and shift”,就是原封不动地拎起来,转移到别的地方去。
|
||
|
||
我举个例子来说明,如果你的公司有一个SAP的ERP系统,可以将它从本地的数据中心迁移到AWS或GCP中。
|
||
|
||
Rehost可以让你在完全不修改已有系统的情况下,快速上云,体验云环境带来的弹性、安全性和高性能,并且迁移过程也能做到很平滑。然而由于没有任何适配,也就无法充分利用云原生的优势,因此还需要对系统内部的代码和架构做进一步调整,比如将单体架构拆分为可以独立运维的微服务。
|
||
|
||
### Refactor/Rearchitect
|
||
|
||
第四种策略是**Refactor和Rearchitect**,它们是指**在不改变系统外部行为的前提下,对代码或架构进行调整、优化,以偿还拖欠已久的技术债务、改善非功能需求、提升系统健康度**。
|
||
|
||
**Refactor主要是指代码级别的重构**,比如你可能用Sonar等代码扫描工具,扫描出了很多代码坏味道、缺陷或隐患,修复这些问题的过程就属于Refactor。这和我们平时说的代码重构基本上是一个意思。
|
||
|
||
**Rearchitect是指架构级别的重构**,它包含两层意思。第一层比较好理解,就是指从单体架构到分布式架构的这种架构调整。第二层是指不改变部署单元之间的关系,而是对单个或多个部署单元内部进行模块化或分层重构。由于这种模块化和分层也会涉及很多代码的调整,所以这种Rearchitect往往会和Refactor同时进行。
|
||
|
||
后面你学到代码和架构现代化的内容时,会看到很多Refactor/Rearchitect相关的模式。这些也往往是遗留系统现代化中最有挑战,也最有意思的部分。
|
||
|
||
### Rebuild/Replace
|
||
|
||
第五种策略是**Rebuild和Replace**,都是指**对遗留系统进行替换**。它们两个替换的范围和程度不同。**Rebuild**可能是**对应用程序的某个组件或某个服务的重新设计或重写**,但会保留其原有的业务范围和业务规则。而**Replace**是指**彻底淘汰应用程序的所有组件,去构建或购买新的软件**,同时会考虑添加新的业务需求或移除某些旧的业务需求。
|
||
|
||
我在[第一节课](https://time.geekbang.org/column/article/505740)提到过,遗留系统中的业务知识是严重缺失的,不仅没有遗留下来的文档供我们查阅,也没有任何一个人能说清楚全部的业务细节。在这样的基础上实施Rebuild或Replace,风险和成本都是相当高的,但相对来说,收益也是最高的,一旦替换成功,就可以彻底摆脱原来的遗留系统了。
|
||
|
||
下图是对上面五种策略的一个总结,你可以从中看出它们的收益、风险和成本(用面积表示):
|
||
|
||
![](https://static001.geekbang.org/resource/image/73/bf/735968254f34d2eecbe73f5f0bed34bf.jpg?wh=3122x1870)
|
||
|
||
### 其他策略
|
||
|
||
除了上面的几种策略以外,对于遗留系统来说还有一些应对策略可以选择。不过由于不涉及到代码、架构或运行环境的变更,我没有把它们作为遗留系统现代化的策略。
|
||
|
||
其中一种是Retain,即保持系统当前的状态不做任何修改或更新。对于尚可满足使用的遗留系统来说,这无疑是风险和成本最低的策略。我在[第五节课](https://time.geekbang.org/column/article/509535)说过,使用人数不多、需求很少、只需要一两个人维护的遗留系统,就可以使用这种策略。
|
||
|
||
还有一种是Retire,就是评估完工作量、使用情况和业务价值之后,选择完全停止使用的一种策略。有的时候系统已经没有什么人用了,或者类似的功能在其他系统中可以替代,你就可以选择让这个旧系统彻底退休了。
|
||
|
||
## 你应该选择什么样的策略?
|
||
|
||
面对如此眼花缭乱的策略,你恐怕更加无所适从了吧?别担心,接下来我就来帮你梳理一下如何选择。
|
||
|
||
到底是Replatform还是Rehost?是Refactor还是Rebuild?是Rearchitect还是Replace?其实,我们还是要依据目标和系统现状做判断。
|
||
|
||
先看最终目标,第五节课我列出了企业遗留系统现代化的四个目标,即业务敏捷、运营效率、客户洞见、系统韧性与弹性。
|
||
|
||
对于业务敏捷来说,Replatform和Rehost通过替换运行时环境和上云可以提升部署频率,特别是Rehost可以显著提升系统在遇到故障时的恢复时间;Refactor/Rearchitect通过改善代码和架构的质量,可以缩短需求交付周期,减少线上问题数量;而Rebuild/Replace由于在某种程度上做了替换,也可以大幅度提升业务的响应力和交付质量。
|
||
|
||
对于运营效率来说,Refactor/Rearchitect和Rebuild/Replace都可以提升价值流效率。而要想改善客户洞见,最有效的方式还是Rebuild/Replace。在系统韧性与弹性方面,Rehost显然是不二之选。
|
||
|
||
我们要结合当前遗留系统的现状和想要提升的目标,做综合判断,对于不同的模块,也可以选择不同的策略组合,来实现一个完整的业务目标。
|
||
|
||
比如遗留系统中的有些业务,需要提供7x24小时的高可用服务,类似银行转账、保险报案等模块。但这些模块很有可能还位于单体的“大泥球”中,和其他模块有着剪不断、理还乱的关系。
|
||
|
||
为了支撑这些需求,我们可以先采用Rearchitect的模块化策略,结解耦模块之间的关系;然后再用Rearchitect的服务化策略,将这些模块拆分成独立的服务;最后再用Rehost策略将这些服务部署到云上,以提升系统的可用性。
|
||
|
||
当然,如果由于系统耦合严重,模块化改造很难实施,你也可以选择用Rebuild策略重写这一部分模块。
|
||
|
||
再比如一个部署在WAS v6上的Java Web遗留系统,由于只支持Java EE 1.4,技术栈严重落后,已经很难在市场上招到人来维护了。
|
||
|
||
这时,可以先选择Replatform策略,将WAS替换为较新版本的Tomcat,以摆脱昂贵的商业软件;然后再次使用Replatform升级Java的版本,包括所依赖的第三方工具,这样就完成了整个技术栈的升级。
|
||
|
||
如果企业认为当前遗留系统已经彻底无法满足业务的需要,且具备足够的资源来构建新的系统,就可以使用Replace策略来彻底替换旧系统。同时,在遗留系统并不是很大,但重要性又相对很高的情况下,也可以考虑Rebuild/Replace。
|
||
|
||
这里我一直没有提Encapsulate这种策略,是因为它有自己独特的适用场景,也就是与其他外部系统集成的时候。
|
||
|
||
## 小结
|
||
|
||
又到了总结的时刻。今天我们学习了遗留系统现代化的几种策略,不同的策略有不同的适用场景,我把它们总结到了一张表中。
|
||
|
||
![图片](https://static001.geekbang.org/resource/image/ec/d6/ecd8ff1b982116d6ff91e4f5bebb5cd6.jpg?wh=1920x1135)
|
||
|
||
你要记住的是,一定要根据项目的情况来选择不同的策略组合。不要上来就大张旗鼓地重构或重写,一定要弄清楚想要的是什么。除了重构和重写,你其实还有很多选择。
|
||
|
||
从[下节课](https://time.geekbang.org/column/article/512658)开始,我们马上进入各种模式的学习了,你准备好了吗?
|
||
|
||
## 思考题
|
||
|
||
感谢你学完了今天的内容。我给你留的作业是这样的:
|
||
|
||
假设你是一个项目的技术负责人,你的项目基于.NET Framework 4.6.1,而该版本即将在这个月(2022年4月)“寿终正寝”。为了避免潜在的安全风险,你不得不将.NET版本进行升级(假设系统当前部署在Windows虚拟机上)。
|
||
|
||
这时你有以下三个选择:
|
||
|
||
1.升级到.NET Framework 4.6.2版本,几乎不用对代码做任何修改,只需要升级一下各个部署环境的虚拟机即可,保守估计三天之内也能完成升级并上线。
|
||
|
||
2.升级到最新的.NET Fremework 4.8版本,可以获得更长的技术支持,还能使用新版语言的特性,以提升开发效率。但有些旧的第三方库并不支持4.8,需要升级或替换。你可能需要两周到一个月的时间来完成全部升级。
|
||
|
||
3.升级到.NET 5,以充分享受跨平台和容器化的优势,系统也将彻底摆脱Windows的束缚。但代码需要改动的地方很多,几乎所有第三方依赖也都需要升级。预计需要五到六个月的时间才能搞定。
|
||
|
||
你会做出什么样的选择呢?补充一句,如果你不熟悉.NET的版本和特性,可以自行搜索一下。
|
||
|
||
本次作业没有正确答案,在我看来,任何一个选择都是可以接受的。你可以根据自己的思考给出答案,并说明理由。必要的时候,也可以自己添加一些约束条件,来支持自己的选择。
|
||
|
||
期待你的分享。如果你觉得今天这节课对你有帮助,别忘了分享给你的同事和朋友,说不定就能帮他解决一个难题。
|
||
|