Day949.遗留系统之殇:为什么要对遗留系统进行现代化? -遗留系统现代化实战
遗留系统之殇:为什么要对遗留系统进行现代化?
Hi,我是阿昌,今天学习记录是关于遗留系统之殇:为什么要对遗留系统进行现代化?的内容。
不知道你是否跟曾经一样,身处一个遗留系统的漩涡之中,每天为毫无头绪的代码和混乱不堪的架构发愁。
一个新的需求来了,都不知道从哪儿开始改起,即便看似简单的需求都要很久才能上线。
到底什么样的系统才能称之为遗留系统呢?它存在哪些问题,复杂在哪里?
一、关于遗留系统的误区
一个问题:假如一个系统七八年了,它是不是个遗留系统?系统的时间长等同于就是遗留系统,这是很多人的一个误区。
虽然大多数遗留系统确实是存在的时间很长,但并不等于时间长的都是遗留系统。一个项目上工作 6 年多,这是一个有着 12 年历史的老项目。
- 它的技术栈最初是.NET Framework,现在已经有部分迁移到了.NET Core;
- 它最初是单体架构,现在是一个小单体加多个微服务;
- 它从第一行代码开始就使用 TDD 的方式开发,至今已经有 30000 多个不同类型的测试;
- 它一开始使用 SVN 来管理源代码,不过早在十年前就被迁移到了 Git
- 它从第一天就有 CI/CD 相伴,并且一直坚持基于主干开发的分支策略,每个月都有稳定的版本发布;
- 它没有一行注释,但是任何开发人员都能通过阅读源代码快速了解系统的实现,也就是说代码质量相当高……这个系统历时 12 年之久,比很多公司活的时间都长。
那它是遗留系统吗?答案是否定的。
因为它的代码质量高、架构合理、自动化测试丰富、DevOps 成熟度也高,各种技术、工具都是相对先进的,怎么能说是遗留系统呢?
思考一下:存在时间短的系统就不是遗留系统吗?
一个项目,它仍旧是基于 JDK 1.4 的(那个时候 Java 6 已经发布 4 年了),很多 Java 的新特性都无法使用。它是一个 C/S 结构的软件,前端基于 Java 富客户端,后端是一个大单体向前端提供 RPC 服务;
它没有一行测试,每改一行代码都提心吊胆,有时为了不影响别的功能,只好把代码复制一份,加入自己的逻辑,这就导致了大量的重复代码;每次发布日期都是一拖再拖,而部署到生产环境上的 war 包,甚至在是开发机器上打包的……别看这个系统只开发完 3 年,但毫不客气地说,它从刚开发完毕的那一刻起,恐怕就是个遗留系统。
它的代码质量差、架构不可演进、没有自动化测试、缺乏 DevOps,各种技术、工具也十分落后、老旧,这样的系统,即使刚开发完,也是遗留系统。
那么从上面的描述看,已经发现了我判断遗留系统的几个维度:代码、架构、测试、DevOps 以及技术和工具。
所以说啊,时间长短并不是衡量遗留系统的标准。代码质量差、架构混乱、没有测试、纯手工的 DevOps(或运维)、老旧的技术和工具,才是遗留系统的真正特点。
二、遗留系统的特点和问题
-
首先就是
代码质量差。说优秀的代码都是相似的,而糟糕的代码则各有各的糟糕之处。一个有着 6000 行代码的单个方法,至今印象深刻。其中包含 6 个大的 if/else 块,每个块中大概有 1000 行左右的代码,这 6 个 1000 行的代码只有十分细小的差别。显然是开发人员为了偷懒,不敢在原代码上改动,于是复制出来加入自己的逻辑。他倒是图省事儿了,但是对于维护人员来说简直是噩梦。正所谓编码一时爽,维护火葬场。 -
其次是
架构,这也是遗留系统的重灾区。一个软件架构的作用,是要解决多个业务模块之间的协作问题。但如果架构混乱,多个模块之间往复调用,数据也是随意访问,模块之间的边界就会变得模糊,数据所有权也会变得含糊。试想一下,如果一张表被 10 个模块访问,谁能说得清这张表到底属于哪个模块呢?
下图是一家银行的核心应用系统模块之间的交互图,想没有一个人愿意工作在这样的系统上吧?

