为什么要设计出具有“永久性”的软件?

2020 年 7 月 03 日

为什么要设计出具有“永久性”的软件?

对于我们身边的每件事,从买杂货到与家人和朋友聊天,我们越来越依赖软件。就像我们不希望我们的汽车在下次旅行中出现故障一样,我们也无法容忍软件在某些情况下出现故障。更新一个库不应该破坏你的系统,而考虑到它们应该能够在自然环境中运行数年,不需要任何外部管理或维护,一个软件应该(尽可能地)按照设计桥梁的方式来设计。


本文最初发布于 Alfonso de la Rocha 的个人博客,由 InfoQ 中文站翻译并分享。


首先,我要在这篇文章中分享的想法可能不是每个人都喜欢,但我希望大家就这个话题展开讨论。


假设你想使用你最喜欢的编程语言为你的系统构建一个代理服务器,你该怎么办?首先选择自己比较熟悉的 Web 框架,这样就有了一个项目脚手架,然后就可以直接开始实现了。完成这个简单的步骤后,你就可以启动服务器并立即接收 HTTP 请求。在服务器中,你希望根据请求的源地址和内容体对请求进行身份验证和流量过滤。因此,除了 Web 服务器框架之外,你还得选择并安装一个中间件来处理身份验证,以及一个库来简化新请求的处理和过滤(现在不是 90 年代了,你不希望重新发明轮子,自己开发所有这些东西,你希望在两到三天内就让你的系统在生产环境中运行起来)。你可以使用一些胶水代码将所有这些部分粘结在一起,然后做一些单元和集成测试,检查一切是否正常,然后就可以开始下一步的工作了。



行动要快,基础要稳


你知道上述方法的问题吗?项目 80%的代码都是别人的,来自项目使用的外部库(见上图),20%的代码是自己的(还有一些来自 StackOverflow 的“灵感”),你自己的代码是作为胶水和粗略的定制,使你的用例可以使用那些库。这种做法是发展趋势,不要误会我的意思,如果你正在构建的是一项众所周知的技术,或者是一个辅助系统(如你个人的 CMS),或者是一个内部报表系统,就该这么做,你很快就可以让什么东西运行起来,你会尽快从你的系统受益。当你在关键系统或科技公司平台(有数百万用户)的核心实现上继续采用这种方法时,问题就真正出现了。相信我,无论公司的规模大小,都会是这样(问下他们那些由咨询公司开展的项目和人力投入——70%是应届毕业生)。


软件开发确实受到了“快速行动,打破常规”哲学的影响。这很好,我完全同意尽早测试新想法,快速迭代验证概念,并在开发中尽可能做到“精益”。软件原型设计成本很低,为什么不呢?这就是创新,对吧?当你开始交付伟大的项目和绝妙的想法时,问题就出现了,它们开始破坏生产环境,伤害你的用户。甚至 Facebook 也意识到,是时候从“快速行动,打破现状”转向“行动要快,基础要稳”了


你永远不会期望一个土木工程师在对他的概念验证进行了几次修改后,就把他的桥(一个关键的基础设施)“完工”了。更重要的是,桥梁和土木工程项目的设计是永久的,为什么软件的设计也要是永久的?正确地设计一座桥需要花时间,并且对其原理(材料、结构、弹性等)有一个良好的理解。如果我们想要把软件工程视为一种工程实践(它值得),我们就需要开始构建健壮的、有弹性的和长久的系统,并且不要草率地在我们的项目中加入任何我们偶然遇到的代码片段。当然,我要为我的极端说法道歉,我并不是说所有的软件都是草率开发出来的。前几天,我读了 SpaceX 这篇关于软件工程的文章,我真的很喜欢他们使用软硬件冗余来最小化像航天飞机这样的关键系统中的潜在错误。我是说,让我们都更像航天飞机软件工程师一点


在StackExchange网站的一条回复中,有人还讲述了他们与SpaceX团队在GDC 2015⁄2016上的互动。他们谈到了三重冗余系统以及SpaceX如何使用Actor-Judge系统。简而言之,有3个双核ARM处理器运行在定制的板上(根据elteto)。对于每个决策,一个“flight string”会比较单个处理器上每个核心的结果。如果输出匹配,则向不同的控制器发送命令。处理器有3个(双核),这意味着每个控制器/传感器将收到三个不同的命令。然后,控制器充当裁判,比较这三个命令。如果三个命令一致,它们就执行操作。如果有一个命令不一致,那么控制器会执行那个来自之前一直发送正确命令的处理器的命令。

这意味着在任何给定的点有6个飞行软件(flight software)的运行过程。

来源:https://yasoob.me/posts/software_engineering_within_spacex_launch/



正是这些想法促使我决定尝试摆脱一些外部依赖,看看这是否会提升我的效率和代码质量(这也是我发表本文的原因)。


不是停止使用,而是更聪明地使用


