写点什么

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

评论

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

【YashanDB知识库】离线升级一章22.2不支持直接升级到23.1

YashanDB

yashandb 崖山数据库 崖山DB

VMware vCenter Server 6.7 U3u (安全更新) - ESXi 集中管理软件

sysin

vSphere vmware vcenter esxi

获取闲鱼商品详情api

api开发

架构升级:火山引擎VeDI实验平台服务能力进一步优化

新消费日报

GreatSQL 构建高效 HTAP 服务架构指南(MGR)

GreatSQL

汽车长翅膀:GPU 是如何加速深度学习模型的训练和推理过程的?

Baihai IDP

程序员 AI gpu LLM 企业号 7 月 PK 榜

马斯克宣布“全球最大AI训练集群”投入使用!苹果、Mistral AI、英伟达、OpenAI加入小模型争霸赛!|AI日报

可信AI进展

人工智能

LeetCode题解:2073. 买票需要的时间,模拟,JavaScript,详细注释

Lee Chen

安吉尔:净水科技的“自转”革命,守护每一滴纯净

科技热闻

客户案例 | 识货基于向量检索服务 Milvus 版搭建电商领域的向量数据检索平台

阿里云大数据AI技术

大数据 向量检索 Milvus

零信任持续高速发展,新场景下展现惊人潜力

芯盾时代

身份安全 数据安全 零信任

重磅 - Github上免费大屏来啦,教你快速搭建

JEECG低代码

报表工具 大屏设计器 数据库可视化 仪表盘设计器

人工智能|RAG 检索增强生成

霍格沃兹测试开发学社

古画新韵——李逸弘国画作品赏析

科技热闻

VMware vCenter Server 8.0U3a 下载 - 集中式管理 vSphere 环境

sysin

vSphere vmware vcenter esxi

京东商品描述API:返回值的详细解读

技术冰糖葫芦

API Explorer API 编排 api 货币化 API 文档

中小制造业工厂要不要上MES系统

万界星空科技

制造业 生产管理系统 mes 云mes 万界星空科技

微店一键复制商品软件使用教程

api开发

利用PDCA循环来进行持续改善

Anliven

团队管理 个人提升 迭代管理 持续优化 组织运营

人工智能丨RAG 检索增强生成

测试人

软件测试

计算机视觉与图像分类:技术原理、应用与发展前景

天津汇柏科技有限公司

计算机视觉 图像分类

ConsenSys 高管:别傻乎乎盯着 CT 了,能明说的大概不是 Alpha

TechubNews

借助 NGINX Plus 优化企业环境中的 MQTT 部署

NGINX开源社区

开源 物联网 IoT mqtt nginx 开源版

即将揭晓:思迈特软件如何用AI Agent引领商业智能格局?

ToB行业头条

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