# 01|遗留系统之殇:为什么要对遗留系统进行现代化? 你好,我是姚琪琳。 不知道你是否跟曾经的我一样,身处一个遗留系统的漩涡之中,每天为毫无头绪的代码和混乱不堪的架构发愁。一个新的需求来了,都不知道从哪儿开始改起,即便看似简单的需求都要很久才能上线。 假如你也如此,请不要悲伤,也不要心急,其中有很多妥善的应对之法,我会在这个专栏中一一交付给你。 但在此之前啊,我想我们是不是得先明确一下,到底什么样的系统才能称之为遗留系统呢?它存在哪些问题,复杂在哪里? 这节课我们就来一探究竟,好为我们后面深入学习遗留系统奠定一个良好的基础。同时,我们也可以看看在高成本的现代化改造之下,为什么遗留系统还要迎难而上? ## 关于遗留系统的误区 请你先思考这样一个问题:假如一个系统七八年了,它是不是个遗留系统? 系统的时间长等同于就是遗留系统,这是很多人的一个误区。虽然大多数遗留系统确实是存在的时间很长,但并不等于时间长的都是遗留系统。 这里分享一个我的项目经历。我之前曾在一个项目上工作6年多,这是一个有着12年历史的老项目。 它的技术栈最初是.NET Framework,现在已经有部分迁移到了.NET Core;它最初是单体架构,现在是一个小单体加多个微服务;它从第一行代码开始就使用TDD的方式开发,至今已经有30000多个不同类型的测试;它一开始使用SVN来管理源代码,不过早在十年前就被迁移到了Git;它从第一天就有CI/CD相伴,并且一直坚持基于主干开发的分支策略,每个月都有稳定的版本发布;它没有一行注释,但是任何开发人员都能通过阅读源代码快速了解系统的实现,也就是说代码质量相当高…… 这个系统历时12年之久,比很多公司活的时间都长。那它是遗留系统吗?答案是否定的。因为它的代码质量高、架构合理、自动化测试丰富、DevOps成熟度也高,各种技术、工具都是相对先进的,怎么能说是遗留系统呢? 这里我还想请你进一步思考一下:存在时间短的系统就不是遗留系统吗? 仍然拿我个人经历过的一个项目来举例。它是07年左右开发完成的,当我10年加入项目组的时候发现,它仍旧是基于JDK 1.4的(那个时候Java 6已经发布4年了),很多Java的新特性都无法使用。它是一个C/S结构的软件,前端基于Java富客户端,后端是一个大单体向前端提供RPC服务;它没有一行测试,每改一行代码都提心吊胆,有时为了不影响别的功能,只好把代码复制一份,加入自己的逻辑,这就导致了大量的重复代码;每次发布日期都是一拖再拖,而部署到生产环境上的war包,甚至在是开发机器上打包的…… 别看这个系统只开发完3年,但毫不客气地说,它从刚开发完毕的那一刻起,恐怕就是个遗留系统。它的代码质量差、架构不可演进、没有自动化测试、缺乏DevOps,各种技术、工具也十分落后、老旧,这样的系统,即使刚开发完,也是遗留系统。 那么从上面的描述看,你大概已经发现了我判断遗留系统的几个维度:**代码、架构、测试、DevOps以及技术和工具**。 所以说啊,时间长短并不是衡量遗留系统的标准。代码质量差、架构混乱、没有测试、纯手工的DevOps(或运维)、老旧的技术和工具,才是遗留系统的真正特点。 接下来我们就从这些特点出发,逐一分析一下它们都造成了哪些问题。 ## 遗留系统的特点和问题 首先就是代码质量差。我们说优秀的代码都是相似的,而糟糕的代码则各有各的糟糕之处。 我曾治理过一个有着6000行代码的单个方法,至今印象深刻。其中包含6个大的if/else块,每个块中大概有1000行左右的代码,这6个1000行的代码只有十分细小的差别。显然是开发人员为了偷懒,不敢在原代码上改动,于是复制出来加入自己的逻辑。他倒是图省事儿了,但是对于维护人员来说简直是噩梦。正所谓编码一时爽,维护火葬场。 其次是架构,这也是遗留系统的重灾区。一个软件架构的作用,是要解决多个业务模块之间的协作问题。但如果架构混乱,多个模块之间往复调用,数据也是随意访问,模块之间的边界就会变得模糊,数据所有权也会变得含糊。试想一下,如果一张表被10个模块访问,谁能说得清这张表到底属于哪个模块呢? 下图是一家银行的核心应用系统模块之间的交互图,我想没有一个人愿意工作在这样的系统上吧? ![图片](https://static001.geekbang.org/resource/image/24/ae/240594a8fb368884641cc981beed27ae.png?wh=1200x688) 综合来看,代码和架构的质量差会导致遗留系统的维护成本相当高昂。这里的维护就包括:新需求的添加、线上Bug的修改,以及为了维护系统运行所需投入的软硬件和人力等。 我说这些可不是空穴来风,IEEE就曾报道过,2010年以来,全世界在IT产品和服务上的支出达到了35万亿美元。其中四分之三用于运营和维护现有的IT系统,至少有2.5万亿用于尝试替换旧系统,其中差不多三分之一的资金都打了水漂(这个报道详情你感兴趣的话,可以看[这里](https://spectrum.ieee.org/computing/it/inside-hidden-world-legacy-it-systems))。 企业在遗留系统上的投入巨大,却没能得到相称的回报。很多资金只是用来维持系统的现状,却不能让它们变得更好。 更严重的是,代码和架构的落后还会导致系统在合规和安全方面的问题。 随着我国法律法规的健全,软件系统的合规性越来越重要,而一个面对任何需求都难以实现的遗留系统,要想进行修改以符合新的法律法规,是难上加难的事情。去年我国正式施行了《中华人民共和国数据安全法》(即中国的GDPR),明确规定了软件系统的数据安全规范。如果不能依法进行系统的整改,将面临法律的制裁。 而在遗留系统开始构建的时候,可能就没有考虑太多的安全性。随着新的攻击手段越来越丰富,遗留系统的安全性越来越脆弱,企业也很难对此投资去专门改善安全性。 然后我们接着看缺乏甚至没有测试所造成的问题。在一个遗留系统上添加新需求简直如履薄冰,当你好不容易找到要修改的位置,敲了几行代码感觉可以了的时候,系统的另一个功能可能会因为你的这几行代码而崩溃。 而一个线上Bug想要找到元凶,可能会难如登天,一方面缺乏有效的日志难以定位(很多遗留系统的日志是打在命令行里的),另一方面修复了一个Bug也可能会导致更多的Bug。 这时就体现出自动化测试的重要性了。我不知道你的系统里有没有或者有多少测试,总之我在那个有着30000多个自动化测试的项目上修复一个Bug的过程是这样的: 1.先在本地复现Bug,找到产生这个Bug的业务场景; 2.为这个业务场景添加一个自动化测试并运行,发现这个测试是失败的; 3.修改代码,让这个新增的测试通过; 4.运行所有的测试,确保所有的测试通过。 经过这一系列操作,我就可以有十分有信心地宣布,这个Bug被我修复了,而且在目前测试覆盖的场景下没有引入新的Bug。但对于没有测试的遗留系统,在测试人员告知测试通过之前,我简直是胆战心惊。 那遗留系统落后的DevOps手段会造成哪些问题呢? 这会造成重大的安全隐患。像我前面举的那个例子,部署到生产环境的安装包是本地打出来的,就是非常严重的安全问题。不知你是否记得几年前著名的XCodeGhost事件,开发人员使用非官方渠道下载的注入了恶意代码的XCode,并用这样的XCode打包App,上传到了App Store上。结果下载了这种App的手机信息就被窃取了。 这里多说两句,这次事件延伸到我们的日常工作中也是有值得深思之处的。 一是不要从非官方渠道下载开发工具,但这个教训直到现在仍然没有引起足够的重视,仍然有很多团队使用的付费开发工具不是从官方渠道下载的。二是不要在开发机器上打包部署到非开发环境(特别是生产环境)上,要通过CI/CD来编译、打包和部署(当然CI/CD上的工具也必须是从官方渠道下载)。这就是DevOps的作用之一。 最后,技术和工具也可能存在很大的安全漏洞(比如前段时间爆雷的log4j)。新的系统虽然也存在这样的风险,但是非常容易补救。反观遗留系统的工具升级,那就举步维艰了,原因也很简单,投入产出比合不来。 另外,落后的技术和工具也使得遗留系统难以与新系统集成。基于Delphi、PowerBuilder、VB或Lotus Notes那一代的桌面应用,就是很好的例子。新的开发团队在面临遗留系统集成的时候,往往都是唯恐避之不及。这样的系统也使得自己所拥有的企业核心数据成为孤岛,难以与其他系统互联。 ## 什么是遗留系统? 说了这么多,我们似乎已经有了一个很具体的关于遗留系统的画像了,参考如下: ![图片](https://static001.geekbang.org/resource/image/29/e5/2956b29efbd201cf8ecabab096fe0ae5.jpg?wh=1920x840) 那是不是可以进一步抽象一下概念了呢? 很简单,不妨直接看看维基百科是如何定义的吧: > 在计算机领域,遗留系统是一种使用旧的方法和技术的、过时的,却仍旧在使用的计算机系统。 而Gartner给出的定义是: > 基于过时技术但对日常运营至关重要的信息系统。 嗯,有信息重合,我们找找关键字:**旧的、过时的、重要的、仍在使用的**…… 这里找找对应的例子,辅助下理解。不知道你是否看过一些医疗类型的美剧,还记得发生危急情况时,医院是如何通知医生们的吗?是使用寻呼机——一个在现实生活中已经寿终正寝了快20年的古老的通信设备。 难道美国的通信设施如此落后吗?当然不是,诞生了iPhone和Android的美国怎么可能通信落后呢?真正落后的是医院的急救寻呼系统。这些系统往往有着六七十年的历史,很难被替换。它就完美符合上面的所有关键字:旧、过时、重要、仍在使用。 还有Windows XP系统,尽管它很经典,但微软在2014年就已宣布不再维护了。不过直到现在,我们仍然能在很多ATM机上看到它的踪影。 到这,我们已经完全明确了遗留系统的定义以及它所带来的问题,所以你觉得一个遗留系统还有保留的价值吗?为什么我们没有替换甚至丢弃,还要继续维护,并为其打上重要的标签呢? ## 遗留系统的现代化价值 原因有很多。首先,**可能是成本太高了,企业不愿意投入资源去改进;也可能是因为积重难返,根本改不动。**而遗留系统往往都是企业的核心业务系统,支撑着整个企业的业务运营,这样的系统就算问题再多,也是不可替代的。 **其次,遗留系统蕴含了大量的数据资产。**遗留系统中的数据虽然很难与其他系统进行集成,但这部分数据的价值又是巨大的。企业的新系统常常不得不在这些数据的基础之上去构建,其他系统要想获得遗留系统中的数据,就必须对遗留系统进行修改,所以很多团队为了避免修改代码就会去寻求数据库层面的复制和同步,这也是一个选择。 **另外,遗留系统中还藏匿着丰富的业务知识。**由于业务人员长期使用并且养成了习惯,很多软件系统已经与业务融为一体,很难区分哪些是真正的业务,哪些是系统的设计。而由于系统历时太久,已经失去了能够正确描述系统现状的文档,所以到最后只有遗留系统的代码才能够准确表达系统的行为,以及与之对应的业务知识。 系统改造,有可为有可不为,而对于遗留系统来说,结合其现代化价值,看上去更像是一种不得不为。所谓现代化,其实就是从代码、架构、DevOps和团队结构这四个方面来对遗留系统进行治理。 既然不能对遗留系统听之任之,我们就要下决心迎难而上,掌握主动权,否则当问题真正出现时就为时已晚了。 举个例子,疫情期间,美国大量人口失业,但上世纪80年代建造的失业系统无法及时发放失业福利,他们的国税局系统则更加老旧,是60年代建造的,总共需要20个星期的时间才能为符合条件的纳税人发放疫情补贴。 我们看到的是,在全球疫情这种黑天鹅事件发生时,一方面,高响应力的公司能够快速推出像疫情地图、行程码这种全新的服务,以造福社会服务大众;而一方面,陈旧的遗留系统却在拖着整个时代的后腿。 用巴菲特的话说就是,当潮水退去之后,你才知道谁在裸泳。 如果说不得不为,那怎么为之更好呢? 在数字化时代,每家企业都应该意识到科技是核心竞争力,要依赖科技去重塑业务、创造新的商业模式,创造数字化收益。也就是我们常说的Tech@Core。 很多互联网公司的数字化基因是与生俱来的,它们能够根据当前的形式和热点迅速地开启一个全新的商业模式并站稳脚跟。比如在疫情下买菜难的问题,很多公司就迅速推出了买菜App。 然而与此同时,对于传统企业来说,与上下游客户和供应商合作的数字化需求其实也是在不断增多的。以汽车保险这个行业为例,与车主、4S店、汽车制造商、交管系统等等合作方之间,存在着大量的互联需求,这里面有很多商机。一个有雄心的企业是不可能用一个落后的遗留系统去应对这些挑战的。 ![图片](https://static001.geekbang.org/resource/image/16/0b/165f49aa7fd90fdaa8133c72c120ea0b.jpg?wh=1920x1253 "图片来源网络") 所以,迎难而上是必须的,让老旧、过时的遗留系统变得现代化也是必须的,这样才能更好地为企业的战略和运营服务。 ## 总结 总结一下今天的内容。 我们从业界对遗留系统的定义中总结出了4个关键字:**旧、过时、重要、仍在使用**。然而人们对于遗留系统的认识存在一个普遍的误区,即时间长的系统就是遗留系统。 事实并非如此。有些系统时间虽长,但如果一直坚持现代化的开发方式,在代码质量、架构合理性、测试策略、DevOps等方面都保持先进性,这样的系统就像陈年的老酒一样,历久弥香。而有些系统虽然刚刚开发完成,但如果在上述几个方面都做得不好,我们也可以把它叫做遗留系统。 遗留系统在维护成本、合规性、安全性、集成性等方面都会给企业造成巨大的负担,但同时也蕴含着丰富的数据和业务资产。我们应该对遗留系统进行现代化,让它重新焕发青春。 那么遗留系统的现代化都包含哪些方面呢,[下节课](https://time.geekbang.org/column/article/506570)我们就揭晓谜题。 ## 思考题 如果你现在正工作在遗留系统上,你或你的团队最大的痛点是什么?你们又是如何解决这些痛点的? 期待你的分享,我们可以在留言区进行互动交流!如果今天的学习让你有所收获,也欢迎把课程分享给有需要的朋友,我们下节课再见!