NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

程序开发者去世,代码没人懂,一个 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/


公众号推荐:

跳进 AI 的奇妙世界,一起探索未来工作的新风貌!想要深入了解 AI 如何成为产业创新的新引擎?好奇哪些城市正成为 AI 人才的新磁场?《中国生成式 AI 开发者洞察 2024》由 InfoQ 研究中心精心打造,为你深度解锁生成式 AI 领域的最新开发者动态。无论你是资深研发者,还是对生成式 AI 充满好奇的新手,这份报告都是你不可错过的知识宝典。欢迎大家扫码关注「AI前线」公众号,回复「开发者洞察」领取。

2020-05-09 09:0210828

评论

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

浅析小程序插件

Speedoooo

小程序 插件 小程序容器

坐标中国|中国速度,挑战极限驱动发展“快车”

天翼云开发者社区

数据库-MySQL-基础(1)

Geek_a7ae15

MySQL 数据库

菲尔兹奖得主小平邦彦:数学是什么?

图灵社区

数学

给传统零售企业穿上“云武装”!

天翼云开发者社区

【等保小知识】等保备案和等保测评有什么差别?

行云管家

等保 等级保护 等保测评 等保备案 等级测评

菲尔兹奖得主小平邦彦:数学是什么?

图灵教育

数学

大闸蟹套路多?“码”上溯源让你安心吃蟹!

旺链科技

区块链 产业区块链 大闸蟹

从无到有,一步一步教你搭建微服务电商项目,包含笔记+视频+源码

小二,上酒上酒

微服务

八月裸辞,九月疫情在家闭关狂刷面试题,十月成功上岸京东物流

小二,上酒上酒

Java 阿里

一文透彻理解微服务架构及相关组件

程序员小毕

Java 程序员 面试 微服务架构 程序人生

小程序技术能否成为移动应用市场新机遇?

Speedoooo

小程序 小程序容器 App生态

5年大厂开发经验,加上这份Java高性能架构笔记,终于拿到了架构师薪资

小二,上酒上酒

Java 大厂 大厂面试 Java面试题

阿里p8免费公开五份Java架构师学习手册,助力金九银十

小二,上酒上酒

Java 架构 阿里

阿里内部独家Java架构面试题,面试再不过来找我

小二,上酒上酒

MySQL spring JVM 多线程 MQ

MatrixOne混沌测试之道

MatrixOrigin

数据库 分布式 混沌测试

Linux系统下基础命令介绍

DS小龙哥

10月月更

Linux下基础命令(二)

DS小龙哥

10月月更

新型蜜罐有哪些?未来方向如何?

郑州埃文科技

蜜罐 隔离攻击IP

百度App性能优化工具篇 - Thor原理及实践

百度Geek说

Java App 企业号十月 PK 榜

带你读AI论文丨ACGAN-动漫头像生成

华为云开发者联盟

神经网络 GAN AI论文 ACGAN-动漫头像 企业号十月 PK 榜

Github三天点击破亿,四天助力金九银十,精通SpringCloud微服务架构,成就大厂梦

小二,上酒上酒

Java spring 编程 Spring Cloud

多云管理平台支持哪些平台?哪款比较火?

行云管家

云计算 多云 云管理

再不看就来不及了,腾讯Spring Boot高阶笔记,限时开源48小时

小二,上酒上酒

Java 面试 大厂

阿里内部JVM G1GC纯手写学习笔记,你确定看得完?

小二,上酒上酒

编程 JVM 马士兵

idea多模块启动

拾光师

IDEA 10月月更

MASA MAUI Plugin IOS蓝牙低功耗(三)蓝牙扫描

MASA技术团队

MASA MAUI Xamarin MASA Blazor

玩转云端| 提升边缘应用交付效率,天翼云Serverless边缘容器有妙招

天翼云开发者社区

当代人假期这几种行为,你中了几条?

天翼云开发者社区

逆流而上!整合阿里高频考点2023Java岗面试突击指南手册震撼首发

了不起的程序猿

Java 程序员 互联网 Java工程师 秋招

阿里内部力荐Spring生态全家桶,务必每个程序员人手一份

小二,上酒上酒

Java 阿里 大厂

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