写点什么

听起来不错但几乎行不通的系统理念

  • 2025-03-05
    北京
  • 本文字数:3557 字

    阅读完需:约 12 分钟

大小:1.65M时长:09:35
听起来不错但几乎行不通的系统理念

本文最初发布于 Steven Sinofsky 的博客 Hardcore Software。



@Martin_Casado在 X 上发表了一些经验之谈,他经常这么做: 



他问还有什么,我就快速列了一个清单并回复了他。下面我将具体说下为什么清单上的内容行不通。工程学也是一门社会科学,什么可行/什么不可行要视具体情况而定。生活中有一个教训是,每当你对工程师(或在 X 上发帖)说某样东西行不通时,往往很快就会遇到证明它行不通的挑战。这就是为什么大部分工程管理(和软件架构)都是 “经验法则”和惨痛教训的结合。

 

在我的清单上,每一条都是以“让我们(let's just) ”开头的,因为每当有人说“让我们”的时候,接下来的事情其复杂度十次有九次会超出在场所有人的想象。我说“十次有九次”是根据经验。我在下面举了两个例子,但每一个例子,我可能都经历过五六次。

 

那么,为什么下面这些 “几乎从未奏效 ”呢?

 


让我们使它可插拔。当你非常确定一种实现方式行不通时,你就会想:“我知道了,我们可以让开发人员或其他人沿用相同的架构,然后再插入一个新的实现”。然后,每个调用应用程序接口的人就都会神奇地获得一些改进或新功能,而无需做任何修改。有句老话说,“API 是行为而不是头文件/文档”。几乎没有任何东西的可插拔性可以达到 “正好可用”的程度。现代软件中最具可插拔性的组件可能是设备驱动程序,它们使现代计算机得以问世,但运行起来却非常糟糕。它们要么已被禁用,要么已被十年来现代计算机自己构建的驱动程序所取代。真正可插拔的唯一方法是,在设计主要实现的同时设计出第二个实现。这样至少可以证明它可以插拔一次。

 

让我们添加一个 API。无数的产品/公司都会在取得了一定程度的成功后决定,“我们需要成为一个平台并拥有自己的开发人员”,然后很快就有了 API。提供 API 有多方面的问题。首先,作为一个 API 提供者本身就是一整套的理念和技能,你需要不断地牺牲兼容性和互操作性来换取新特性,因为如果你受限于遗留行为或性能特性,你的 API 就无法演进了。更重要的是,你提供了 API 并不意味着任何人想使用它。几乎每一个新 API 的出现都是因为合作方/产品需要新特性,但又不想把它们放在足够优先的位置(市场太小,太垂直,太特定于领域等)。理论上,API 会被“传播”给该领域的一些合作伙伴。事实证明,这些人并不是坐在那里等着你填补产品缺失的功能。他们也有自己的业务和客户,他们不想购买另一种产品来解决他们的问题,拥有一个 API——成为一个平台——是一件有实际需求的很严肃的事情。构建平台是有魔力的,但很少有平台是通过简单地“提供一些 API“实现的。即使提供了,它为第三方提供经济基础的机会也很渺茫。这个过程困难重重,很难获得回报。

 

让我们再抽象一次。传奇人物 Butler Lampson(曾供职于施乐公司、麻省理工学院、微软公司等)是我共事过的最睿智的计算机科学家之一。他曾经说过:“计算机科学中的所有问题都可以通过另一个间接层来解决”(这是一个众所周知的 “软件工程基本定理”)。这是真理——真正的真理。这一条为什么会失效呢?有两点。首先,工程师往往事先就知道这一点,所以他们过早地在架构中加入了抽象。Windows NT 中充斥着大量从未真正使用过的抽象概念,这主要是因为在真正计划使用它们之前,它们就已经存在了。相比之下,在 Mac OS 的进化过程中,那些看似奇怪的抽象概念在两个版本之后派上了用场,因为当时就是有计划的。其次,事后添加的抽象会变得非常难以维护、难以保证安全、难以进行性能优化。因此,最终会有太多的代码无法使用新增的抽象。这样,维护工作就会变得非常令人头疼。

 

让我们把它变成异步的。在计算机科学前 25 年的大部分时间里,人们都在研究如何让事物异步运行。如果你是 20 世纪 80 年代的研究生,你会在整门课程中讨论哲学家用餐、生产者-消费者或理发师睡觉。对于大多数工程师来说,当今世界已经将这个问题抽象化了,他们只需按照数据层面的规则进行操作即可。但在用户体验层面,人们仍然希望能够完成更多的工作,永远不让人等待。在抽象这个问题上,Web 框架已经做了大量的工作。但是,一旦你跳出框架或数据层,认为你可以自己管理异步,那么你十有八九会做得很好,除了一年后会出现一个你永远无法重现的 Bug。希望那不是个损坏数据的问题,到时候记得我提醒过你。

 

让我们稍后再添加访问控制吧。当我们在研究生院讨论哲学家用筷子的问题时,我们还讨论了在系统中实现访问控制的具体位置。如今的世界比那个从理论上争论访问控制的时代要复杂得多,因为系统在不断地受到攻击。当然,每个人都知道系统从一开始就必须是安全的,但由于上市速度太快,几乎没有一个系统从一开始就充分考虑了访问控制/安全模型。除非从一开始就从客户和对手的角度考虑问题,否则几乎不可能设计出恰当的产品访问控制。无论快慢,你要么会失败,要么需要重写产品,对包括客户在内的每个人来说,这都将是一次可怕的经历。

 

让我们同步数据吧。在这个拥有多种设备、SaaS 应用程序或数据存储的世界里,听到有人在讨论过程中说“我们为什么不直接同步数据呢”是一件超级常见的事。@ROzzie(Ray Ozzie)从 Plato 产品起步,发明了 Lotus Notes 以及 Groove 和 Talko,并领导组建了微软 Azure 。他是客户端/服务器和数据同步领域的先驱。他有一句至理名言:“同步是一个难题”。在计算机科学中,“难题”意味着超级困难,充满挑战,而且只有经历过才能知道。对于一个语义完整的事务型数据存储来说,这个问题就已经够难了,一旦涉及到同步 blob 或非结构化数据,或者更糟,涉及到某种数据转换,那么这个问题很快就会变得非常困难。几乎从来没有人想把解决方案建立在同步数据的基础上。这就是为什么有价值数十亿美元的公司在做同步。

 

让我们实现跨平台吧。在我的计算机生涯中,我一直在进行这样的辩论。每次提到这个问题,就会有人给我看他们写的一些东西,他们认为这些东西在跨平台方面非常 “棒”,或者有人告诉我 Unity 和游戏的事。真正聪明的人认为,他们可以直接说 “Web”。我明白,但我仍然是对的。当你致力于开发跨平台产品时,无论你多么以客户为中心,无论你的初衷有多好,你都是在致力于构建一个操作系统、一个云服务提供程序或一个浏览器。尽管你认为自己是在打造自己的东西,但承诺跨平台实质上就是通过 “增加一个间接层”(见上文 Butler Lampson 的论述)来打造上面这三种东西中的一种。你以为你可以做一个可插拔的平台(见上文)。但在这方面,一个反复出现的现实情况是,它只在两种情况下运行良好。在平台刚刚出现的时候——比如云是计算和简单存储的时候——跨平台是可行的,再就是作为两个玩家之上的抽象层来做这件简单的事情时。当你的应用/产品新而简单时,它是有效的。当你偏离了底层平台,或者当你构建的功能在每个目标上的表现大相径庭时,这两种方法都会失效。对我来说,最 “有名 ”的例子是微软放弃开发 Mac 软件,这正是因为用相同的代码开发出同时适用于 Mac 和 Windows 的 Office 太难了——要知道,微软之所以存在,其根本原因在于它的业务是开发跨平台应用程序。当一个操作系统的 API 只有 100 页文档,而且每个操作系统都是从 CP/M 衍生而来的时候,这种做法是行得通的。我们在 1998 年创建了 Office 代码的分支,从此再也没有回头。每天使用 Mac Office 时,我都会发现,即使到了今天,跨平台是不可能做得很好的。如果想了解我对这一点的更多看法,请参阅:https://medium.learningbyshipping.com/divergent-thoughts-on-cross-platform-updated-68a925a45a83

 

让我们 “转为本地”吧。由于跨平台只在少数情况下有效,所以框架和 API 抽象提供的最常见的解决方案之一就是 “转为本地”。其思想是,平台进化或添加框架/抽象没有或尚未公开的特性,这可能是因为它必须为尚未具备该能力的其他目标构建一个完整的实现。纸面上听起来,这确实不错,但十有八九也行不通。原因很简单。你所使用的框架或 API 会对一些本地平台能力做抽象,并始终保持一些状态,或缓存它所创建的抽象中正在发生的事情。当你调用底层的本地平台时,就会混入框架不知道的数据结构和状态。许多框架都提供了精心设计的机制,以便将数据或状态信息从“转为本地”的代码交换回框架。这可以起到一点作用,但在一个自动内存管理的世界里,这只是一种类似于 malloc/free 的解决方案,我相信如今没有人会支持这种架构。

 

对于上述这些点,我是一直都持强烈的“反对”态度呢?当然不是。是否可以选择这些方法,它们行得通吗?当然可以。在某些情况下,这些方法可能会起作用。但在大多数情况下,你并不需要它们,而且还有更好的方法。始终遵循第一性原则解决问题,不要一味追求容易失败的软件模式。

 

声明:本文为 InfoQ 翻译,未经许可禁止转载。

 

原文链接:https://hardcoresoftware.learningbyshipping.com/p/225-systems-ideas-that-sound-good

2025-03-05 18:164999

评论

发布
暂无评论

GitHub上线一天星标99.9K:阿里内部高逼格SpringCloud实战手册

碌碌无为小码农

Java 架构 面试 程序人生 编程语言

【笔记】学《郭东白的架构课》:12|法则五:如何提升一个架构设计的外部适应性?

术子米德

架构师成长笔记

被字节跳动气炸了!

Jackpop

【笔记】学《郭东白的架构课》:11|法则五:架构师为什么要关注技术体系的外部适应性?

术子米德

架构师成长笔记

最好用的 7 款 Vue admin 后台管理系统测评

蒋川

Vue Vue 3 vue admin

ReactNative进阶(三十二):前端构建工具--Yeoman

No Silver Bullet

React Native 1月月更 Yeoman

“字节”再次起跳!内部651页剖析HotSpot 源码手册,GitHub开源

碌碌无为小码农

Java 面试 程序人生 编程语言 经验分享

(1-20/20) 用技术实现更快、更好的销售

mtfelix

300天创作 2022Y300P

【笔记】学《郭东白的架构课》:08|架构师如何在一定时间内最大化自己的增量价值?

术子米德

架构师成长笔记

【笔记】学《郭东白的架构课》:04|法则二:架构师为什么要学习马斯洛的需求理论?

术子米德

架构师成长笔记

【笔记】学《郭东白的架构课》:02|法则一:为什么有些架构活动会没有正确的目标?

术子米德

架构师成长笔记

后悔没有再点遇到!字节技术官DDD(领域驱动设计)手册,拆解业务代码首选

碌碌无为小码农

Java 架构 程序人生 编程语言 经验分享

24 Prometheus之微服务监控概述

穿过生命散发芬芳

Prometheus 1月月更

如何用 Serverless 让 SaaS 获得更灵活的租户隔离、更优的资源开销

碌碌无为小码农

Java 架构 面试 经验分享 编程语言、

表妹和我纠结,线上系统因为一个ThreadLocal直接内存飙升

碌碌无为小码农

Java 架构 程序人生 编程语言 经验分享

项目管理的十大领域

石云升

项目管理 项目经理 1月月更

【笔记】学《郭东白的架构课》:01|模块导学:是什么在影响架构活动的成败?

术子米德

架构师成长笔记

深入理解 Go 语言的 map 实现原理

宇宙之一粟

Go map Go 语言 1月月更

【笔记】学《郭东白的架构课》:09|法则四:为什么要顺应技术的生命周期?

术子米德

架构师成长笔记

【笔记】学《郭东白的架构课》:06|法则二:拼多多是如何通过洞察用户人性而脱颖而出的?

术子米德

架构师成长笔记

【笔记】学《郭东白的架构课》:05|法则二:研发人员的人性需求是如何影响架构成败的?

术子米德

架构师成长笔记

【笔记】学《郭东白的架构课》:07|法则三:架构师如何找到自己的商业模式?

术子米德

架构师成长笔记

阿里最新丰碑:国内第一本凤凰架构,全面构建可靠大型分布式系统

碌碌无为小码农

Java 架构 程序人生 编程语言 经验分享

Redis:我是如何与客户端进行通信的

碌碌无为小码农

Java 面试 程序人生 编程语言 经验分享

php中序列化与反序列化

喀拉峻

网络安全

参数校验Spring的@Valid注解用法详解

JavaEdge

1月月更

【笔记】学《郭东白的架构课》:03|法则一:如何找到唯一正确的架构目标?

术子米德

架构师成长笔记

顶级好用的 5 款 Vue table 表格组件测评与推荐

蒋川

Vue vue table

混沌工程之 ChaosToolkit K8S 使用之删除 POD 实验

zuozewei

k8s 混沌工程 1月月更

【笔记】学《郭东白的架构课》:13|法则六:如何鉴别文化环境是否有利于架构师的生存?

术子米德

架构师成长笔记

【笔记】学《郭东白的架构课》:10|架构设计中怎么判断和利用技术趋势?

术子米德

架构师成长笔记

听起来不错但几乎行不通的系统理念_技术选型_Steven Sinofsky_InfoQ精选文章