当然,我并不主张完全停止使用外部依赖,一切从头开始实现。那太疯狂了!我想说的是,避免使用那些非绝对必要的依赖。例如,当 Go 的 HTTP 标准库给了你构建一个出色的 Web 服务器所需要的一切时,为什么还要使用一个 Go Web 框架呢?(我非常同意这个看法。)这是我几年前开始构建我的第一个 Go REST API 时所遵循的做法的一个完美示例,现在我已经完全放弃了。这就是我所说的“停止滥用依赖”的一个例子。我不需要 Web 框架。是的,它在开始时大大加速了我的开发,但我不知道它的工作原理,在遇到错误或想要提高系统的性能时,我完全无法控制,我会因此犯强迫症的(我在这里简要提及了)。


使用一个框架就像驾驶一辆新车,在高速开出许多英里后,它抛锚了,而你不知道如何修理它。


我绝不会建议你将一些核心依赖项从“经常使用”的列表中删除,因为为你的项目从头编写它们提供的功能将是自杀行为(因为它们是由极具天赋的团队花很长时间开发出来的)。举几个例子,我最近在我的系统里使用了它们,比如 libp2p、Geth 客户端、Polkadot 或 Qiskit(好吧,你可能会从我选择的库中看出了我有点偏向)。如果我的实现中没有它们,我就完了。当然,如果你需要构建 p2p 网络,我不会要求你重写 libp2p。如果你需要从它那里获得“其他东西”,那么因为它是开源的,所以你总是可以大胆地提出添加建议。如果你想要玩下量子计算,那么对于 Qiskit 也同样如此。它们背后都有很棒的开发团队和研究人员致力于最大限度地发挥其产品的作用,所以,只要你知道自己在做什么,以及为什么要使用它们(务必谨记使用它们可能带来的潜在开销),就用吧。


所以我的观点不是“停止使用外部依赖”,而是“明智地使用它们”。如果你已经知道如何使用自己偏好的编程语言所提供的功能来解组和解析 JSON,那么就不要偷懒,不要使用一个没有得到良好维护的 JSON 解析库。你可以避免不必要的开支和麻烦。同样,不要使用你发现的第一个“有效”的库,你应该花一些时间研究可选方案,从而选择一个更符合你需求的,甚至完全放弃使用库,自己编写代码来完成这项任务(同样,对于一座桥所需的材料和地基,土木工程师会做一个彻底的分析)。



停止滥用外部依赖的四大理由


下面我们看看我得出这些结论的原因,以及为什么我尽量避免过度使用外部依赖。至少,你应该知道,使用库存在如下这些风险。


  • 你使用的库和外部依赖可能已经过时、缺少维护,或者/并且存在隐藏的安全缺陷和性能瓶颈。当你是代码的所有者时,你知道自己在做什么,但是当你使用其他地方的代码时,你不知道开发人员是否犯了错误(如果你不阅读和理解你添加到项目中的代码)。你不知道他是否使用了低效的实现,或者如果你不检查源代码,也不知道他是否添加了恶意代码。更重要的是,如果开发者放弃了这个库,而你在未来的系统中还需要依赖它,那该怎么办?那就需要你了解所使用的代码。下面这段话完美地说明了使用外部依赖的风险:


攻击者经常使用社会化工程将他们的包放入应用程序中。他们创建一个具有有用特性的包,然后偷偷加入一些恶意代码。一旦代码进入应用程序,用户启动应用程序时,这些代码就会攻击用户。


从今年3月开始,一名黑客就用这种方式窃取了价值150万美元的加密货币


第0天(3月6日):攻击者在npm上发布了一个模块electron-native-notify。这似乎很有用——它帮助Electron应用以一种跨平台的方式触发本机通知。那时,它还没有任何恶意代码。


第2天:为了完成盗窃,攻击者必须将这个模块放入加密货币应用中。他们选择的媒介是一个帮助用户管理其加密货币的应用程序Agama Wallet的一个依赖项。


第17天:攻击者添加恶意载荷。


第41到66天:重新构建应用程序,加入该依赖项的最新版本,其中包括electron-native-notify。此时,它开始向服务器发送用户的“种子”(用户名/密码组合)。然后,攻击者就可以利用这些种子掏空用户的钱包。


第90天:用户向npm报告了electron-native-notify中的可疑行为,然后他们通知了加密货币平台,后者将资金从易受攻击的钱包转移到了安全的钱包。


摘自:https://bytecodealliance.org/articles/announcing-the-bytecode-alliance


有很多这样的例子可以说明不加选择地使用库所带来的风险(快速搜索一下,看看这是怎么发生的)。


  • 库会显著增加代码的大小和编译时间。这篇博文很棒,生动地说明了Rust的问题。我强烈推荐大家阅读一下。你可能会遇到这样的场景:你包含一个完整的库来执行某项任务,但最终使用的是整个库的一个函数。或者,在package.json中有些依赖项,你在开发和试验解决方案时用到了但最后忘了清理。所有这些都对你不利。

  • 库可能会向你隐瞒解决方案中的许多权衡、设计决策和潜在的故障点。与信任各种外部依赖项相比,在系统所有的基本代码都由你完全设计并实现时,识别潜在的攻击媒介要容易得多。

  • 最后,在系统中不使用任何库是一种挑战和乐趣。当然,这并不是一个令人信服的理由,但即使你不同意我的观点,我也建议你一生中至少要这样设计一次。你会看到在这个过程中你学到了多少东西,如何更好地掌握和理解你所选择的编程语言,以及完全控制你的系统、任何时候都可以清楚地了解你的系统在做什么是多么得令人心旷神怡。我要说的是,我曾经是那种拆开祖父的收音机来了解它是如何工作的孩子。


