cover of episode DOP 282: How To Measure Software Complexity

DOP 282: How To Measure Software Complexity

2024/9/25
logo of podcast DevOps Paradox

DevOps Paradox

People
D
Darren Pope
V
Victor Farsen
Topics
Darren 和 Victor 讨论了 John Gall 关于复杂系统的观点,即复杂的系统总是从简单的系统演化而来,而从头设计复杂的系统通常会失败。他们以 Kubernetes 为例,阐述了复杂系统是如何从简单的系统演化而来的,以及在演化过程中遇到的挑战。他们还讨论了 Mahesh Balakrishnan 提出的软件复杂度的三个定律:1. 设计良好的系统会随着时间的推移而退化;2. 复杂性是由于抽象的泄漏造成的;3. 软件复杂性没有上限。他们分析了每个定律背后的原因,并探讨了如何应对软件复杂性增加的问题,例如如何处理需求变更、如何权衡维护现有系统的成本和替换系统的成本,以及如何避免系统设计过度复杂。他们认为,需要定期评估系统的成本效益,并决定是否需要替换系统,同时经验丰富的工程师应该指导初级工程师,避免软件变得过于复杂。 Darren 和 Victor 详细分析了软件复杂度增加的原因,并提出了相应的应对策略。他们认为,软件复杂性的增加是不可避免的,因为需求会随着时间的推移而发生变化。为了应对这种变化,我们需要定期评估系统的成本效益,并决定是否需要替换系统。同时,我们需要在设计系统时注意避免过度复杂,并采用良好的抽象机制来隐藏实现细节。此外,经验丰富的工程师应该指导初级工程师,避免软件变得过于复杂。他们还以 Kubernetes 和 Kafka 为例,说明了如何通过移除冗余代码和改进系统设计来降低软件复杂性。

Deep Dive

Chapters
This chapter explores John Gall's General Systematics and its application to software systems, using Kubernetes as a prime example. It examines how complex systems evolve from simpler ones and the challenges of designing complex systems from scratch.
  • Complex systems that work evolve from simpler systems.
  • Designing complex systems from scratch rarely works.
  • Kubernetes' evolution from simpler systems like Docker illustrates this principle.

Shownotes Transcript

复杂软件的需求摆在那里,所以我们是否想要它其实并不重要。我们在某种程度上是被迫接受它的,对吧?这里是DevOps Paradox,第282集。如何衡量软件复杂性。欢迎收听DevOps Paradox。这是一个关于各种随机话题的播客,在其中,我们Darren和Victor假装自己知道自己在说什么。

大多数时候,我们通过在任何可以放的地方都加上“DevOps”这个词来掩盖我们的无知,并将其与Kubernetes、无服务器、CICD、团队生产力、快乐岛屿以及其他让我们听起来像知道自己在做什么的花哨表达的随机流行语混合在一起。

偶尔,我们会邀请一些确实懂行的人做客,但我们不会经常这样做,因为他们可能会让我们显得无能。真相就在那里,我们不可能找到它。是的,是Darren在读这段文字,并且因为Victor让我读而感到尴尬。以下是您的主持人,Darren Pope和Victor Farsen。Victor,你听说过John Gall吗?没有,我没有。

不知道。我听说过John Father,但没听说过Gollum。不是Gollum。好吧,John Gall,看看他的维基百科页面,他于2014年去世。他出生于1925年。所以他比我们俩都早一点,但他一直活到十年前。他以其1975年的著作《一般系统论》而闻名,这是一篇关于系统如何运作,尤其如何失效的论文。我们对系统失效一无所知,对吧?

不,我的系统从未失效。我现在还计划开始用Rust编写代码,因为有人告诉我Rust没有bug。没有bug?没有bug。这是一种无bug的语言。是的,当你用Rust编写代码时,你的代码就是无bug的。这就是承诺。有趣。我认为这将是另一集的内容。让我们看看结果如何。但高斯定律指出,我将读一下,一个有效的复杂系统……

好的,一个有效的复杂系统。除了这五个词,我们还需要更多吗?因为要得到一个有效的复杂系统,这是定义,它总是被发现是从一个有效的简单系统演变而来的。继续,从头开始设计的复杂系统永远不会有效,也无法通过修补使其有效。你必须从一个有效的简单系统开始。

