写点什么

程序开发者去世,代码没人懂,一个 bug 导致千万损失

  • 2020-05-09
  • 本文字数:3321 字

    阅读完需:约 11 分钟

程序开发者去世,代码没人懂,一个bug导致千万损失

系统出故障了。当年负责写这个程序的开发者早在十五年前就去世了,现在已经没有人能读得懂他的代码了…


现在一些关键系统的运行仍依赖于过时的软件,但编写他们的人要么离职要么已经去世。中间也缺少维护或更新,导致现在几乎没人能理解它们,而且一旦出现 Bug 就会给企业造成不可挽回的损失。


而现实中的这种例子,远比你想象中的要多。

一个令人深思的故事

我的一位客户负责数项世界排名前一百的养老基金,该公司在前几个月成功的将程序搬到了云端。作为项目的主任架构师,前两天我很意外地直接收到了 CIO 的短信:“抱歉打扰,我们出 S1X 级的大问题了。你能下午飞过来吗?”



“S1X”是他们对“比最严重级别还要糟,级联影响到业务其它非直接相关部分”问题的定义。


事情看起来十万火急,当天晚上我就飞到现场进行了诊断,发现是该客户的系统中一个批处理任务发生了崩溃。


该任务每天晚上执行一次,通过写一个 CSV 文件为某些养老金计算缴费率,再将计算的结果输出到另一个收益(benefit)分配程序。原先收益分配程序设定为在缴费(contribution)低于预测(projection)时会向客户发出报警。由于上一个处理任务已发生崩溃,不再产生输出,因此程序认为“所有缴费为零”。


所以系统立即向该养老基金的经理们发出大量警报邮件,基金经理们被吓得赶紧从该项目里撤离了。


起初没有人找得出问题发生在哪里,在大家的记忆和工作记录中,这个批处理任务也从未崩溃过。编写该批处理程序的人已经在 15 年前离世了,并且数十年前就不再是该企业的员工了。尽管该批处理的程序代码规模不大,但非常难读。因为在编程时主要考虑的是提高计算效率,并未考虑如何适合他人阅读。当然,程序也没有留下任何测试。


事故发生的前一天,脚本在运行环境中的编排发生了一次更改。这被认为是导致事故的罪魁祸首。幸运的是,这个更改做了版本 push,工程部门据此回溯到先前的版本。但不幸的是,这只使事情变得更糟。


最后我们通过提供热修补脚本的方式解决了这个问题。但实质上这次崩溃已经给该基金造成了 170 万美元(约 1203 万人民币)的直接损失。

“数据溃烂”(Bit Rot)问题

事实上类似的故事并非孤例。我在 2012 年离开英特尔加入 Sun 公司,切身体会到他们的 SPARC 产品线做得是多么的糟糕。Sun 在互联网泡沫时代的杀鸡取卵行为,导致此后 SPARC 远远落后于英特尔的至强产品线。


我的经理都和我说,要在英特尔至强服务器上运行模拟程序,因为 SPARC 服务器“非常慢”。更严重的问题在于,英特尔不仅 CPU 性能更好,而且具有制造优势,这意味着 CPU 的制造成本也大大降低。


随之而来的问题很明显:既然远远落后于竞争对手,那么为什么客户还是会购买我们的 SPARC 芯片?一位高级架构师向我给出了令人震惊的答案。那是因为我们的客户的软件系统过于僵化,只能在 SPARC/Solaris 系统上运行。而迁移到 x86/Linux 对客户而言是一项艰巨的任务。许多客户甚至丢失了源代码,无法重新编译应用。


他们能做的最好选择,就是升级到最新一代的 SPARC 处理器,无论这样的处理器性能多慢,价格多么昂贵。


这就是问题所在。我们整个部门的业务模式,完全围绕着这个国家中那些溃烂的软件系统。

维持运转的代价

我加入 Amazon 后,发现自己面对正是这样一个为遗留系统而构建适用的原型。该系统是另一个团队基于大量技术债务开发的,并且团队早已解散。之后该项目的所有权就移交给我们的团队……事实操明,这并非揽下一件好事。所以开发人员陆陆续续跳槽到其他的团队。我加入时的十多名团队成员中,一年后一位都没有留下。


