「转载」面向痛苦的编程
2023-1-10
| 2024-9-2
0  |  Read Time 0 min
type
status
date
slug
summary
tags
category
icon
password
有一天,有人问我一个有趣的问题:“你在一家初创公司工作的同时,如何证明冒如此大的风险来构建Storm是合理的?” (Storm 是一个实时计算系统)。我可以看到,从局外人的角度来看,投资如此庞大的项目对于初创公司来说似乎风险极大。但从我的角度来看,构建 Storm 一点也不冒险。这很有挑战性,但没有风险。
我遵循的开发风格可以大大降低像 Storm 这样的大型项目的风险。我将这种风格称为“面向痛苦的编程”。面向痛苦的编程可以这样概括:除非你感到没有技术的痛苦,否则不要构建技术。它适用于大型的架构决策以及较小的日常编程决策。面向痛苦的编程可以确保您始终专注于重要的事情,从而大大降低风险,并且可以确保您在尝试进行大量投资之前精通某个问题领域。
对于面向痛苦的编程,我有一句口头禅:“首先让它成为可能。然后让它变得美丽。然后让它变得更快。”

首先使其成为可能

当遇到您不熟悉的问题域时,尝试立即构建“通用”或“可扩展”解决方案是错误的。您只是对问题领域的了解不够深入,无法预测您未来的需求。您将使不必要的事情变得通用,从而增加复杂性并浪费时间。
最好只是“解决问题”并非常直接地解决你手头的问题。这使您可以完成需要完成的工作并避免浪费工作。当你解决问题时,你会越来越多地了解问题空间的复杂性。
Storm 的“使其成为可能”阶段是使用队列和工作人员破解流处理系统的一年。我们了解了如何使用“ack”协议来保证数据处理。我们学会了使用队列和工作人员集群来扩展实时计算。我们了解到,有时您需要以不同的方式对消息流进行分区,有时是随机的,有时是使用哈希/修改技术来确保相同的实体始终发送到相同的工作人员。
我们甚至不知道我们正处于“使其成为可能”阶段。我们只是专注于构建我们的产品。不过,队列和工作人员系统的痛苦很快就变得严重。扩展队列和工作系统非常乏味,而且容错能力远未达到我们想要的水平。很明显,队列和工作人员范例没有处于正确的抽象级别,因为我们的大多数代码都与路由消息和序列化有关,而不是我们关心的实际业务逻辑。
同时,开发我们的产品促使我们在“实时计算”问题空间中发现新的用例。我们为我们的产品构建了一个功能,可以计算 Twitter 上 URL 的覆盖范围。覆盖率是指在 Twitter 上接触某个 URL 的唯一用户数量。这是一项困难的计算,仅一次计算就可能需要数百次数据库调用和数千万次展示才能区分。我们最初在单台机器上运行的实现对于硬 URL 来说需要一分钟多的时间,很明显我们需要某种分布式系统来并行计算以使其更快。
引发 Storm 的关键认识之一是“到达问题”和“流处理”问题可以通过简单的抽象来统一。

然后把它变得美丽

当你通过破解问题来探索问题空间时,你会绘制出问题空间的“地图”。随着时间的推移,您将在问题领域中获得越来越多的用例,并对构建这些系统的复杂性产生深入的理解。这种深刻的理解可以指导创建“美丽”的技术来取代您现有的系统,减轻您的痛苦,并启用以前难以构建的新系统/功能。
开发“美丽”解决方案的关键是找出解决您已有的具体用例的最简单的抽象集。尝试预测您实际上没有的用例是错误的,否则您最终会过度设计您的解决方案。根据经验,您尝试进行的投资越大,您需要越深入地了解问题领域,并且您的用例也需要越多样化。否则,您将面临第二系统效应的风险。
notion image
“让它变得美丽”是指你使用你的设计和抽象技能将问题空间提炼成可以组合在一起的简单抽象。我认为美丽抽象的开发类似于统计回归:您在图表上有一组点(您的用例),并且您正在寻找适合这些点(一组抽象)的最简单曲线。
您拥有的用例越多,您就越能找到适合这些点的正确曲线。如果没有足够的点,则图表可能会过度拟合或欠拟合,从而导致工作浪费和过度设计。
让它变得美丽的一个重要部分是了解问题空间的性能和资源特征。这是您在“使其成为可能”阶段学到的错综复杂的知识之一,在设计漂亮的解决方案时您应该利用学到的知识。
通过 Storm,我将实时计算问题域提炼为一小组抽象:流、喷口、螺栓和拓扑。我设计了一种新的算法来保证数据处理,消除了对中间消息代理的需求,而中间消息代理是我们系统中最复杂和最痛苦的部分。流处理和覆盖范围这两个表面上截然不同的问题,如此优雅地映射到 Storm 上,这有力地表明我正在做一些大事。
我采取了额外的步骤来获取 Storm 的更多用例并验证我的设计。我询问了其他工程师,了解他们正在处理的实时问题的细节。我不只是问我认识的人。我还在推特上表示,我正在开发一个新的实时系统,并想了解其他人的用例。这引发了许多有趣的讨论,使我对问题领域有了更多的了解并验证了我的设计思想。

然后让它快点

一旦构建出精美的设计,您就可以放心地投入时间进行分析和优化。太早进行优化只会浪费时间,因为您仍然可能重新考虑设计。这称为过早优化。
“让它变得更快”并不是指系统的高级性能特征。对这些问题的理解应该在“使其成为可能”阶段获得,并在“使其变得美丽”阶段进行设计。 “让它变得更快”是指微观优化和收紧代码以提高资源效率。因此,您可能会担心“使其变得美丽”阶段中的渐近复杂性等问题,并在“使其快速”阶段中关注恒定时间因素。

冲洗并重复

面向痛苦的编程是一个持续的过程。您构建的精美系统为您提供了新的功能,使您能够在问题空间的新的和更深层次的领域“使其成为可能”。这将学习反馈给技术。您经常需要调整或添加已经提出的抽象来处理越来越多的用例。
Storm 已经经历了很多这样的迭代。当我们第一次开始使用 Storm 时,我们发现我们需要能够从单个组件发出多个独立的流。我们发现添加一种称为“直接流”的特殊流将允许 Storm 将批量元组作为具体单元进行处理。最近,我开发了“事务拓扑”,它超越了 Storm 的至少一次处理保证,并允许为几乎任意实时计算实现精确一次消息传递语义。
就其本质而言,在您不太理解的问题领域中进行破解并不断迭代可能会导致一些草率的代码。面向痛苦的程序员最重要的特征是对重构的不懈关注。这对于防止意外复杂性破坏代码库至关重要。

结论

在面向痛苦的编程中,用例就是一切。它们的价值相当于黄金。获取用例的唯一方法是通过黑客获取经验。
大多数程序员都会经历一定的演变。你一开始就努力让事情正常运转,并且你的代码完全没有结构。代码很草率,复制/粘贴很普遍。最终,您将了解结构化编程和尽可能共享逻辑的好处。然后,您将学习如何进行通用抽象并使用封装来更轻松地推理系统。然后,您会痴迷于使所有代码通用,使代码可扩展以使您的程序面向未来。
面向痛苦的编程拒绝承认您可以有效地预测当前没有的需求。它认识到,在不深入了解问题领域的情况下尝试使事物变得通用将导致复杂性和浪费。设计必须始终由真实、有形的用例驱动。
 

原文地址

 
「转载」通过博客打入硅谷「转载」即使没有读者你也应该写博客
  • Utterance
Catalog