这听起来像是你读到后首先想到的是城市,对吧?城市可能属于这一类。这是一个有效的复杂系统。它最初的设计范围并非如此之大。它只是在那里发展起来的。这有点像大学校园里的走道。

是的,那是设计的。好吧,除了大学生不会呆在人行道上。他们只是直接穿过人行道去寻找新的走道。这就是我的意思,你设计了整个大型人行道网格,但没有人使用。而不是先看看交通模式是什么,然后再铺设混凝土。没错。是的。

这场谈话不可避免地会导致瀑布模型。我认为我们已经到了,但让我们把它变成更具体的东西。所以让我们将我们的文本基础应用于前五个词,“一个有效的复杂系统”。我们现在可以定义一个复杂系统吗,只是为了定义?假设它是Kubernetes,对吧?这足够公平吗?是的,这是一个复杂系统,相当复杂的系统。好的,对。

总是被发现是从一个有效的简单系统演变而来的。Kubernetes会从哪个简单的系统演变而来,或者可能是多个系统?在Kubernetes的第一个迭代中,只有无状态应用程序和Docker,没有更多。基本上,它是我们如何运行容器和一些网络,非常原始的一个。所以它与今天的Kubernetes非常非常不同。

特别是如果你把Kubernetes考虑进去,不仅仅是Kubernetes本身,还有最终结果。最终结果几乎从来都不是只有核心Kubernetes。所以你的意思是说,那个发展到最初Kubernetes的简单系统,对吧?让我们把那部分做好。相对来说,很简单。是的。是Docker。

一种跨节点集群调度Docker容器的方法。我现在正在简化它,但为了论证起见,让我们假设这就是它。那么我们是如何达到那一点的呢?所以让我们,再说一次,我们停留在Kubernetes的V1版本,不是技术上的V1版本,而是,你知道的,Kubernetes的初始版本。Docker导致了这一点,但是什么导致了Docker?

导致Docker的是一家公司,我现在忘了它的名字了,Solomon在创立Docker之前的那家公司,试图解决其提供的SaaS服务的内部问题。好的。但是什么技术让我们达到了这一点?技术。技术就像古代的。我们说的是命名空间之类的东西,这些东西没有人知道如何使用,但在Linux中却永远存在。对。

这就是你指的吗?这就是LXC和其他类型的容器化的地方,尽管它不是我们2024年所知的容器。顺便说一句,容器这个术语直到今天在Linux中仍然不存在。我只是把它称为容器。它不是容器。是的,我知道。Cgroups,其他一切,对吧?因为Docker使我们能够轻松地管理Cgroups。没错。它让大众,让每个人都能使用它。

当我提到每个人时,字面上的每个人都能在短时间内学会它。现在,我们不会从Cgroups之前继续讨论,因为那确实早于……好吧,它并不早于我,但它是我没有深入研究的部分。是的,我们不会走大型机和Linux之前的任何东西的路线,对吧?Unix。让我们谈谈Unix。现在让我们想象一个世界。

Kubernetes只是试图由,比如说谷歌创建的,因为谷歌为Magic Hand Wave创建了Kubernetes。他们只是说,这是Kubernetes。如果没有之前的东西,他们能做到吗?回到Gall定律,一个复杂的系统不可能仅仅存在。它必须首先是简单的系统,然后才能变得复杂。所以,公平地说,他们可以做到,无论是否成功,都可以不用Docker,但是……

但他们不可能在没有Cgroups等等的情况下做到这一点。或者至少不是以我们今天拥有的那种形式,对吧?很难玩“本来可以”、“应该可以”之类的游戏,对吧?所以是的,他们不可能在没有先前工作的情况下做到这一点,当然。这几乎适用于所有事情,对吧?

另一方面,某种允许我们大规模利用计算能力的系统迟早会出现,这几乎是不可避免的。但是是的,如果它不存在,他们需要想出专有的东西。这仍然基于先前的工作。我们已经谈论了高斯定律,但我想要关注一篇博客文章,请原谅我,我会尽力而为,Mahesh Balakrishnan。Mahesh,如果我甚至接近……