综合来看,代码和架构的质量差会导致遗留系统的维护成本相当高昂。这里的维护就包括:新需求的添加、线上 Bug 的修改,以及为了维护系统运行所需投入的软硬件和人力等。
IEEE 就曾报道过,2010 年以来,全世界在 IT 产品和服务上的支出达到了 35 万亿美元。其中四分之三用于运营和维护现有的 IT 系统,至少有 2.5 万亿用于尝试替换旧系统,其中差不多三分之一的资金都打了水漂(感兴趣的话,可以看这里)。
企业在遗留系统上的投入巨大,却没能得到相称的回报。很多资金只是用来维持系统的现状,却不能让它们变得更好。
更严重的是,代码和架构的落后还会导致系统在合规和安全方面的问题。随着我国法律法规的健全,软件系统的合规性越来越重要,而一个面对任何需求都难以实现的遗留系统,要想进行修改以符合新的法律法规,是难上加难的事情。
去年我国正式施行了《中华人民共和国数据安全法》(即中国的 GDPR),明确规定了软件系统的数据安全规范。如果不能依法进行系统的整改,将面临法律的制裁。而在遗留系统开始构建的时候,可能就没有考虑太多的安全性。随着新的攻击手段越来越丰富,遗留系统的安全性越来越脆弱,企业也很难对此投资去专门改善安全性。
然后接着看缺乏甚至没有测试所造成的问题。在一个遗留系统上添加新需求简直如履薄冰,当好不容易找到要修改的位置,敲了几行代码感觉可以了的时候,系统的另一个功能可能会因为你的这几行代码而崩溃。而一个线上 Bug 想要找到元凶,可能会难如登天,一方面缺乏有效的日志难以定位(很多遗留系统的日志是打在命令行里的),
另一方面修复了一个 Bug 也可能会导致更多的 Bug。这时就体现出自动化测试的重要性了。
不知道系统里有没有或者有多少测试,总之我在那个有着 30000 多个自动化测试的项目上修复一个 Bug 的过程是这样的:
- 先在本地复现 Bug,找到产生这个 Bug 的业务场景;
- 为这个业务场景添加一个自动化测试并运行,发现这个测试是失败的;
- 修改代码,让这个新增的测试通过;
- 运行所有的测试,确保所有的测试通过。
经过这一系列操作,我就可以有十分有信心地宣布,这个 Bug 被修复了,而且在目前测试覆盖的场景下没有引入新的 Bug。但对于没有测试的遗留系统,在测试人员告知测试通过之前,我简直是胆战心惊。那遗留系统落后的 DevOps 手段会造成哪些问题呢?这会造成重大的安全隐患。
那个例子,部署到生产环境的安装包是本地打出来的,就是非常严重的安全问题。不知你是否记得几年前著名的 XCodeGhost 事件,开发人员使用非官方渠道下载的注入了恶意代码的 XCode,并用这样的 XCode 打包 App,上传到了 App Store 上。结果下载了这种 App 的手机信息就被窃取了。
这里多说两句,这次事件延伸到我们的日常工作中也是有值得深思之处的。
-
一是不要从非官方渠道下载开发工具,但这个教训直到现在仍然没有引起足够的重视,仍然有很多团队使用的付费开发工具不是从官方渠道下载的。
-
二是不要在开发机器上打包部署到非开发环境(特别是生产环境)上,要通过 CI/CD 来编译、打包和部署(当然 CI/CD 上的工具也必须是从官方渠道下载)。这就是 DevOps 的作用之一。最后,技术和工具也可能存在很大的安全漏洞(比如前段时间爆雷的 log4j)。新的系统虽然也存在这样的风险,但是非常容易补救。反观遗留系统的工具升级,那就举步维艰了,原因也很简单,投入产出比合不来。
另外,落后的技术和工具也使得遗留系统难以与新系统集成。基于 Delphi、PowerBuilder、VB 或 Lotus Notes 那一代的桌面应用,就是很好的例子。新的开发团队在面临遗留系统集成的时候,往往都是唯恐避之不及。
这样的系统也使得自己所拥有的企业核心数据成为孤岛,难以与其他系统互联。
三、什么是遗留系统?
说了这么多,我们似乎已经有了一个很具体的关于遗留系统的画像了,参考如下:

