在《斯蒂芬·科尔伯特深夜秀》的一次采访中,喜剧演员拉里·戴维解释说,他的新年决心是“两次搬运”(第857集,2020年1月8日)。
例如,当把杂货搬进屋时,很想一次搬完,但那样你就会把哈密瓜掉在地上,然后你不得不清理残局。虽然看起来一次搬完会更快,但如果你把清理残局的时间算进去,那么两次搬运实际上会更快。
这段经历促使拉里将其作为新年决心来实践。当有疑问时,就两次搬运!
这种“两次搬运”策略并非石破天惊的突破。它不会治愈癌症,结束世界饥饿,或解决气候危机。然而,我采纳了这个理念,它带来了许多好处。
最直接的好处是,我现在更有可能腾出一只手来开门。从口袋里掏钥匙不再需要用胸膛和房子之间的杂货袋来挤压。
更大的好处来自于在编码和运营中都采纳了这个理念。
前几天,我正在为一些旧代码添加一个功能。这段代码报告了先前计算的结果,并带有各种可以启用或禁用的格式化选项。
这段代码非常复杂,因为某些选项会以影响其他选项的下游方式影响格式。这段代码能够在一个数据传递中满足所有各种选项和控制,并沿途打印报告。
然而,由于代码如此复杂,因此难以维护。更新一个功能会导致另一个功能出现错误,然后修复这些错误又会导致行数发生变化。那些行数是早期功能所需要的,而早期功能已经根据现在过时的假设打印了其部分。
总而言之,这段代码简直是一团糟。例如,它沿途维护着并行的计数器和统计数据,直到确定实际需要为止——类似于既买了猫粮又为宠物葬礼做准备,直到薛定谔的盒子最终被打开。
这段代码难以阅读。它是经典的意大利面条式代码——作为一个有意大利血统的人,我不会轻易使用这个短语。
我真诚地努力将我的新功能添加到这个不断增长的复杂循环中。
然后我想起了拉里的建议:两次搬运。
如果代码对数据进行两次传递,将会显著简化。一次传递收集数据,计算需要计数的事物,求和子总计,等等。第二次传递将获取所有这些信息并输出报告,并且会容易得多,因为它从一开始就拥有了它所需的所有信息。没有薛定谔的猫。
我应该做这个改变吗?看起来两次搬运效率会更低。原始版本没有分配新的内存,因为它是在迭代数据时输出报告的。新版本将需要分配数据结构来存储中间结果。
这是一个经典的复杂性与内存工程决策:忍受复杂性还是忍受潜在的内存耗尽。
我的第一台电脑非常慢,只有5K的RAM。这教会了我吝啬地使用内存。有时我会回到我的旧习惯,计算每一字节的内存,即使这无关紧要。正如高德纳教导我们的那样,过早的优化是万恶之源。
我很快意识到自己很傻。要报告的数据量很少超过一屏幕——通常只有零个、一个或两个项目,并且可能极少情况下会达到数千个。此外,输出通常发送到终端(stdout),这将比我在重写中可能损失的任何微小的编码效率更耗时。
我重构了代码以进行两次传递。事实证明,新代码甚至没有执行很多分配。我最大的担忧是白费了。
现在,有了大大简化的代码,我可以轻松添加新功能。它更简洁,更易于测试。这给了我信心去清理一些小的瑕疵,并使整个报告更具可读性。
不是要吹牛,但新格式非常漂亮。不用谢我。谢谢拉里!
有时,与其每次搬运少量袋子进行多次搬运,不如在第一次搬运时根本不带任何袋子。这就是预先走一遍——只是探索路径以侦察问题。
预先走一遍可以及早发现问题。当搬运大型家具时,预先走一遍可以清理路径,提醒你注意麻烦的狭窄走廊转弯,并移开宠物,宠物正坐在新沙发将要放置的地方。任何这些任务都更容易用两只空闲的手来完成。
有时我会预先走一遍去打开酒架门,然后再带着新酒瓶回来。
谁没有遇到过入住酒店房间后才发现房间一团糟、有人占用或其他无法使用的情况?然后你不得不把所有的行李都带回接待处,然后再带到你的新房间。现在我先预先走到房间去查看一下。
金丝雀部署是分布式计算版本的预先走一遍。将新软件推广到数千个实例是有风险的。如果串行执行,可能需要很长时间。然而,并行执行可能会推送错误的发布版本,并非常迅速地重新创建大规模中断。
金丝雀策略是指在将新软件推广到n-1个剩余实例之前,先将其推广到单个副本以验证发布版本。第一次搬运很慢,因为它等待软件初始化并开始报告心跳或其他健康检查。如果成功,第二次搬运将并行升级所有其他副本,确信它们也会启动。
一个经过良好测试的软件包怎么可能无法运行?这里测试的不仅仅是软件的功能。它也是部署系统本身。
例如,我记得有一次金丝雀死了,避免了可能发生重大中断的情况。协调推广的主机磁盘空间不足。它没有将安装包复制到所有机器,而是复制了一个零长度的文件。由于软件将工作分为两次搬运,因此唯一的中断是金丝雀,而不是整个应用程序。由于有数百个副本,一个副本宕机几乎不会被注意到。
在另一个实例中,软件已成功推广到QA(质量保证)环境,但当在生产环境中部署完全相同的位时,金丝雀死了。这怎么可能?事实证明,该版本需要在配置文件中添加一个新条目。该字段已添加到QA环境中使用的配置文件中,但未添加到生产环境中使用的配置文件中。这个错误在金丝雀阶段早期被捕获。又避免了一次重大中断。
Kubernetes的金丝雀部署使用此策略升级副本。通常,首先升级一小部分副本。只有在未检测到错误时,才会升级其余副本。
该策略也用于大规模软件部署。例如,Chrome等网络浏览器具有金丝雀发布版本,允许一些用户选择早于大多数客户接收最新版本。即使在向其余用户推广新版本时,这些用户也经常首先升级到随机样本。这也发生在功能级别。有风险的新功能有时会以禁用状态发布,然后在启用给所有用户之前,先为随机样本的金丝雀用户启用它们。
药物研究试验包含类似金丝雀的阶段。第一阶段试验通过仅冒少量生命风险来衡量安全性。后续阶段在更大规模的群体中测试疗效。
我经历过的最精彩的“两次搬运”示例涉及将遗留应用程序迁移到云端。我不能为此邀功;这是一个同事的想法。
该项目涉及将应用程序从遗留数据中心迁移到云提供商。我们不能简单地将整个系统“原封不动地迁移”到云端。该应用程序与另一个将要保留的应用程序紧密耦合。
因此,该项目涉及解耦这两个应用程序。该项目需要修改两个系统,在两者之间拆分数据库,以及其他不胜枚举的复杂性。哦,而且必须在有限的停机时间内完成。
该项目的前两次尝试都失败了。第一次失败是因为缺乏资源。一年后,第二次尝试资源更好,但仍然失败了。尚不清楚具体原因。传统观点认为,相关人员只是耗尽了时间或没有得到足够的支援。
如果这是真的,那么第三次尝试只需要更多的资源和更多的时间。对吗?错了。
在研究了架构、代码和过去的失败之后,一位工程师做出了一个重要的发现:这个项目是不可能的!失败的原因是之前的团队试图做一些不可能成功的事情。
迁移有很多步骤。有些步骤可以在失败时回滚,但风险最高的步骤不能。没有撤消按钮。对于没有撤消的步骤,甚至没有办法提前测试以确保不需要撤消。
在一个足够复杂的系统中,了解可能发生哪些问题的唯一方法是尝试该过程,让它失败,回滚,然后重试。如果没有办法回滚,并且你知道该过程不可能在第一次尝试时奏效,那么它基本上是不可能的。
你可以冒险并希望它在第一次尝试时奏效吗?不。希望不是一种策略。
然后我的同事有了一个绝妙的主意:两次搬运。
她得出结论,如果当前的架构使项目不可能实现,那么我们首先必须创建一个改变架构的项目。然后迁移项目才有可能实现。
这两个项目(搬运)都很复杂,但都不是不可能的。
第二个项目仍然具有最大的风险。虽然现在所有步骤都具有回滚能力,但有一个步骤具有不可逆转的点,在该点对数据库进行的更改将无法撤消。然而,在此步骤之前,有机会进行广泛的测试,以确保提交到新数据库是安全的。
宣布了三个停机时间窗口用于此次停机。在第一个窗口期间,发现了一些问题,这些问题需要数天才能修复。执行了回滚,因为尚未执行不可逆转的点步骤。
团队还有两次机会。下一个窗口发现了一些新的故障。大多数故障都得到了实时修复,尽管有些故障被认为足够小,可以稍后修复。不需要第三个窗口,这让项目中的每个人以及客户都松了一口气。
这两个项目花了一年时间才完成。作为一个项目是不可能完成的;作为两个项目,它获得了成功。
划分项目还有一个附带的好处,即为客户提供了更好的体验。这是意想不到的,但令人欢迎的。最初的计划要求客户在特定的停机窗口期间,在特定的某一天在其端进行更改。期望数百名客户同时进行更改是不现实的。客户无法在停机事件之后(即在不可逆转的点之后)测试其更改,这一事实使该计划更加岌岌可危。
两阶段方法消除了这个问题。所有客户可见的更改自然而然地落在了第一个项目期间,而所有用户可见的停机时间都落在了第二个项目期间。现在可以在更长的时间跨度内的任何时间进行和测试更改。这使客户更容易,并大大降低了协调此类更改的复杂性。
向客户传达所有这些信息也变得更容易了。解释复杂的更改加上停机时间是很困难的。现在可以单独解释更改。停机时间将在几个月后发生,并且可以使用客户习惯的标准停机时间公告流程轻松解释。解释两个孤立的(就客户而言)事件比解释一个组合的、复杂的大型事件更容易。
该项目的完整故事可以在Stack Overflow博客上找到
传播两次搬运的福音。它不仅可以提高团队的生产力,还可以减少在使用该技术时所需的解释量。前几天,我的伴侣沮丧地看着我,问我为什么车门没关。我回答说:“我正在两次搬运”,然后走开了。她放松了。这就是全部需要的解释。
虽然你不应该从拉里·戴维那里吸取你生活中的所有教训,但“两次搬运”策略是一个很好的工具,可以放在你的工具箱中。
无论你的项目是像把杂货搬进屋子这样简单,还是像多年的工程项目这样复杂,“两次搬运”都可以简化项目,减少出错的机会,提高成功的可能性,并使解释更容易。
谢谢你,拉里!
感谢Benjamin Dumke-von der Ehe、Jessica Hilt、Jeremy Peirce、George V. Reilly、Tom Reingold、Mandy Riso、Margret Treiber和许多其他人对早期草稿的反馈。
托马斯·A·利蒙切利 是Stack Overflow Inc.的技术产品经理,他在新泽西州的家中工作。他的著作包括云管理实践 (https://the-cloud-book.com)、系统和网络管理实践 (https://the-sysadmin-book.com) 和系统管理员的时间管理 (https://TomOnTime.com)。他的博客是 EverythingSysadmin.com,并在 @YesThatTom 上发帖。他拥有德鲁大学计算机科学学士学位。
版权 © 2024 由所有者/作者持有。出版权已授权给。
最初发表于Queue vol. 22, no. 2—
在数字图书馆中评论本文
Catherine Hayes, David Malone - 质疑评估非加密哈希函数的标准
尽管加密和非加密哈希函数无处不在,但在它们的设计方式上似乎存在差距。加密哈希存在许多由各种安全需求驱动的标准,但在非加密方面,存在一定程度的民间传说,尽管哈希函数历史悠久,但尚未得到充分探索。虽然针对真实世界数据集的均匀分布非常有意义,但当面对具有特定模式的数据集时,这可能是一个挑战。
Nicole Forsgren, Eirini Kalliamvakou, Abi Noda, Michaela Greiler, Brian Houck, Margaret-Anne Storey - DevEx 在行动
随着领导者寻求在财政紧缩和人工智能等变革性技术的背景下优化软件交付,DevEx(开发者体验)在许多软件组织中越来越受到关注。技术领导者凭直觉接受,良好的开发者体验能够实现更有效的软件交付和开发者幸福感。然而,在许多组织中,旨在改善 DevEx 的拟议倡议和投资难以获得支持,因为业务利益相关者质疑改进的价值主张。
João Varajão, António Trigo, Miguel Almeida - 低代码开发生产力
本文旨在通过介绍使用基于代码、低代码和极限低代码技术进行的实验室实验结果,研究生产力差异,从而提供对该主题的新见解。低代码技术已明确显示出更高的生产力水平,为低代码在短期/中期内主导软件开发主流提供了强有力的论据。本文报告了程序和协议、结果、局限性和未来研究机会。
Ivar Jacobson, Alistair Cockburn - 用例至关重要
虽然软件行业是一个快节奏且令人兴奋的世界,新的工具、技术和技巧不断被开发出来为商业和社会服务,但它也很健忘。在其快速前进的匆忙中,它容易受到时尚的 whimsy 的影响,并且可能会忘记或忽略针对其面临的一些永恒问题的行之有效的解决方案。用例最初于 1986 年引入,后来普及,是这些行之有效的解决方案之一。