这是一个奇迹。如果我把它弄糟了,我道歉。但他最近在2024年5月发表了一篇题为《软件复杂性的三大定律》的文章,副标题为“为什么软件工程师总是脾气暴躁”。所以我们真的想要复杂的软件吗,Victor?我们想要吗?复杂软件的需求摆在那里。所以我们是否想要它其实并不重要。

我们在某种程度上是被迫接受它的,对吧?因为如果你回到20年前,那么制作能够维持任意数量用户的东西将会非常不同,因为任意数量的用户将是,如果你曾经获得1000个并发用户,那将是令人难以置信的。我们永远不会达到这个数字,对吧?而今天,这就是业余爱好网站。这有点像,好吧,这太荒谬了。当然,有点像……

在你的笔记本电脑上运行的要求,对吧?需求正在因为我们周围的力量而发生变化。随着这些需求的变化,复杂性也在增加。现在,问题是,我们看到了多少复杂性,我们没有看到多少复杂性,对吧?因为你可以说,嘿,我自己设置并运行Kubernetes,对吧?

这比IU的GKE复杂得多,而IU的GKE比IU的GKE Autopilot复杂得多,IU的GKE Autopilot比IU的Google Cloud Run复杂得多。现在,实际上,复杂性没有区别。我的意思是,有一些区别,对吧?但为了论证起见,我刚才提到的所有这些系统都同样复杂,但我们作为用户并不一定能看到这种复杂性。但它就在那里。

所以让我们来看看Mahesh写的三大定律。软件复杂性的第一定律。你可能会笑,你可能不会笑。我们拭目以待。一个设计良好的系统会随着时间的推移而退化为一个设计糟糕的系统。这是对Kubernetes的预兆吗?是的。我的意思是,我不确定这是对Kubernetes的预兆,但他所说的不可避免地是正确的,原因有两个。首先,因为我们随着时间的推移会把它搞砸,对吧?

你有一个设计精良的系统,然后,你知道的,参与的人越多,积累的混乱就越多。这只是我们人类的运作方式,对吧?我还没有看到很多公司真正花大量时间清理烂摊子的例子。这种情况很少发生,因为,嘿,它正在工作。另一个原因是我之前提到的,

需求将会改变。当过一段时间后通过新需求的棱镜来看待这个系统时,我们会说,嘿,这太傻了。这没有意义。设计得这么糟糕。但我现在不包括人们随着时间的推移而把它搞砸的情况,对吧?根据设计时的需求,它不一定是设计糟糕的,但随着新需求的引入,

这变成了一个糟糕的设计。你知道的,如果我需要有一百万个并发用户,这太可怕了。我永远不会设计这样的东西。但在设计它的时候,这不是一个要求。他们现在让每个人都感到困惑,对吧?我不确定我刚才说了什么。好吧,你所说的意思是,我们有最初的需求。一切都满足了。然后我们开始对该系统进行变更请求。

我们失去了对该初始系统应该做什么的了解。我们开始把它变成弗兰肯斯坦的怪物。宾果。这就是重点。当我提到改变需求时,我指的不仅仅是,嘿,管理层每天都在随意改变事情。但仅仅是我们需要的每個月、每年都不同,对吧?需求不同。你不能比较,我不知道,比如说今天的Netflix和10年前的Netflix,对吧?他们的系统变了。

今天与那时相比,需求非常非常不同。所以如果你相信你会编写一个系统并启动它,并且再也不会碰它了,这是可能的。但我认为在这个十年里,随着时间的推移,这不太可能。需要明确的是,这几乎适用于任何事物。它不仅与软件有关,对吧?

我可以对桥梁说同样的话,对吧?如果你拿一座100年前设计的桥梁,它的设计理念是,嘿,你平均每五分钟可能只有一辆车。而现在你每五秒钟就有一辆车,对吧?桥上的负载一定不同。它不一定会立即倒塌,对吧?

但它会比最初想象的更快地被浪费掉。我们如何处理系统上出现的这些变更请求?如果变更请求会过多地改变系统,我们应该拒绝吗?当然,你可以拒绝一些变更请求。但大多数情况下,你不能改变,因为我们所做的一切都是为了我们工作的企业的利益,对吧?