那是不是可以进一步抽象一下概念了呢?很简单,不妨直接看看维基百科是如何定义的吧:
在计算机领域,遗留系统是一种使用旧的方法和技术的、过时的,却仍旧在使用的计算机系统。
而 Gartner 给出的定义是:
基于过时技术但对日常运营至关重要的信息系统。
嗯,有信息重合,找找关键字:旧的、过时的、重要的、仍在使用的……这里找找对应的例子,辅助下理解。
不知道是否看过一些医疗类型的美剧,还记得发生危急情况时,医院是如何通知医生们的吗?是使用寻呼机——一个在现实生活中已经寿终正寝了快 20 年的古老的通信设备。
难道美国的通信设施如此落后吗?当然不是,诞生了 iPhone 和 Android 的美国怎么可能通信落后呢?真正落后的是医院的急救寻呼系统。
这些系统往往有着六七十年的历史,很难被替换。它就完美符合上面的所有关键字:旧、过时、重要、仍在使用。还有 Windows XP 系统,尽管它很经典,但微软在 2014 年就已宣布不再维护了。
不过直到现在,仍然能在很多 ATM 机上看到它的踪影。到这,已经完全明确了遗留系统的定义以及它所带来的问题,所以觉得一个遗留系统还有保留的价值吗?为什么没有替换甚至丢弃,还要继续维护,并为其打上重要的标签呢?
四、遗留系统的现代化价值
原因有很多。首先,可能是成本太高了,企业不愿意投入资源去改进;也可能是因为积重难返,根本改不动。而遗留系统往往都是企业的核心业务系统,支撑着整个企业的业务运营,这样的系统就算问题再多,也是不可替代的。
其次,遗留系统蕴含了大量的数据资产。遗留系统中的数据虽然很难与其他系统进行集成,但这部分数据的价值又是巨大的。企业的新系统常常不得不在这些数据的基础之上去构建,其他系统要想获得遗留系统中的数据,就必须对遗留系统进行修改,所以很多团队为了避免修改代码就会去寻求数据库层面的复制和同步,这也是一个选择。
另外,遗留系统中还藏匿着丰富的业务知识。由于业务人员长期使用并且养成了习惯,很多软件系统已经与业务融为一体,很难区分哪些是真正的业务,哪些是系统的设计。而由于系统历时太久,已经失去了能够正确描述系统现状的文档,所以到最后只有遗留系统的代码才能够准确表达系统的行为,以及与之对应的业务知识。
系统改造,有可为有可不为,而对于遗留系统来说,结合其现代化价值,看上去更像是一种不得不为。所谓现代化,其实就是从代码、架构、DevOps 和团队结构这四个方面来对遗留系统进行治理。既然不能对遗留系统听之任之,就要下决心迎难而上,掌握主动权,否则当问题真正出现时就为时已晚了。
举个例子,疫情期间,美国大量人口失业,但上世纪 80 年代建造的失业系统无法及时发放失业福利,他们的国税局系统则更加老旧,是 60 年代建造的,总共需要 20 个星期的时间才能为符合条件的纳税人发放疫情补贴。
看到的是,在全球疫情这种黑天鹅事件发生时,一方面,高响应力的公司能够快速推出像疫情地图、行程码这种全新的服务,以造福社会服务大众;而一方面,陈旧的遗留系统却在拖着整个时代的后腿。
用巴菲特的话说就是,当潮水退去之后,你才知道谁在裸泳。如果说不得不为,那怎么为之更好呢?
在数字化时代,每家企业都应该意识到科技是核心竞争力,要依赖科技去重塑业务、创造新的商业模式,创造数字化收益。也就是我们常说的 Tech@Core。
很多互联网公司的数字化基因是与生俱来的,它们能够根据当前的形式和热点迅速地开启一个全新的商业模式并站稳脚跟。比如在疫情下买菜难的问题,很多公司就迅速推出了买菜 App。
然而与此同时,对于传统企业来说,与上下游客户和供应商合作的数字化需求其实也是在不断增多的。
以汽车保险这个行业为例,与车主、4S 店、汽车制造商、交管系统等等合作方之间,存在着大量的互联需求,这里面有很多商机。
一个有雄心的企业是不可能用一个落后的遗留系统去应对这些挑战的。

