当前最佳简单系统

Hacker News Top 新闻

摘要

Dan North 提出了“当前最佳简单系统”(BSSN)方法,作为软件开发中快速粗糙与过度工程之间的中间道路,提倡满足当前产品需求的简单性。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/07/03 17:15

# 当前最佳简单系统 来源:https://dannorth.net/blog/best-simple-system-for-now/ 目录- 当前最佳简单系统的特征 (https://dannorth.net/blog/best-simple-system-for-now/#characteristics-of-the-best-simple-system-for-now)- 当前 (https://dannorth.net/blog/best-simple-system-for-now/#for-now) - 简单 (https://dannorth.net/blog/best-simple-system-for-now/#simple) - 最佳 (https://dannorth.net/blog/best-simple-system-for-now/#best) - 反对 BSSN 的理由 (https://dannorth.net/blog/best-simple-system-for-now/#the-case-against-bssn)- 原型阶段过于复杂 (https://dannorth.net/blog/best-simple-system-for-now/#it-is-overkill-for-a-prototype) - 它不完整 (https://dannorth.net/blog/best-simple-system-for-now/#it-is-incomplete) - 它效率低下 (https://dannorth.net/blog/best-simple-system-for-now/#it-is-inefficient) - 为什么我们不这么做? (https://dannorth.net/blog/best-simple-system-for-now/#why-do-we-not-do-this)- 良好习惯 (https://dannorth.net/blog/best-simple-system-for-now/#good-habits) - 勇气 (https://dannorth.net/blog/best-simple-system-for-now/#courage) - 谦逊 (https://dannorth.net/blog/best-simple-system-for-now/#humility) - 从哪里开始? (https://dannorth.net/blog/best-simple-system-for-now/#where-do-you-start)- 示例:JSON 库 (https://dannorth.net/blog/best-simple-system-for-now/#example-the-json-library) - 示例:XML 流处理器 (https://dannorth.net/blog/best-simple-system-for-now/#example-the-xml-streamer) - 总结 (https://dannorth.net/blog/best-simple-system-for-now/#tl-dr) *只要烘焙得当,鱼与熊掌兼得。* “我们可以走捷径,日后偿还;也可以走彻底的路,现在付出代价。”这似乎是软件开发中“完美主义”与“实用主义”之间的根本冲突,但我认为这未必是一种取舍。一位 CTO 朋友用清理山上两条路的比喻来解释。左边那条路是即兴的、粗糙的,用砍刀和蛮力开辟——你不指望别人跟着你,只是硬闯过去——但进展很快。右边那条路更宽、更清晰、铺砌得更好,也更扎实,但需要花费时间逐步修建。左路适合侦察前方路况。右路则是你带领大家走的路,因为知道路是安全的。我的朋友一直处于一种紧张状态:一边是走左路的人——这恰好也是赚钱最快的方式(他在一家交易公司工作,所以这个论点极具分量!)——另一边是走右路的人,他们想构建一个坚韧、可持续的产品。这描述了我合作过的许多组织。双方的紧张关系都源于认为“另一种方式”是错误的。右路倡导者担心左路者是在积累技术债务,日后会反噬,还打着“实用主义”的幌子。左路者则认为右路者是“完美主义者”,过度设计和镀金(而且通常有大量业务经理认同他们)。我提出存在一条双方都忽视的中间道路。我称之为**当前最佳简单系统(Best Simple System for Now,简称 BSSN)**。我花了很长时间才构思出这个想法,它多年来一直“隐藏在显而易见的地方”。有几个来源想法要么暗示了它,要么至少有所提示,我希望在此致谢,但我从未见过明确的表述。那么,就在这里。 ## 当前最佳简单系统的特征 (https://dannorth.net/blog/best-simple-system-for-now/#characteristics-of-the-best-simple-system-for-now) > 当前最佳简单系统是**最简单的**系统,它满足产品**当前**的需求,并以**适当的标准**编写。它没有多余或过度设计的代码,任何代码都恰到好处地健壮可靠,不多也不少。 “当前最佳简单系统”这个短语的每个部分都是有意为之,并且相互强化。任何一部分的偏离都意味着它不再是当前最佳简单系统,而是某种更弱的东西。让我们逐一解析这些部分。 ### 当前 (https://dannorth.net/blog/best-simple-system-for-now/#for-now) 系统*不应以任何方式预测未来*。这与我作为程序员所接受或给出的几乎所有建议都背道而驰!当我们程序员面对一个问题时,我们倾向于通用解决方案。甚至有一幅 xkcd 漫画为此作证,所以这一定是真的:xkcd 漫画“通用问题” (https://xkcd.com/974/) 我们距离“这是规则引擎!”或“这是状态机!”只有一小步之遥,因此我们高效的工程思维决定节省一些时间——以及返工——并立即采用这种解决方案。做出这个决定的部分原因是知道每次变更都有成本,因此分几步到达某个地方总是不如我们“知道”需要的那一大步更高效。我们依赖自动且无意识的模式匹配来为有意识的处理腾出空间。在《思考,快与慢》中,Daniel Kahneman 分别称它们为系统 1 和系统 2。模式匹配是无意识的系统 1 活动;我们擅长它,而且它自然而然地发生。“看到真实情况”更难,需要有意识的思考和努力。*当前*设计恰恰与此相反。这是一种**尽管**大脑呈现给你的模式,仍能看清真实情况的艺术。这是 BSSN 的关键——也许可以发音为“bison”,作为我们通常最后剃掉的那些牦牛(yaks)的伴侣。 ### 简单 (https://dannorth.net/blog/best-simple-system-for-now/#simple) 当我设计某物时,通常有两个我在冲突。“聪明”的我——拥有数十年经验和自负的那个——**知道**那边的这个事物会先变化,所以我们应该把它做成接口。或者我们**几乎肯定**会有这种数据量,所以我们应该直接构建这个并发版本的算法,而不是直截了当的串行版本。更谦逊的我明白,我的任何预测都会接近正确但实际错误,就像我之前多次那样接近正确但实际错误,所以也许现在先别做。更谦逊的我通常输掉。所以当未来发生时,我最终处于两种模式之一:要么绕开我做出的假设,因为我的预测并不*完全是*接下来发生的事;要么回溯并更改代码,使其能够按我所需的方式灵活调整。但别担心,聪明的我向我保证,这只是偶然,下次我一定能完全做对!这在我身上一而再再而三地发生。我写软件赚钱已经超过 30 年了,所以你可能以为我现在已经弄明白了,但并没有。在这些年里,我见过并在许多代码库中工作过,我暗自确信其他人也同样如此。我遇到的大多数代码都有推测性的接缝和接口,它们已有几十年历史,从未被利用过;还有算法和技术的选择,对于 20,000 个并发用户来说会很棒,但实际只为十二个人服务;还有我们做梦都想象不到的“可扩展性”和“灵活性”钩子和扩展点,也许写这些代码是希望这能成为他们的**杰作**系统,定义他们职业生涯的系统。当然,由于所有这些复杂性之间的交互,这些代码充满了细微的错误。我一再学到的是,**简单是当前状态的函数**。换句话说,我不断寻找适合当前需求和约束的最小复杂度,**仅此而已**。当这些需求和约束发生变化时,“简单”的定义必然随之改变。 > 完美不在于无以复加,而在于无可减省。 —— 安托万·德·圣-埃克苏佩里 按照这个定义,我们追求完美!如果你能从当前系统中拿走任何东西,而它仍然能完成**当前**所需的任务,那么它从一开始就不属于那里。BSSN 不是通过迎合所有可能的未来来实现“面向未来”、“可扩展性”或“灵活性”,而是通过如此简单,以至于我们可以随意地对其进行调整。如果需要处理更高的负载,我们有信心实现。如果需要管理更广泛的输入,或提供不同的验证,或与意想不到的上游或下游服务集成,我们有信心也能做到。如果需要沿着我们尚未考虑到的维度演进,我们至少能像使用其他任何解决方案一样轻松地做到。没有推测性的接口,没有过于宽泛的数据类型,没有能用特定代码代替的通用功能。相反,它是高度主观的,在设计上有意狭窄。相反,我亲身经历过**高尔定律** (https://en.wikipedia.org/wiki/John_Gall_%28author%29#Gall%27s_law)¹ (https://dannorth.net/blog/best-simple-system-for-now/#fn:1) 无数次,从企业数据字典到通用工作流解决方案,再到点击配置的规则引擎,都有一个不可避免的 3-5 年失败弧线。 > 一个有效的复杂系统总是从一个有效的简单系统演化而来。从头设计一个复杂系统从来不会有效,也无法通过修补使其工作。你必须从一个有效的简单系统重新开始。 —— John Gall ### 最佳 (https://dannorth.net/blog/best-simple-system-for-now/#best) 保持简单并不意味着偷工减料。在任何情况下,都有一种正确的方式来做事情,我们可以选择以这种方式来做。完美主义者和实用主义者面临的挑战在于,“正确的方式”是上下文的;支撑核心业务功能的代码应该比实验性功能的草图有更高的质量门槛。对一个投资不足与对另一个投资过度一样,都是一种风险。前者,风险在于稳定性和韧性。后者,则是**机会成本**,我将在下面讨论。此外,“草图”代码的能力与设计健壮、有韧性的解决方案的能力一样,都是一种技能。草图不等于黑客!我认识的工程师中,擅长“正规”代码的远比擅长草图的多。草图是提供**恰好足够**的质量,以便你能维持一个方向,同时知道以后可以回去填补空白(或者以最小的投资损失丢弃草图)。黑客代码很快就会变得不可管理。草图良好的代码只是真实事物的轻量级版本。当 Ward Cunningham 谈到“可能起作用的最简单事物”时,他指的是这个。他并非在引用某种柏拉图式的简单理想,而是在指他能尝试的任何微小步骤,以推进他的理解。在 2004 年与 Bill Venners 的一次精彩采访中,他观察到“仅仅是编写这一行为就理清了我们的思路” (https://www.artima.com/articles/the-simplest-thing-that-could-possibly-work#:~:text=the%20mere%20act%20of%20writing%20it%20organized%20our%20thoughts)。最佳代码是揭示意图的;它在内聚与耦合之间取得平衡,使其易于导航、易于推理、易于更改。如果代码中存在重复,或者更糟的是,**思想**上的近似重复,那么这不是最佳代码。相反,如果代码过度追求 DRY,仅仅因为两处的代码看起来相似而导致耦合,那么这也不是最佳代码。如果代码没有将领域术语放在首位,并且直接映射到你现实世界的用例,那么这也不是最佳代码。我曾经写过关于我认为的好代码、**令人愉悦的**代码的文章,我将其表述为 CUPID (https://cupid.dev/): - **可组合(Composable):** 能与其他代码良好协作;依赖最小 - **Unix 哲学:** 只做一件事,而且做得彻底; - **可预测(Predictable):** 行为、可观测性、运行时特性、故障模式可预测;更改时会发生什么也可预测 - **惯用(Idiomatic):** 使用对熟悉该技术的开发者来说熟悉的模式和惯用法 - **基于领域(Domain-based):** 命名、行为和代码结构都揭示意图 重要的是要注意,你可以**以同样快的速度**——实际上更快——编写令人愉悦的代码,而不是黑客式的、“实用”的代码。CUPID 建立在 Richard Gabriel 的**代码宜居性** (https://cupid.dev/#joyful-software) 概念之上,他将其描述为使用代码库时感到**舒适**和**自信**。你不需要数天或数周的深入思考来编写最佳代码,但你需要**良好的习惯** (https://en.wikiquote.org/wiki/Kent_Beck#Quotes)。这些习惯需要时间来培养,理想情况下是通过与持有这些价值观、并且愿意与你分享他们习惯的人一起工作。我很幸运能与那些慷慨分享时间和知识的人一起工作。有时我会**谈论他们** (https://youtu.be/i11a3NGMZkU)。你经常会听到结对编程或测试驱动开发等 XP 技术被提及为优秀代码的前提,但不要被狂热者或理论家吓到。这些技术只是工具;既非必要也非充分,但通常很有帮助。虽然我是一个毫不掩饰的技巧粉丝,但我曾在大型 C 和 C++ 代码库中使用过结构良好、意图揭示、易于导航的代码,而这些代码并没有使用这些技术。相反,我也经历过严格测试驱动的代码,它缺乏内聚性,领域理解差,更改起来痛苦不堪,感觉就像在等价于复式记账的代码中拖拽。见鬼,我甚至写过不少这样的代码。现在想起来都让我觉得难受。 ## 反对 BSSN 的理由 (https://dannorth.net/blog/best-simple-system-for-now/#the-case-against-bssn) 让我们看看**反对**编写当前最佳简单系统的一些论点。 ### 原型阶段过于复杂 (https://dannorth.net/blog/best-simple-system-for-now/#it-is-overkill-for-a-prototype) *如果我们只是要把这段代码用完即弃,为什么要投资让它成为“最佳”代码?* 许多原型最终进入生产环境,此时我们希望调整它们来适应新功能。由于原型只是黑客作品,没有好方法添加新功能,所以我们只能把它们塞到任何能塞进去的地方。成功的原型不会在发布前重写,而是在发布后继续构建。当只有几个 else-if 分支时,再添加一个分支影响不大。当有 20 或 30 个分支时,再添加一个分支影响也不大。除非我们主动选择,否则永远不会有一个合适的时机,比起再添加一个边缘情况或条件,清理这一团乱麻更为明智。因此,我们的选择是:忍受混乱、重写它,或者从一开始就不要陷入这种境地。我最喜欢的技术专家之一 Randy Shoup 说过,如果一个数字企业在扩展过程中不重写其整个技术栈,那么它很可能从一开始就过度设计了。我相信在大多数情况下这是真的,但在少数情况下,他们坚持使用当前最佳简单系统,就根本不需要重写。在被 Meta 收购之前,WhatsApp 使用 Erlang 代码库,由大约 13 名工程师构建和管理,扩展到超过 5 亿活跃用户。同样,SQLite 关系型数据库是**部署最广泛** (https://www.sqlite.org/mostdeployed.html)

相似文章

我的软件北极星

Lobsters Hottest

作者提出了一套软件开发的优先级列表:对最终用户的有用性、正确性、可维护性和效率,认为所有工作都应致力于最大化用户效用。

选择无聊技术与创新实践

Hillel Wayne — Computer Things

文章认为,团队应选择无聊且已被充分理解的技术以确保可靠性,同时可以在开发实践上自由创新,比如TCR(测试&&提交||回滚),这些实践更易于采纳和放弃,没有长期维护负担。

构建Gin:简单胜于容易

Hacker News Top

作者回顾了为Go语言创建Gin Web框架的过程,强调了“简单胜于容易”的设计原则,以平衡魔法和样板代码,并介绍了框架的上下文对象如何演变以兼容Go标准库。

快优于慢

Lobsters Hottest

一篇博客文章,主张软件开发中的速度能带来更好的学习和决策,并提供实用建议,如避免拖延和尽早分享工作。