该系统从表面上看并非一无是处。它使用了现代编程语言和技术栈(Java 8)编写,由拿着六位数收入的开发人员所组成的团队进行着日常维护,并不断更新以修复错误和添加新功能。尽管如此,测试修改(turnover)依然是拖累整个系统的显著负担。所有权变更和团队更替导致整体代码设计、端到端功能、最佳实践和调试技术等大量实操知识的丢失。尽管我们一直努力保持项目的推进,但感觉就像进入了一片沼泽地,四处亡羊补牢,深陷战争迷雾(Fog of War)中。


设想一下,一个运行在第一版 Java 上的项目,开发活动几乎为零,没有开发人员负责。还有比这更糟的项目吗?

如何预防发生灾难性故障

软件开发人员致力于构建健壮、无错误的系统,无需过多人工维护就能正常运行多年。据此标准,上面所说的养老金脚本无疑是非常成功的项目。


然而现实很严峻,再好的项目有发生崩溃的一天。最终,所有内容都需要做更新。导致原因可能是:


  • 系统运行所基于的硬件系统停产了。

  • 系统的依赖关系不再可用。

  • 依赖关系中出现了严重安全漏洞,而唯一可用的安全补丁仅适用于并不后向兼容的版本。

  • 应用开发基于一些已不再成立的假设。

  • 甚至是整个世界发生了改变,软件必需因势而变。


无论出于何种原因,变更都是不可避免的。唯一的问题是,当最终需要变更时,它的代价有多大。


对于一个活跃维护的系统,变更就不会那么痛苦。但是,对于一个已有几年甚至数十年没有维护的系统,那么很多因素都可能会导致灾难性的错误。例如:


  • 构建系统的开发人员已经离职。

  • 源码丢失。

  • 开发人员不了解如何正确地编译源码,并构建可执行文件。

  • 开发人员不了解如何部署系统。

  • 开发人员不了解如何正确地配置运行可执行文件。

  • 开发人员对代码的架构和实现一头雾水。

  • 开发人员不了解使代码功能正常运作所依赖的常量和隐含假设。

  • 开发人员不了解如何运行自动化测试。

  • 开发人员不了解如何调试测试问题。

  • 开发人员不了解如何调试生产故障。

  • 开发人员不了解如何获取生产日志和指标度量。


一种解决方案是对上述问题做尽量详细的记录。但文档并非最优的解决方案,因为其中难免会有遗漏。再全面的文档,也比不上自己亲自动手操作。

理想的做法

一个好的开端,就是企业指定专门的开发人员全面负责上述所有问题。但这还不够。


如果仅仅反复“阅读文档”,那么就会产生厌倦。人们并不能从中获得实践经验,进而解决实际问题。


如果加上“绩效审核”,那么人们更倾向于新的出彩项目,很有可能会简单地抹去并掩盖旧的问题,甚至直接从中剔除问题。如果没有真正面对的可交付成果或挑战,许多人自然会选择一条最轻松的道路。


真正要想避免软件发生溃烂,唯一的方法是确保项目的持续推进,即使看起来毫无必要,或是存在风险。建立、维护和验证实操知识和能力的最佳方法,就是不断做出变更,并测试这些变更是否能成功地执行。一旦项目停止推进,那么相关实操知识就会过时和消解。


即使原地打转听上去很可笑,但这对疏于维护而言仍是一种进步。事实上,维护人员总是可以做一些事情实现向前推进,虽然步伐可能很小。


一种做法是使用所有依赖关系的最新版本去更新开发环境,例如:


  • 从 JDK 8 迁移到 11。

  • 更新 JVM,使用 G1 垃圾回收机制替代原先的 CMS。

  • 将 GCC 编译器从版本 5 更新到 7。

  • 将数据库从 Postgres 9.5 更新到 Postgres 11。

  • 将 AWS SDK 从版本 1.10 更新到 1.11。

  • 在生产环境中安装最新的 Linux 发行版。


在一些情况下,依赖关系会过时。这时就需要考虑整体迁移到新的架构。例如:


  • 从 SPARC 迁移到 x86;

  • 从 Solaris 迁移到 Linux。