软件设计应具永久性


我们需要开始构建有弹性的软件。我们越来越意识到,这是智能合约和分布式系统领域的一个趋势。在这类系统中,网络中大量的用户共享并执行相同的基本代码。因此,软件故障或安全缺陷不会只影响一个人的基础设施,而是影响系统中的每个人。已经有很多这样的案例报道,智能合约的失败造成了数百万美元的损失客户软件中的漏洞危害了他们的系统


对于我们身边的每件事,从买杂货到与家人和朋友聊天,我们越来越依赖软件。就像我们不希望我们的汽车在下次旅行中出现故障一样,我们也无法容忍软件在某些情况下出现故障。更新一个库不应该破坏你的系统,而考虑到它们应该能够在自然环境中运行数年,不需要任何外部管理或维护,一个软件应该(尽可能地)按照设计桥梁的方式来设计。


我们应该开始编写一份弹性软件宣言吗?我不知道,但是让我们开始讨论这个问题。同时,我很想听听你的想法。


其它参考资料


下面这些资料也可以证明我的观点:



查看英文原文:


https://adlrocha.substack.com/p/adlrocha-software-should-be-designed


2020 年 7 月 03 日 09:502132
用户头像
陈思 InfoQ编辑

发布了 555 篇内容, 共 189.6 次阅读, 收获喜欢 1064 次。

关注

评论

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

架构师0期04周命题作业

喵呜的小哥哥

年薪百万架构师推荐的888页Java王者级核心宝典,offer直接来

无予且行

拥抱开源开放,易观技术开发者的星海征途

易观大数据

海豚调度 调度引擎

架构师0期04周总结

喵呜的小哥哥

如何写好一封邮件?

石云升

职场 职场成长 邮件

攻克SpringBoot底层源码后,才发现开发原来这么香

无予且行

Java spring Spring Boot 开发 Java 面试

javascript 部分数据类型的用法

Isuodut

面试官:十亿级数据ES搜索怎么优化?我直接傻了

犬来八荒

Java 面试 大厂

信创舆情一线--英特尔暂停向浪潮供货

统小信uos

服务器 舆情 芯片

这20道微服务面试题要是不会,offer就与你无缘

犬来八荒

Java 架构 微服务 面试题 Java 面试

让你秒懂Spring中Mybatis的花样配置

小谈

Java spring Spring Cloud mybatis Java 面试

极客大学架构师训练营 系统架构 第8课 听课总结

John(易筋)

极客时间 系统架构 极客大学 极客大学架构师训练营 系统架构演化

到底什么是HashMap?

小闫

Java spring 后端 JVM hashmap

被“假”老干妈耍惨了?憨憨腾讯花1624万卖萌,引全网吃瓜!

程序员生活志

腾讯 互联网 大厂

七月份最新“美团+字节+腾讯”面试题,测试一下你能走到哪一面?

犬来八荒

Java 面试 java面试 大厂面试 线程’

如何构建你自己的 JVM (2) HelloWorld

孤星可

Java JVM 深入理解JVM

终于有大佬把TCP/IP协议讲清楚了!面试再也不怂面试官提问了

小闫

jdk JVM Netty buffer TCP/IP

Linux 性能优化实战 笔记-IO篇

王传义

Java 面试必考的 6 个技能,都在这了

架构大数据双料架构师

Google官方MVP+Dagger2架构详解

小吴选手

架构 架构师 架构是训练营

计算机操作系统基础(九)---存储管理之段页式存储管理

书旅

php laravel 线程 操作系统 进程

Week4总结

王志祥

极客大学架构师训练营

系统架构:学习小结

行下一首歌

极客大学架构师训练营

从 0 到 1 搭建技术中台之推送平台实践:高吞吐、低延迟、多业务隔离的设计与实现

伴鱼技术团队

kafka 缓存 分布式架构 消息推送 push

使用 Flutter 快速实现请假与写周报应用

LeanCloud

flutter 数据 教程 后端开发

游戏夜读 | 关卡设计新手必看

game1night

如果是你,年薪80万和阿里P7月薪36K,会怎么选?

犬来八荒

Java 腾讯 面试 阿里 java面试

基于 Flagger 和 Nginx-Ingress 实现金丝雀发布

郭旭东

Kubernetes CI/CD

当国产iVX遇上新晋产品PowerPlatform,能否披荆斩棘、稳住阵脚?

代码制造者

程序员 编辑器 低代码 快速开发 开发工具

【自学成才系列一】multipass安装篇

小朱

multipass

「NIO系列」——之Reactor模型

小谈

Spring Boot reactor 后端 nio SpringCloud

为什么要设计出具有“永久性”的软件?-InfoQ