所以,迎难而上是必须的,让老旧、过时的遗留系统变得现代化也是必须的,这样才能更好地为企业的战略和运营服务。
五、总结
从业界对遗留系统的定义中总结出了 4 个关键字:
- 旧、
- 过时、
- 重要、
- 仍在使用。
然而人们对于遗留系统的认识存在一个普遍的误区,即时间长的系统就是遗留系统。事实并非如此。有些系统时间虽长,但如果一直坚持现代化的开发方式,在代码质量、架构合理性、测试策略、DevOps 等方面都保持先进性,这样的系统就像陈年的老酒一样,历久弥香。而有些系统虽然刚刚开发完成,但如果在上述几个方面都做得不好,也可以把它叫做遗留系统。遗留系统在维护成本、合规性、安全性、集成性等方面都会给企业造成巨大的负担,但同时也蕴含着丰富的数据和业务资产。应该对遗留系统进行现代化,让它重新焕发青春。
相关文章:
Day949.遗留系统之殇:为什么要对遗留系统进行现代化? -遗留系统现代化实战
遗留系统之殇:为什么要对遗留系统进行现代化? Hi,我是阿昌,今天学习记录是关于遗留系统之殇:为什么要对遗留系统进行现代化?的内容。 不知道你是否跟曾经一样,身处一个遗留系统的漩涡之中&…...
DAY 45 Nginx服务配置
Nginx概述 Nginx: Nginx 是开源、高性能、高可靠的 Web 和反向代理服务器,而且支持热部署,几乎可以做到 7 * 24 小时不间断运行,即使运行几个月也不需要重新启动,还能在不间断服务的情况下对软件版本进行热更新。 对…...
如何收集K8S容器化部署的服务的日志?
做开发的同学都知道日志的重要性,日志的种类一般有接口日志、错误日志、关键步骤日志、用户操作日志等。本文主要详细讲解使用kubernetes容器化部署的服务该如何记录和收集日志。 一、使用标准输出方式 将想要记录的日志内容输出到stdout或stderr即可(…...
python删除csv文件中的某几列或行
1. 读取数据 用pandas中的read_csv()函数读取出csv文件中的数据: import pandas as pddf pd.read_csv("comments.csv") df.head(2)用drop函数进行文件中数据的删除行或者删除列操作。 2. 删除列操作 方法一:假设我们要删除的列的名称为 ‘观众ID’,‘…...
Redis持久化机制导致服务自启动后恢复数据过长无法使用以及如何关闭
场景 若依前后端分离版手把手教你本地搭建环境并运行项目: 若依前后端分离版手把手教你本地搭建环境并运行项目_霸道流氓气质的博客-CSDN博客 在上面搭建前后端分离的项目后,如果需要在windows服务上进行部署。 若依前后端分离版本,Windo…...
DAY 37 shell免交互
Here Document 概述 常用的交互程序:read,ftp,passwd,su,sudo cat也可配合免交互的方式重定向输出到文件 Here Document 的作用 使用I/O重定向的方式将命令列表提供给交互式程序标准输入的一种替代品 格式 命令 …...
用python脚本从Cadence导出xdc约束文件
用python脚本从Cadence导出xdc约束文件 概述转换方法先导出csv文件修改CSV文件 CSV转XDC检查输出XDC文件csv2xdc源代码下载 概述 在Cadence设计完成带有FPGA芯片的原理图的时候,往往需要将FPGA管脚和网络对应关系导入vivado设计软件中,对于大规模FPGA管…...
【C++ 六】内存分区、引用
内存分区、引用 文章目录 内存分区、引用前言1 内存分区模型1.1 程序运行前1.2 程序运行后1.3 new 操作符 2 引用2.1 引用基本使用2.2 引用注意事项2.3 引用做函数参数2.4 引用做函数返回值2.5 引用本质2.6 常量引用 总结 前言 本文包含内存分区、引用基本使用、引用注意事项、…...
markdown基本语法
来自神秘人儿的投稿! markdown的使用,可以参考https://markdown.com.cn/basic-syntax/ 标题:用 # 表示 段落:enter即可,两端之间有一个空行 换行:一行的末尾加两个或者多个空格,两端之间没有…...
第十篇 Spring 集成Redis
《Spring》篇章整体栏目 ————————————————————————————— 【第一章】spring 概念与体系结构 【第二章】spring IoC 的工作原理 【第三章】spring IOC与Bean环境搭建与应用 【第四章】spring bean定义 【第五章】Spring 集合注入、作用域 【第六章】…...
PADS-LOGIC项目原理图设计
最小板原理图设计 目录 1 菜单与工具使用 2 常用设置 2.1选项卡 2.2 图纸设置 2.3 颜色设置 3 设计技巧 3.1 模块化设计思路 3.2 元件放置 3.3 走线及连接符 4 原理图绘制 4.1 POWER原理图设计 4.2 MCU原理图设计 4.2.1晶振电路 4.2.2复位电路 4.2.3 BOOT电路 …...
36岁大龄程序员被裁,找了2个月工作,年包从100万降到50万,要不要接?
为了找到工作,你愿意接受降薪多少? 一位36岁的杭州程序员问: 36岁被裁,找了2个月工作,年包从100万降到50万,真心纠结,要不要接? 网友们分成了旗帜鲜明的两派,一派人认为不…...
Android Retrofit 源码分析
1、简介 Retrofit 是一种基于 Java 的 RESTful Web Service 客户端库,它可以将网络请求抽象出来并支持多种转换器,可以将 JSON、XML 和其他格式的响应数据自动转换为 Java 对象。Retrofit 通过注解的方式来描述 REST API 调用,使开发人员能够…...
CDN如何阻止网络攻击
随着网络技术的发展,网络攻击事件也越来越多,对企业和个人的安全和稳定造成严重威胁。为此,高防CDN应运而生,成为广大用户保障网络安全的重要工具。什么是高防CDN?高防CDN的特点有哪些?高防CDN如何阻止网络攻击?接下来让我们一…...
Mybatis-Plus -04 条件构造器与代码生成器
Mybatis-Plus--条件构造器与代码生成器 1 条件构造器1.1 > < 1.2 in notin1.3 between...1.4 orderBy...1.5 like... 2 代码生成器2.1 引入依赖2.2 生成器代码 1 条件构造器 通过条件构造器可以更加轻松的完成条件查询与更新(底层就是动态SQL) 1.1 > < ge 小于 &l…...
MapReduce高级篇——全局计数器
MapReduce Counter 计数器 概念 在执行MapReduce程序的时候,控制台输出日志中通常下面片段,可以发现输出信息中的核心词是counter,中文叫做计数器 在执行MapReduce城西过程中,许多时候,用户希望了解程序的运行情况,H…...
轻松掌握K8S目录持久卷PV/PVC的kubectl操作知识点04
1、介绍 在docker中可以将容器中的目录挂载出来,在k8s中pod可以部署在不同节点,假如该节点的机器宕机了,k8s可能就会将此Pod转移到其他机器,就不是原先的机器了。k8s有自己的一套挂载方案,如下图所示, 原…...
Appuploader证书申请教程
转载:IOS证书制作教程 点击苹果证书 按钮 点击新增 输入证书密码,名称 这个密码不是账号密码,而是一个保护证书的密码,是p12文件的密码,此密码设置后没有其他地方可以找到,忘记了只能删除证书重新制作&…...
acwing17给了一个头节点,从尾到头输出链表的元素,顺便练练容器
方法一 建立一个数组,从头到尾遍历一遍链表,然后将链表的每个元素的值赋给数组 犯了一个错误 新建的vector容器是一个可变长的数组,要想像数组下标那样访问前提是这个下标所指向的元素得存在,这也就跟那个声明一维数组得写出长度来…...
Linux 性能优化大全!
性能指标 高并发和响应快对应着性能优化的两个核心指标:吞吐和延时 应用负载角度:直接影响了产品终端的用户体验 系统资源角度:资源使用率、饱和度等 性能问题的本质就是系统资源已经到达瓶颈,但请求的处理还不够快࿰…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
Oracle11g安装包
Oracle 11g安装包 适用于windows系统,64位 下载路径 oracle 11g 安装包...
何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡
何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡 背景 我们以建设星云智控官网来做AI编程实践,很多人以为AI已经强大到不需要程序员了,其实不是,AI更加需要程序员,普通人…...
Copilot for Xcode (iOS的 AI辅助编程)
Copilot for Xcode 简介Copilot下载与安装 体验环境要求下载最新的安装包安装登录系统权限设置 AI辅助编程生成注释代码补全简单需求代码生成辅助编程行间代码生成注释联想 代码生成 总结 简介 尝试使用了Copilot,它能根据上下文补全代码,快速生成常用…...