同样,保持开发人员的战斗力,可对应用做如下更新:


  • 修复顽疾和一些边缘用例。

  • 加固自动测试套件。

  • 清理技术债务。

  • 做性能优化。

  • 实现新特性。

  • 增量重构代码库,改进可读性。


上述变化势必会带来暂时性风险,并产生看似“不必要的”支出。开发人员难免会犯一些错误,甚至引入新的错误。面对这些代价时,人们很容易退而求其次,认为“如果还没有破裂,就不要修复。”


对于一个业务价值不大的系统,这可能是合理的做法。但对于任何关键任务系统而言,忽略问题将仅会掩盖较小的瞬态风险,而没有解决永久的灾难性风险。一旦有一天需要紧急调试或更新系统,那么企业将无所适从。


对于任何关键任务系统,至关重要的就是维持实操知识和能力。做到这一点的唯一方法,就是不断地开展实操。企业的大脑和肌肉一样,不使用,就会失效。


作者介绍:


Rajiv Prabhakar,毕业于密歇根大学和斯坦福大学,之后曾在 Intel 和 Sun Microsystem 公司工作五年,参与了 Jaketown、Skylake 和 SPARC 设计团队。之后转向软件相关工作,先后任职于 Amazon、Google、Engineers Gate,并数次自己组建创业公司。


原文链接:


https://software.rajivprab.com/2020/04/25/preventing-software-rot/


2020-05-09 09:0211274

评论

发布
暂无评论
发现更多内容

业财一体,精细管控丨华为云SparkPack助力成长型企业数字化转型

YG科技

WebAssembly:让Istio变得更强大

谐云

istio WebAssenbly

穷人版生产力工具,好用得飞起 「GitHub 热点速览」

EquatorCoco

sql git 开源

云会议成为企业高频服务,华为云会议为何成为众多企业的选择?

轶天下事

视频会议进入云时代,为何企业需要华为云会议

轶天下事

福昕软件与国广传媒达成战略合作,共促AI技术创新发展

新消费日报

浅谈kubernetes存储—glusterfs故障排查

谐云

kuberedge

红队攻防之快速打点

权说安全

网络攻防

Databend v1.2 版本发布!Data + AI

Databend

华为云WeLink——助力企业数字化办公降本增效,提升竞争力

轶天下事

安全是企业数字化的基石,华为云WeLink如何构建数字化办公护盾

轶天下事

AntDB数据库将携创新性解决方案亮相2023可信数据库发展大会

亚信AntDB数据库

数据库 AntDB AntDB数据库

落地领域大模型应知必会 (1) :主要微调方法总览

Baihai IDP

人工智能 白海科技 大语言模型 大模型微调 领域大模型

传统网络环境应付不了企业发展需求,华为云下载加速解决方案体验如何?

YG科技

云原生MYSQL数据库架构分享

谐云

MySQL 云原生

内卷时代如何上云,华为云Web及移动App上云又有何亮点?

YG科技

大模型之Huggingface初体验

程序员架构进阶

Transformer 大模型 7月日更 7月月更 huggingface

云上办公时代,华为云会议如何保障企业的开会效率与数据安全?

轶天下事

企业数字化办公,华为云WeLink为何能脱颖而出

轶天下事

网页直播源码知识分享:“直播卫士”,查杀病毒功能在此!

山东布谷科技

软件开发 直播 源码搭建 网页开发 直播源码

什么是KubeEdge?

谐云

kuberedge kurbernetes

自动化回归测试平台 AREX 0.4.0 版本发布

AREX 中文社区

开源 自动化测试 接口测试

一文讲透 Redis 事务 (事务模式 VS Lua 脚本)

高端章鱼哥

lua redis vs

MySQL笔记之Checkpoint机制

互联网工科生

MySQL 高可用 CheckPoint

华为云SparkPack:成长型企业的数字化转型利器

YG科技

什么是WebAssembly及其必要性

谐云

WebAssenbly

微服务架构概览图

wiflish

微服务 微服务架构 架构治理

避免“一选定终身”,那些从就读到就业的AI真相

脑极体

AI

从低谷逆转,数字化转型企业可以信任华为云SparkPack

YG科技

程序开发者去世,代码没人懂,一个bug导致千万损失_语言 & 开发_Rajiv Prabhakar_InfoQ精选文章