所以我现在不包括来自那些不知道自己在做什么的愚蠢经理的愚蠢请求。但总的来说,你不能改变请求。“嘿,我们有更多客户。这是一个新的请求。你需要处理更高的负载。”我不能拒绝。我不能说,“嘿,你知道吗?我们可以破产。我为什么要在乎?”我的意思是,你可以尝试这样做,但这并不是世界运作的方式。你有了新的需求,你需要接受它们。

实际上,这里有两个重要的问题。你会做必要的事情来延长某事物的寿命吗?这同样适用于软件工程或其他任何事情,对吧?所以我可以有一个这样设计的系统,它可以存活五年。我们事先不知道这一点,但历史稍后会告诉我们这是五年,对吧?现在,我可以对该系统做一些事情,对其进行现代化改造,以便它可以存活十年。

只要我们理解它,这就是需要进行的投资。第二件事是认识到,维护某事物,维护某事物的价格以及应用新需求的成本何时会高于完全替换它。我可以对之前的桥梁例子重复同样的故事,对吧?你正在维护桥梁,但随后到了某个时间点,你通过

粉刷它,增加结构,等等来延长它的寿命。但随后到了某个时间点,你会说,你知道吗?这座桥现在太贵了,我要建一座新的。对我来说,这将更便宜、更高效。我是一个简单的人。我将用汽车的例子。在某个时候,一辆汽车的维护成本太高了。没错。然后是时候再花5000美元去买一辆便宜的车了

那将是丰田或雷克萨斯或日产或英菲尼迪,顺便说一句。我喜欢我的欧洲车,但它们的维护成本太高了。其他的,相比之下,并没有那么高,至少是这样。所以在那个时候,一旦你维护了那辆车,你现在每月花费600美元,但这600美元每月可以更好地用于一辆新车。是的。而那辆新车,

可能比你买的老车的原价更便宜,你仍然会获得更好的价值。因为事情会改变。我不知道。现在是电动的,现在有自动刹车,或者其他什么。我不怎么开车,但无论有什么新东西,对吧?你会得到它。而且很可能,是的,你买的可能不是5000美元,但如果是一辆新车,今天是15000美元的车,而那辆车的

维护成本将低于你旧车的维护成本,并且会给你带来比你旧车更多的益处,你可能花了5万美元。技术类比,一旦开始花费更多,就该摆脱它了。是的。但现在我们遇到了问题。如果我继续用汽车做类比,你说每月维护费用是600美元,对吧?是的。

假设一个季度是1800美元,对吧?你说,好吧,但你知道吗?如果我投资1800美元,这仍然比5000美元便宜得多。而且我真的很——我们只需要度过这个季度。我们现在只需要挺过去,对吧?我们稍后再考虑新车。

然后它从一个季度流向另一个季度,再流向另一个季度。你的维护成本将持续增加。但短期生存总是比长期投资更便宜。而从长远来看,更便宜的东西更贵,需要明确这一点。但现在,我需要投入到修理那辆旧车上的现金比买一辆新车要少。

因为你试图用现金或至少接近现金的方式购买新车。是的,让我们这么说吧,对吧?这就是我需要做的投资。所以然后你说,那么我应该怎么做?如果我关注我的幸福,我的未来,那么我应该做什么非常清楚。我应该找到这5000美元、15000美元,无论价格是多少,然后买一辆车。

这很清楚。但如果我考虑的是我的短期利益,如果我只关注,如果这个季度之后什么都不存在,世界末日就要来了。那么修理汽车更有意义。我觉得很多公司更关注后者。别给我那五年,无论什么。有一个季度,我们需要在这个季度取得成功。

这就是为什么我们有维护成本极高的旧系统。每个人或大多数人都知道,用别的东西替换它很愚蠢。它已经超过了我可以改进它的门槛,对吧?不,这个系统没有任何改进。它花费了大量的金钱。我们都知道我们应该使用其他东西。然而,没有人愿意这样做。

让我们继续第二个要点。软件复杂性的第二定律是复杂性是一个充满泄漏抽象的护城河。让我读第一句话。设计一个好的抽象是一个微妙的平衡,它在为应用程序提供效用的同时隐藏了有关实现的细节。没错,对吧?我们需要这样做。问题是,我正试图在这里快速找到它。我只是跳到段落的底部。

