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

云栖号资讯:【点击查看更多行业资讯】
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!

首先,我要在这篇文章中分享的想法可能不是每个人都喜欢,G U S _ l f C u但我希望大家就这个话题展开讨论。% v
假设你想使用你最喜欢的编程语言为你的系统构建一个代理服务器,你该怎么办?首先选择自己比较熟悉的 Web 框架,这样就F # f m w 3有了一个项目脚手架,然D 8 $ n I N G R j后就可以直接开始实现了。完成这个简单的步骤X ^ J o后,你就可以启动服务器并立即接收 HTTP 请求。在服务器中,你希望根据请求的源地址和内容体对请求进行身份验证和流量T k E r f D v过滤。因此,除了 Web 服务器框架之外,你还得选择并安装一个中间件来处理身份验证,以及一个库来简化新请求的处理和过滤(现在不是 90 年代了,你不希望重新发明轮子,自U J ~ r f Z Z己开发所有这些东西,你希望在两到三天内就让你的系统在生产环境中运行起来)。你可以使用一些胶水代码将所有这些部分粘结在一起,然后做一些单元和集成测试,检查一切是否正常,然% } $ , a v . & {后就可以开始下一步的工作了。

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

行动要快,基础要稳

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

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

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

在 StackExchange 网站的一条j 9 )回复中,有人还讲述了他们与 SpaceX 团队在 GDC` n q x q y g 2015⁄2016 上的互动。他们谈到了三重冗余系统以及 SpaceX 如何使用 Acz t ftor-Judge 系统。简而言之,有 3 个双h ) Z 5 $ W核 ARM 处理器运行在定制的板上(根据 elteto )。对于每个决G { n策,一个“flight string”会比较单个处理器上每& T V f v F y {个核d t 8心的结果。如果输出匹配,则向不同的控制器发送命令。处理器有 3U R u % W Y $ g 个(双核),这意味着每个控制器 / 传感器将收到三个不同的命p q Z令。然后,控制器充当裁判,比较这三个命令。如果三个命令一致,它们就执行操作。如果有一个命令不一致,那么控制器会执行那个来自之前一i S 7 ; 5 _ &直发送正确命令的处理器的命令。
这意味着在任何给定的点有 6 个飞行软件(flight software)的运行过程。
来源: htG O - ktps://yasoob.me/poh s L c g O v ssts/software_engineering_within_spacex_launch/

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

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

不是停止使用,而是更聪明地使用
当然,我并不主张完全停止使k | v G [用外部依赖,一切从头开始实现。那太疯狂了!我想说的是,避免使用那些非绝对必要的依赖。例如,当 Go 的 HTF H $ hTP 标准库给了你构建一p = 4 $个出色的 Web 服务器所需要的一切时,为什么还要使用一个 Go WebA x 1 K . % 框架呢?(我非常同意这{ ? k 4 1个看法。)这是我几年前开始构建8 ; i O x 4 J )我的第一个 Go REST API 时所遵循的做法的一个完美示例,现在我已经完全放弃了。这就是我所说的“停止滥用依赖”的一个例子。我不需要 Web 框架。是的,它在开始t F A : n s z s X时大大加速了我的开发,但我不知b z L v n ` H l道它的工作原理,在遇到错误或想要提高系统的性能时,我完全无法控制,我会因此犯强迫症的(我在这里简要提及了)。

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

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

停止滥用外部依A 4 m 9 C E赖的四大理由

下面我们看看我得出这些结论的原因,以及为什么我尽量避免过度使用外部依赖。至少,你应该知道,使用库存在如下这些风险。
你使用的库和n ~ ` b - ^外部依赖可能已经过时、缺少维护,或者~ ^ g ~ A ? h t / 并且存在隐藏的安全缺陷和性能瓶颈。当你是代码的所有者时,你知道自己在做什么,但是当你使用其他地方的代码时,你不知道开发人员是否犯了错误(如果你不阅读和理解你添加到项目中的代码)。你不知道他是否使用了低效的实现,或者如J * `果你不检查源代3 { ` | ` 0 D ,码,也不知道他是否添加了恶意代码。更重要的是,Z { a J : F c I s如果开发者放弃了这个库,而你在未来的系统中还需要依赖它,那该怎么办?那就需要你了解所使用的代码。下面这段话完美地说明了使用外& ^ m ;部依赖的风险:
攻击者经常使用社会化工程将他们的包D l v % 9 Q放入应用程序中。他们创建一个具有有用特性的包,然后偷偷加入一些恶意代码。一旦代码进入应用程序,用户启K 2 w c , p动应用程序时,这些代码就会攻击用户。

从今年 3 月开始,一名黑客就用这种方式窃取了价值 150 万美元的加密货币。
第 0 天(3 月 6 日):攻击者在 npm 上发布了一个模块 electron-native-notify。这似乎很有用——它帮助 Electron 应用以/ c L I C ~ . h一种跨平台的方式触发本机通知。那时,它还没有任何恶意I s E G代码。

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

第 17 天:攻击者添加恶意载荷。
第 41 到 66 天:重新构建应用程序,加入该依赖项的最新版本,其中包括 electro~ i )n-native-notify。此时,它开始向服务器发送用户的“种子”(用户名 / 密码组合)。然后,攻击者G a ) Z ( j 2 *就可以利用这些种子掏空用户的钱包。
第 90 天:用户向 npm 报告了 electron-native-not? ^ kify 中的可疑行为,然后他们通s J N }知了加密货币平台,后者将资金从易受攻击的钱包转移到了安全的钱包。

摘自: https://bytecodealliu W vance.org/articles/announcing-the-bytec) N F k aode-alliance
有很多这样的例子可以说明不加选择地使用库所带来的风险(快速搜索一下,看看这是怎么发生的)。
库会显I ) ? 0 }著增加代) . j ^ J | | L -码的大小和编译时间。这篇博文很棒,生动地说明了 RusU X T F : 6 Pt 的问题。我强烈推h d 2 v荐大家阅读一下。你可能会遇到这样的场景:你包含一个完整的库来执行某项任务,但最终使用的是整个库& n H o的一个函数。或者,在 package.j y 2 )son 中有些依赖项,你在开发和试验解决方案时用到了但最后忘了清理。所有这些都对& O U I你不利。

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

最后,在系统中不使用任何库K V i /是一种挑战和乐趣。当然,这并不是一个令人信服的理由,N ^ u w s但即使你不同意我的观点,我也建议你一生中至少要这样设计一次。你会看到P N 3 W w在这个过程中你学到了多少东西,如何更f i H O - ? M好地掌握和理解你所选择的编程语言,以及完全控制你的系统、任何时候都可以清楚地了解你的系统8 l G D在做什么是多么得令人心旷神怡。我要说的是,我曾经是那种拆开祖父的z C c ; Z | e sW i W k s - W J音机来了解它是如何工作的孩子。

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

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

我们应该开始 0 | 1 Zg H d Z c写一份弹性软件宣言吗?我不知道,但是让我们开始讨论这个问题G O l +。同时,我很想听听你的想法。

其它参考资料

下面这些资料也可以证明我的观点:
软件祛魅。如果你必须从这个列表中选择一篇阅读材料的话,那就选这篇吧。它完美地补充了我的观点(我是在本文写完时发现的这篇文章,所以我的讨论没有加入其中的许多思想)。
为什么软件会失败?
“快速行动,敢于} . . f |突破”真是废话!

【云栖号在线课堂】每天都有产品技术专家2 S R B - m + o U分享!
课程地址:http( ? 0 M Ps://yqh.aliyun.com/zhibo+ W - 6

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-07-03
本文作者:Alfonso de la Rocha
本文来自:“InfoQ”,了解相关信息可以关注“InfoQ”