根据这条定律,大多数工程师都会在设计糟糕的系统上工作,因为大多数成功的流行系统都是设计糟糕的系统。我不同意设计糟糕的系统这最后部分。你知道的,事后看来,是的。哦,如果我有经验,你知道的,如果我们有今天的Kubernetes经验并将其应用于10年前或20年前我们正在做的事情,哦,很明显20年前是一个

这是一个灾难性的系统,但你不能应用这种逻辑,因为我们那时不知道我们现在所知道的。我必须假设至少有一些公司或项目是基于之前的经验来构建他们的API、系统和抽象的。我不是说每个人都在这样做,但我们今天设计事物的能力比过去要强。

如果你知道这是一个设计糟糕的系统,那么请便。让我们谈谈那个人的名字是什么,它有什么不好,我们可以改进它,对吧?无论我们谈论的是哪个系统。他在这里给出的例子是,事实上,我只是要读完整个段落。当系统相互竞争市场份额时,精细性就会消失,设计者通常会一次性地为应用程序提供所有内容。它回到了第一点,对吧?我们不断地往里面倾倒东西。

这具有双重作用:通过吸引开发人员来增加市场份额,同时又使竞争系统难以在幕后替换不同的实现。他给出的例子是Zookeeper和Kafka的工作方式。就像一旦你嫁给了那个系统,你就不能轻易地用其他东西替换Zookeeper,而不会付出很多努力。是的,这是绝对正确的。但这又回到了汽车的类比,对吧?

Kafka中有很多决定在今天看来毫无意义。这并不是说它们在Kafka设计之初没有意义。我的意思是,我记得那些日子。如果你当时问我,我会用什么?Zookeeper规则。当然我会使用Zookeeper。现在如果你告诉我Zookeeper,我会走开。我不想再参与那场谈话了,对吧?现在,问题是……

那个人对那个人来说是可以的,所以你是说你现在正在使用的那个Kafka系统相当于旧车,你也不愿意买一辆新的,当然会花钱,但总成本可能不会是,我的意思是总成本可能在一两年内就能收回,这并不是很长的时间,我不是说每个人都应该替换Kafka,只是为了100%清楚,对吧

我只是说你需要一直进行成本效益评估。而不仅仅是在预算编制时进行一次。也许这应该是一项季度性工作。好的,我们知道我们有很多不应该拥有的东西。本季度的选择是什么?做一个选择。

每家公司都有很多候选者。不仅仅是一个。有很多。所以选择本季度应该淘汰还是不淘汰,应该被替换。好吧,我一直在很多公司看到这种情况,他们试图在管理现金方面更有效率。这意味着摆脱订阅。哎呀,我甚至可以把它应用到我自己身上。我家里可以摆脱哪些订阅?Netflix保留。其他的会消失吗?因为我需要它们吗?

是的,没错。至少在我家,它们随着时间的推移而积累。实际上,可能是在一个月或两个月前,我看了看,好吧,伙计,这没有意义。我的意思是,我可以负担得起,但拥有HBO、Prime、Disney毫无意义。我有Netflix,我有Apple,可能还有其他七个我两年都没用过的。是的,我不得不摆脱其中一些。现在,

这比我们正在讨论的要容易一些,因为你知道的,摆脱你没有使用或没有提供太多价值的订阅是一项简单的练习。删除Kafka不仅仅是删除Kafka,而是要找到一个替代品并更改很多东西。这花费很多,但问题是,应该这样做吗?软件复杂性的第三条也是最后一条定律是,软件复杂性没有根本的上限。

我将读第一句话。我认为我不需要阅读这句话以外的任何内容。在由大量人员长期构建的现实世界系统中,复杂性仅受人类创造力的限制。只要假设当我们谈论复杂性时,我们谈论的是随着时间推移而积累的东西,对吧?不是设计复杂性,对吧?大多数情况下,它不是设计复杂性。它只是随着时间的推移变得复杂。

任何事物都是这样运作的。你现在正在买新房子,对吧?当你搬进那所房子时,伙计,它将比半年后不那么复杂。你放了所有的家具,忘记打扫房间等等,对吧?这是不可避免的。这就是生活的方式。我认为你说的是反过来的。你说它会随着时间的推移变得不那么复杂。我说的是不那么复杂吗?不,我想说的是更复杂。是的,没错。是的,它变得更复杂了。

是的。这是一个设计糟糕的房子吗?不。这是一个复杂的房子吗?不。搬进去和居住的行为会增加它的复杂性。保证的。不过,我喜欢他这句话的一部分是,复杂性仅受人类创造力的限制。我认为他是在委婉地说。我在考虑新员工来维护一个他们从未维护过的应用程序。也许这是他们维护的第一个应用程序。

他们是来维护这个应用程序的第10、15、20批人,人们会随着时间的推移变得更有创造力。我的意思是,我们将回到2004年。你在2004年主要写什么?我为20年前,2000年我所做的事情感到羞愧。好的。但有一些应用程序仍然在20年前运行。是的,当然。在2004年参与开发它的那些人与在2024年参与开发它的人相同的可能性有多大?

很少,对吧?我们最终会退休。我们搬到其他公司等等。但同样,这不应该是一个问题,对吧?至少从我的角度来看,我们在那个例子中看到的问题是,这意味着你的资深人员没有做好他们的工作。我不能责怪初级人员过于热情并想要东西。我的意思是,你想要这样的人。

你想要那些对他们正在从事的工作感到兴奋的人,他们充满动力,他们去那里并把它搞砸了。但随后是你,有点。你有数十年经验,有点。就像,指导人们是你的工作,对吧?而且,你知道的,这与回到桥梁的例子是一样的。你不会

让随机的人去做随机的事情来建造一座桥梁。你会有某个人来监督它,而那个人可能更资深,并且知道他或她正在做什么以及什么不是。一个很好的例子是,我不知道确切的日期,但不久之前,Kubernetes中有一个单一的拉取请求删除了大约一百万行代码。你明白一百万行是什么吗?这个数字本身就比许多人的

系统要大,也许不是系统,而是应用程序或平台或其他什么。Kubernetes社区删除了大约一百万行代码。这些代码与云提供商有关,你知道的,AWS之类的。所有这些都被删除了,复杂性也降低了。这是可能的,但这项工作没有人愿意做。我的意思是,不是没有人,而是没有多少人。

我希望我当时是那个人的批准者。LGTM,一百万行,消失了。我会在不查看它的情况下批准它。你甚至不会那样做。你只会说,是的,继续吧。继续吧。我相信你。一旦你有了这种级别的删除,你最好相信提交那个PR的人。

已经很久了,所以这个例子不是一夜之间发生的。这些相同的提供商正在研究如何在Kubernetes之外将相同的功能服务作为附加组件提供等等。所以这是一个过程,但它最终……这是一个需要时间、努力、金钱以及一切的过程,它最终导致了大约一百万行代码被删除。这是一项无意识的投资。

#282:在不断发展的技术世界中,软件开发和系统设计的复杂性持续挑战着工程师和开发人员。约翰·盖尔等过去思想家的见解,结合现代实践,为我们理解和应对这些复杂性提供了一个引人入胜的视角。在本集中,Darin 和 Viktor 讨论了 Mahesh Balakrishnan 的一篇博文,标题为“软件复杂性的三大定律(或:为什么软件工程师总是脾气暴躁的)”。软件复杂性的三大定律(或:为什么软件工程师总是脾气暴躁的) https://maheshba.bitbucket.io/blog/2024/05/08/2024-ThreeLaws.html   YouTube 频道:https://youtube.com/devopsparadox   在 Apple Podcasts 上评价播客:https://www.devopsparadox.com/review-podcast/   Slack:https://www.devopsparadox.com/slack/   联系我们:https://www.devopsparadox.com/contact/</context> <raw_text>0 我对这一点印象非常深刻。诸如此类。那么,你如何衡量你的软件复杂性呢?前往 Slack 工作区,查找第 282 集,并在那里留下你的评论。我们希望本集对您有所帮助。如果您想讨论或提问,请与我们联系。我们的联系信息和 Slack 工作区的链接位于 devopsparadox.com/contact。

如果您通过 Apple Podcasts 订阅,请务必在那里留下您的评价。这有助于其他人发现这个播客。立即注册 DevOpsParadox.com,以便在我们发布最新剧集时收到电子邮件。感谢收听 DevOps Paradox。