【外评】软件复杂性的三大法则(或:为什么软件工程师总是脾气暴躁)

我认为,大多数软件工程师(尤其是那些从事基础架构系统工作的工程师)注定要沉溺于不必要的复杂性中,这是因为有三个基本规律。

软件复杂性第一定律:随着时间的推移,设计良好的系统会退化为设计糟糕的系统。

我们先从一个见仁见智的定义开始:一个设计良好的系统是一个容易随着时间推移而改变的系统;一个设计糟糕的系统是一个难以改变的系统。假设 X 系统设计良好。有人出现了,并把它改变成了另一个系统 X’–顾名思义,改变得又快又容易。现在,X’要么继续是精心设计的;在这种情况下,它可以被快速、轻松地修改为另一个系统 X”;要么它将进入一种糟糕的设计状态,从而难以修改。例如,考虑一个设计良好的数据库,它在一个干净的存储引擎 API 后面使用 RocksDB;然后有人来了,给它添加了一个 getLevelSize 调用。由此可见,设计良好的系统是一种不稳定的、短暂的状态;而设计糟糕的系统则是一种稳定的、持久的状态。因此,野生系统的组合会不断向设计不良的方向退化。在野外,代码的二阶导数总是负的:代码变化的速度会随着时间的推移而降低。根据这一规律,大多数工程师都会在设计糟糕的系统上工作,因为随着时间的推移,大多数系统都会变成设计糟糕的系统。

软件复杂性第二定律:复杂性是一道护城河(由泄漏的抽象填满)。

设计一个好的抽象概念是一种微妙的舞蹈,既要为应用程序提供实用性,又要隐藏实现的细节。当系统为争夺市场份额而相互竞争时,精巧的设计就无从谈起,设计者往往会给应用程序提供它想要的一切。这样做有双重效果,一方面可以通过吸引应用程序开发人员来增加市场份额,另一方面又可以使相互竞争的系统难以在引擎盖下替代不同的实现方式。地球上一些最成功的系统的应用程序接口(API)几乎不可能以任何其他方式实现(ZooKeeper 的强于线性的一致性和基于 TCP/IP 的短暂节点语义;Kafka 的empotent produce 语义)。根据这一定律,大多数工程师都会在设计糟糕的系统上工作,因为大多数成功/流行的系统都是设计糟糕的系统。

软件复杂性第三定律:软件复杂性没有基本上限。

在现实世界中,系统是由一大群人长期构建而成的,其复杂性仅受到人类创造力的限制。一个系统的形态是由数十名开发人员的能力、理念和特质决定的,每个人都在一套复杂的真实和感知的激励机制下工作。例如,为什么这个复制数据库使用自己的八卦层来检测故障,而不是依赖 Kubernetes?也许 TL Alice 和开发人员 Bob 一致认为,基于八卦的故障检测才是王道;但 Bob 写完代码后,Alice 意识到,在容器化环境中这是错误的方法;但经理 Charlie 已经为 Bob 写好了宣传文档,Alice 不想冒阻止公关的政治风险。又或者,这个系统最初是鲍勃为非容器化环境设计的,在这种环境下,八卦实际上是个不错的选择。又或者,鲍勃的博士论文研究的是基于流言的协议。也许爱丽丝后来为了避免与鲍勃和查理对立,把成员资格和领导者选举分成了不同的层,这就是为什么你的系统现在有两层,并且有有趣的交互。现有的每个系统都是由几十个你可能根本不认识的人对你发起的 DoS 攻击;是一个由滴答作响的复杂性定时炸弹组成的诱杀宫殿,这些定时炸弹在你参与之前就已经埋好了。根据这一规律,在设计糟糕的系统上工作的工程师将尤其痛苦,因为设计糟糕的系统具有无限的复杂性。

对于这种状况,我们能做些什么呢?在我的职业生涯中,我采取了一种特殊的方法,即从零开始建立新系统(在它们屈服于三大法则之前),但这比听起来要难得多–关于这一点,我们将在另一篇文章中详述。

本文文字及图片出自 Three Laws of Software Complexity (or: why software engineers are always grumpy)

你也许感兴趣的:

共有 138 条讨论

  1. > 在我的职业生涯中,我采取了一种基于从头开始构建新系统的特殊方法(在它们屈服于三大法则之前),但这比听起来要难得多

    在我所见过的大多数实际案例中,这似乎都是一个不可行的解决方案。尤其是当你的系统已经有 15 年的历史,并投入了数千人年的心血时。

    现在,”不要让它发展到那个地步 “是我经常看到的自以为是的 IT 呆子的回答,但这似乎是一种新创公司的思维,不适用于 90% 以上的软件开发。我们有一个现有的系统。它需要维护和扩展,因为这是我们的核心业务。我们不能说 “哦,它变得太复杂了。让我们花几年时间开发一个新产品吧,这样我们就可以冻结多年的功能”。

    在我看来,唯一可行的方法就是缓慢、渐进地将软件分割成类似领域的孤岛。在这一点上,当筒仓变得过于复杂/繁琐/庞大时,你可以开始考虑重写它们。然而,在这一点上,你已经在进行复杂、冗长且普遍存在风险的重构了,说它是从头开始写显然是不对的。

    1. 我同意你的观点。我们很多开发人员往往会忘记,客户只看功能。整洁的代码、单元测试、严谨的流程……它们的价值仅在于它们实现功能的速度。

      复杂性和技术债务是我们的问题,客户不会关心我们如何解决它,甚至不会关心我们是否解决了它。只要我们还在纠结于一个老旧的问题,这对我们来说就是好事:这意味着产品还在销售,维护它的努力还有意义!

      没有复杂性和技术债务的销售会更好,我想我们都同意这一点,但这是我从未亲眼目睹过的成功。

      1. 虽然很难一概而论,但我基本上同意这种观点。

        我曾多次建议客户不要使用 WordPress 这样的软件,结果却不得不使用 Divi 这样的页面生成器,这让我很受伤。

        归根结底,大多数人并不关心臃肿/传统代码、严格的测试模式、使用现代技术栈或类型安全代码库等问题。他们只是想让你 “让事情变得更糟”。

        我可以提供建议,直到我的脸都青了,但现实情况是,我的客户中至少有 90% 都想要最快、最便宜的方案,这导致我的技术能力几乎连表面都达不到。

        我想,从商业角度来看,最主要的是我得到了报酬,但同时,我的感觉也很糟糕。

        1. > 我可以提供建议,但现实是,我的客户中至少有 90% 都希望获得最快捷、最便宜的选择,这导致我的技术能力几乎不值一提。

          我认为问题的部分原因在于 “柠檬市场”:客户分不清短期便宜、长期昂贵和短期昂贵、长期便宜之间的区别,因此不相信昂贵的方案从长远来看确实能为他们省钱,而不是短期昂贵、长期昂贵、甚至更昂贵的方案。

          考虑重新粉刷房屋的报价。一个建筑商报价 2000 英镑,另一个报价 4000 英镑。现在,第一个建筑商是否给了你一个合理的价格,而第二个建筑商是否因为你不知道什么是合理的价格而向你收取额外的费用?还是第一个建筑商是个 “牛仔”,会把工作做得很糟糕,让你后悔,而第二个建筑商是个工匠,会把工作做得很出色,让你满意?也许你知道得够多了,但我在装修房子时可不是这样。

          再考虑到客户并不确定他们在三年内是否还需要一个网站;也许在那之前,这个新项目就已经夭折了。到那时,我们会加倍庆幸他们没有投资一个相当于 10 年质保的网站。

        2. > 我可以提供建议,但现实是,我的客户中至少有 90% 都希望获得最快捷、最便宜的选择,这导致我的技术能力几乎不值一提。

          直到有更多的客户因为廉价的选择而付出昂贵的维护费用,他们才会在意。许多客户(整个行业的客户,我不了解你们的客户,他们可能与普通客户有很大不同)不会被烧伤,因为大多数问题都不会那么复杂。然而,大多数客户都会遇到很多小问题,这些问题很快就能解决,这意味着大多数开发人员都会把时间花在少数问题复杂的客户身上。

        3. 我同意你的观点,但我会选择最便宜的前期方案。从长远来看,它们的成本往往更高。参见特里-普拉切特的靴子理论:https://en.m.wikipedia.org/wiki/Boots_theory

          1. 这个理论的问题在于,50 美元的靴子制造商会在创始人想退休时将产品卖给私募股权投资公司,然后私募股权投资公司会将价格抬高到 75 美元,并将质量降到 10 美元纸板靴子以下……这样你就得花更多的钱去买湿脚的靴子。

      2. 客户非常在意缺陷,而我亲眼目睹缺陷因复杂性和技术债务而急剧增加。

      3. 被遗忘的是你看不到的东西。

        持续性重构的代价是显而易见的(昂贵的开发人员时间),而回报却是巨大的,但却非常分散,很难解释,而且经常被归咎于其他原因–有时甚至是开发人员的原因。

        这样的事情不止这一件。很多公司都在成本中心上吝啬,结果导致严重失败,有时甚至没有意识到在相关成本中心上吝啬是失败的原因。

        1. 只有当决策者关心长期后果和价值时,重构才有意义。很难明智地做出此类决策的一个原因是,没有人知道一款软件在生产中能使用多久。

          如果我能确定一款软件将在生产中使用 50 年,那么我就能更好地进行重构。同样,如果我确定一款软件将在一年内被完全取代,我也不会费心去重构。

          在我的职业生涯中,我从未准确预测过软件在生产中的寿命。我曾让一些废弃的代码存活了数年,而那些我以为会永远存活下去的东西却几乎在一瞬间就被替换掉了。

          1. 重构不仅能让你在 50 年内受益,还能在数周内受益。

            我同意,丢弃代码是一种浪费,丢弃代码很常见,最终成为丢弃代码的代码也很难预测,但我认为,了解代码被丢弃的可能性并非不可能,而且这种了解可以用来调整重构预算。

            1. > 几周内就能受益。

              但是,如果我花了一个月的时间重构,却只在项目废弃前获得了一周的收益,那就不值得了。如果我花了一个月的时间,却在项目被放弃前只得到了一个月的收益,那也是不值得的(我本可以从事其他工作)。除非我们知道如何量化能收回多少收益,以及收回收益需要多长时间,否则我们就无法对是否值得进行适当的经济分析–但这正是我们所需要的。

              1. 重构是一项投资决策。当它得到回报时,回报的方式并不明显。这就是我最初的观点。

                但如果我的投资决策没有回报呢?”这样的回答表明你可能忽略了这一点。

                当你做出投资决定时,有时会出现这种情况。你不知道能赚回多少,甚至不知道能不能赚回什么。

                有些人不投资储蓄,因为他们无法忍受无法量化回报的不确定性,但我不会。

            2. > 重构不仅能让你在 50 年内受益,还能在数周内受益。

              证明价值是个难题。如今,要证明小改动的价值几乎是不可能的。产品想要做 X,他们决定价值取舍,而不是负责实现价值的工程师。

              1. >证明价值是个难题

                甚至是不可能的。

                这就是我为什么要这么做的原因。如果有人想证明我多年的经验以特定的方式应用会带来可量化的好处,而这是不可能的,我就会避免做那件事。

                或者更有可能的是,搬到一个能够欣赏这些不可量化的好处的地方。

                1. > 或者更有可能的是,搬到一个能够欣赏这些非量化效益的地方。

                  如何才能找到这样的地方?这肯定会引起我的共鸣。

                  做正确的事情有很多价值,但却无法证明。我可以做一些事情,让我的团队走得更快,但没有办法说,有了 X% 更好的测试,我们就能发布 Y% 更多的功能。

                  1. 尽量给人一种值得信赖的感觉,挑选那些信任你的客户/顾客/雇主。

                    我不会假装这对我总是有效,但一旦我能展示出丰富的经验,肯定会变得更容易。

                    运气也很重要。

                    在重构方面,我有一条铁律,那就是绝不征求别人的同意。如果我认为有必要做,我就去做。我不会向产品说明理由,也不会征求他们的意见。技术问题和优先级是基于需要知道的基础,他们不需要知道。所有的工作都会融入到功能工作和错误修复中。

            3. 嗯…如果你已经在重构,那就很好地证明了你并没有丢弃代码。

      4. “客户根本不在乎”。这种观点过于狭隘,甚至可以说是一种谬论,经常被用来为放弃一些好的做法辩护。

        是的,客户在乎。如果我把车停在维修车间,我关心的是修理工是否把车弄得一团糟,我在寻找良好的常规、巧妙的流程、他们为自己做出的明智选择。如果他们帮我修好了车,却弄得一团糟,我就再也不会去找他们了。大多数客户都会做出这样的选择。

        1. 在软件中,所有这些都是不为最终客户所知的。在你下载安装的任何软件中,你如何在代码中寻找实施团队所做的明智选择?

          1. 哦,相信我,我使用过很多公司软件,在这些软件中,开发过程中的缺陷是显而易见的,但我并不了解任何相关人员。

            1. 你使用它的事实不就意味着客户(选择供应商的人)并不在乎吗?

              1. 这显然是不同的,因为在公司层面上,选择一个供应商比选择另一个供应商涉及到政治因素,但与此同时,无论我参与决策过程,我都会尽量避免使用这些产品。

              2. 供应商的选择不是二元对立的。软件解决方案之间存在权衡。软件质量不是一个线性梯度。

                如果您的解决方案是最好的折衷方案,那么它就更有可能被选中。

                1. 在公司里,妥协比政治要少得多。在十多年的时间里,我看到了两种主要的模式:

                  – 必须购买产品,因为其供应商与高层管理人员或投资者有某种联系

                  – 必须购买产品,因为选择该产品的人目前被认为具有某种圣牛的地位,不容批评,因此周围的人将其发挥到极致

                  世界各地的大多数公司都是如此。

              3. 如果你在一个地方呆的时间超过几年,你就会成为做出这些选择的人,而且你不会忘记。

          2. 开发过程中的错误选择会以多种方式暴露出来:性能低下、工作流程笨拙、强制升级、单向数据转换、文档不准确等等。

            1. > 开发过程中的错误选择会以多种方式暴露出来:性能低下、工作流程笨拙、强制升级、单向数据转换、文档不准确等等。

              不一定。一些原本很好的系统可能在上述每一类中都有一个错误。

              而且,其他大多数设计不佳、执行不力的系统也会在客户毫无察觉的情况下顺利运行。

              1. 当然,好吧,设计精良的系统可能会有漏洞,而一团巨大的胶带和麻绳也可能运行得很好。

                但这并不是常态,我希望我们都能承认这一点。

            2. > 开发过程中的错误选择会以多种方式暴露出来:…

              你提供的所有例子都没有抓住重点。收入才是重点,其他的充其量只是辅助。

              1. jagged-chisel 问我们如何在下载和安装软件时做出明智的选择。我举例说明了最终用户会遇到哪些糟糕的选择,我的意思是,如果没有这些问题,就说明我们的选择是明智的。我不明白收入与这个问题有什么关系。谁的收入?我的例子又是如何偏离了重点?

        2. > 哦,是的,顾客在乎。如果我把车停在修理厂,我关心的是修理工是否把那里弄得一团糟,我在寻找良好的例行公事、巧妙的流程、他们为自己做出的明智选择的迹象。如果他们帮我修好了车,却弄得一团糟,我就再也不会去找他们了。

          在汽车上,你可以分辨,但在软件上,你却无法分辨。

          再加上大多数情况下,即使是牛仔编码的系统也是一次性的,会被替换(而不是维护),你真的无法分辨系统是被写成了一个等待被利用的大烂摊子,还是一个精心设计的弹性系统,因为大多数系统都是内部的,除了员工之外,不会面临任何对手的威胁。

          1. 我不敢苟同。

            我可以从软件的使用中看出不良的开发习惯,因为代码气味(和流程气味)很难闻。

            此外,我说的不是汽车修理本身,而是汽车修理的过程,当你参观汽车修理厂并与修理工交谈时,你就会发现这一点,而当你使用软件时,这一点就更明显了。

            具体例子

            – 涉及交互式脚本的安装/升级过程意味着,有人认为为极少数技术水平较低的用户简化这样的过程是可以的,而忽略了大多数人自动化一切的需求(交互式脚本显然更难自动化);这也可能是供应商的决策者(产品负责人)脱离现实的一个迹象

            – 文档不完善;一个明显的例子是,当产品的一部分是应用程序接口(API)时,当然 “有 “文档,由 Swagger 生成,但其中最关键的部分是传递参数的一些微妙之处,而这些参数本身是没有文档的,这使得整个应用程序接口根本无法使用。供应商的借口是 “但我们有文档”–是的,这是因为你的员工讨厌他们的上司,所以给了他们一些东西让他们滚蛋;而这些东西又给了客户。这其实是很常见的做法,我可以给你一份臭名昭著的供应商名单

            – 速度慢得令人痛苦的网络应用程序是一种程序味道的标志,没有人考虑过现实世界中的操作;有时甚至不是可扩展性的问题,而是库的选择/使用不当;我需要在这里用 rot13 给出名字,这样就不会被 googl:Argfhvgr 为公司开发了一款人力资源产品,在该产品中,每次点击浏览器中的任何活动字段都会触发一系列到服务器的往返操作,每次操作耗时超过 2 秒。这个产品的可用性在很多层面上都是如此的愚蠢,以至于我可以看到一大批团队都无法阻止这个垃圾产品的发布和销售。说来话长,但这是一个很好的例子,只要使用一下该产品,你就能知道供应商有多糟糕

      5. 如果客户不付钱给你,而且他们是你唯一的客户,他们也会很高兴。

    2. 你所描述的很多情况听起来都像是创新者的困境。传统系统之所以如此困难,其中一个原因是所有东西的存在都是有原因的,但这个原因往往是为了满足少数客户的需求。我们很容易看到一个组件,然后说,哎呀,只有 2% 的客户使用这个功能,我们应该考虑放弃它。但这 2% 的人是公司的客户,如果你放弃它,他们会大吵大闹。因此,自然而然地,系统中就会充斥着所有这些特殊情况,以处理小众客户的使用情况,而这些情况都是无法取消的。这时,一家初创公司出现了,他们瞄准了高价值的基本用例,放弃了最先进的功能。然后,他们开始获得越来越多的客户,复杂性不断增加,以满足他们的需求,最终初创公司成为传统公司,周而复始。

      作为开发人员,问题是我是否愿意在这样的系统上工作?对我来说,答案是否定的。如果我同意在这样的系统上工作,我基本上就会被我加入公司之前的错误设计决策所奴役。在这样的系统上工作永远不会让人真正满意。我说的都是经验之谈。

      但这就是公司支付薪水的原因。成熟公司的思维模式不是初创公司的思维模式,也不是业余项目的思维模式。在任何时候,什么才是最适合你的,这取决于你自己,而这种选择可能会随着生活环境的变化而改变。

    3. 即使是拥有 15 年历史、耗费大量人力的系统也会被替换。这些项目并不总能成功,但我亲眼目睹过。

      从头开始的重写并不需要与旧系统完全一致,只要你能事先发现、实施和测试所有关键的业务用例。

      最后一部分也是大型系统更换项目失败、超出预算或根本没有尝试的最常见原因。

      控制资金的人往往不具备技术知识,不知道旧系统隐藏了多少复杂性。安全的做法是继续使用。

      1. > 即使是拥有 15 年历史、耗费大量人力的系统也会被替换。这些项目并不总能成功,但我亲眼目睹过。

        是的,有可能。如果不能做到 99.9% 的成功率,就不能将其作为普遍的业务实践。

        > 从头开始重写不需要与旧系统完全一致,只要你能事先发现、实施和测试所有关键的业务用例。

        除了我普遍认为不需要完全对等(如果我们削减功能,”因为重建一个在你们那里运行良好的系统更容易”,我们的客户会很生气,而且他们有枪!)之外,我认为这没有充分考虑到年复一年的提交和代码中可能隐藏的内容。在我们维护的系统中,负责维护的团队会说:”看,我们有一百万行 SQL 存储过程。我们不知道它们都在做什么,因为它们中的大多数都是在时间的迷雾中编写的,没有任何文档,或者文档明显是错误的,我们可以完全忽略不计”。尽管大家都在抱怨,如果人们保留了适当的文档,这种情况就不会发生,但这种情况在任何遗留应用程序中都会发生。我们说的是一百多人并肩工作了几十年。这些事情会被遗漏,或者 “最佳实践 “会发生很大变化,以至于当时是个好主意的事情,现在却完全不知道了。

        如果能正确处理这些问题,即使是像试图对这些问题进行扼杀这样的非侵入性处理,也会耗费大量的时间和精力。

        1. > 我们有一些正在维护的系统,负责维护的团队会说:”看,我们有一百万行 SQL 存储过程。我们不知道它们都在做什么,因为它们大多是在迷雾中编写的,没有文档,或者文档明显是错误的,我们可以完全忽略不计”。

          有趣的是,当我想到我曾亲手开发过的系统,我永远无法想象它们会被取代时,我首先想到的是一个存在完全相同问题的系统:核心是一个庞大的 SQL 存储过程缓存,似乎没有人能够完全理解它。这家公司甚至使用 SQL 脚本实现了 HTTP auth 标记验证和所有访问控制,而没有任何集中式访问控制架构。接触 SQL 简直是一件可怕的事情。

          我刚开始在这家公司工作时,经理宣传说,他们对在公司工作多年的开发人员的补偿 “真的没有上限”。

          我想,用 SQL 来实现大部分应用逻辑的一个大问题是,它根本就不是一个合适的工具。逐步替换甚至推理各种 SQL 脚本中的逻辑是非常困难的。因此,没有人能够全面了解单体的所有功能。

    4. > 在我所见过的大多数实际案例中,这似乎是一个不可行的解决方案。尤其是当你的系统已经有 15 年的历史,并且投入了数千人年的心血。> 现在,”不要让它发展到那一步 “是我经常看到的自以为是的 IT 书呆子的回答,但这似乎是一种新创公司的想法,不适用于 90% 以上的软件开发。

      我认为,经常发生的情况是,旧系统在其复杂性的重压下摇摇欲坠,最终被 “新事物”(初创公司等)接管。只要新事物只是放弃了很少人关心的旧东西,增加了很多人关心的东西,就有可能被接管。

    5. 熵是软件复杂性的一个很好的比喻。减少熵比增加熵需要更多的能量,这是理解为什么在自然界中,在没有外部干预的情况下,系统往往会朝着更无序的方向发展的根本原因。因此,一个工程师就能对系统造成巨大破坏,甚至根本无法修复。

      这似乎是问题的关键,也是人的问题。

    6. 当一个代码库必须以某种方式进行扩展时,问题就会出现,而这种扩展并非最初设计的一部分。实际上,你必须在扩展时进行 “大重构”,就好像在最初设计系统时就知道新的需求一样。否则,很快就会出现复杂的混乱局面。

    7. 什么是不可行的解决方案?文章并没有描述作者的 “特殊方法”。

      你摘录了

      > [……]比听起来要难得多–关于这一点,我们将在另一篇文章中详述。

      截至目前(2024 年 5 月 29 日),我没有看到该博客有更新的文章。

      1. > 什么似乎是不可行的解决方案?文章并没有描述作者的 “特殊方法”。

        好吧,让我们在情况变得更糟之前重建这里 “的整个想法。这只适用于绿地开发中的一个小角落,几乎没有人在那里工作。说 “哦,是啊,千万别让软件变得复杂”,就好比说 “那你有没有试过在致癌突变发生之前将其切除”?

    8. 读到这个结论时,我真的笑出了声。这不仅听起来不可行、不可持续,而且听起来不免有些傲慢,很可能会惹恼与他们共事的其他人。

      1. 你能想象对你的产品负责人或其他什么人说:”哦,是的,我们不会做任何新东西。我们将在未来一年左右的时间里重建这项服务,因为代码看起来很难看。

  2. 这些绝对不是定律,而是不良工程文化的后果。

    我曾在设计良好的系统上工作过,但这些系统一直保持着良好的设计。例如,我记得有一个系统非常可靠:怎么做到的?团队从管理层那里获得了足够的支持和自主权:

    1) 允许我们聘用合适的人(没有秘诀–只是在不太快的增长速度和前辈与后辈的健康组合之间进行权衡)

    2)不急于推出新功能,在设计和技术方面投入足够的时间。

    该系统是一个核心的电子商务平台,这可能对它有所帮助。如果系统陷入混乱,可能会对顶线产生重大负面影响。

    在阅读此类文章时,请记住许多人都生活在泡沫中,可能只在功能失调的组织中工作过。当系统和复杂性对企业至关重要时,有大量的反事实存在。(也有大量例子表明,尽管如此,企业仍然陷入了混乱–但这并不是定律)

    1. 基本同意,不过我认为 “好的 “工程文化是如此罕见,以至于称其为 “好的 “而称其他一切为 “坏的 “感觉很奇怪。这更像是 “常规 “工程文化与 “特殊 “文化的对比。

      根据我的经验,我认为团队规模越大,就越有可能聘用 “普通 “工程师,质量也就越低。因此,只有一小部分拥有非凡运气和招聘控制力的专业团队才能成为 “卓越 “团队。

    2. > 糟糕的工程文化

      如果 99% 的项目都以某种方式结束,那就不是不良工程文化造成的后果。

      1. 可以说,整个软件行业都缺乏其他领域的 “工程文化”。

        1. 称任何软件猴子为工程师都是可笑的。工程师通常要对系统的灾难性故障负责。在 IT 行业,这被视为进步的必然结果。我们还需要三五代人的时间才能掌握适当的知识体系,甚至拥有工程文化。

          即便如此,我也不确定它是否能真正得到改善。除了人的因素之外,我觉得渐进式的构建方法无法也不会适用于有序的系统。除非我们回到凯在上世纪80年代所谈到的物体生态系统,否则我们将永远创造出 “怪胎”,即使我们做到了:我们也只是创造出了另一种 “怪胎”。

        2. 我所看到的因为人们真的不懂而导致的 “丛林联盟 “式错误数量之多令人震惊。但是,我们已经从一个你必须非常了解机器才能完成工作的世界,变成了一个只需参加六个月的特定网络技术速成班就能获得六位数收入的世界。我们还能指望什么呢?

    3. 听起来你的公司被金钱和时间淹没了。

      1. 他们资金充足,但遇到这样的文化并不十分罕见。

      2. 你认为这是巧合,还是你相信有果必有因?

        1. 我不太相信因果关系。除非你有大量的资金和时间,否则很难在拥有高质量代码的同时达到 PMF 的水平。我甚至不认为这样做是个好主意。

    4. 听起来你并不像你认为的那样不同意这篇文章的论点。你的论点基本上可以归结为:”如果你花足够的时间和心力去做,复杂性是可以控制的”。

      我的意思是,这正是这篇文章的重点–软件复杂性就像熵一样,它会随着时间的推移而不断增加,除非花费精力将其保持在较低水平。

      大多数公司都没有花费这种精力的经济动机,因此大多数软件都会随着时间的推移而变得糟糕。这不是因为工程师不好,也不是因为他们的文化不好,而是因为我们生活在一个资本主义世界。

      1. > 大多数公司都没有花费这种精力的经济动机,因此大多数软件都会随着时间的推移而变得糟糕。这不是因为工程师不好,也不是因为他们的文化不好,这只是因为我们生活在一个资本主义世界。

        我感到沮丧的是,经济激励是存在的,但它是分散的,难以衡量。而在当今的文化背景下,无法衡量的利益就是不被重视的利益。

        这并不是软件开发所独有的现象,但这似乎是如今困扰整个美国思想的问题。公共教育极具价值,但成本高昂,而且越来越多的人对公共教育的好处持怀疑态度。修补坑洼固然昂贵,但肯定低于修补爆胎和断裂车轴的漫反射成本。公共交通成本高昂,但减少交通流量和提高生活质量的好处却巨大而广泛。花在高危青少年身上的钱通常会在数年后以难以关联的方式偿还,即减少治安和监狱需求。

        我们越来越多地将注意力集中在短期支出上,而忽略了长期利益。

      2. 是啊,仔细想想,我不同意这些都是 “规律 “的说法,它们更像是 “趋势”。

        我也不认为复杂性一定是护城河。有时,复杂性会驱使客户选择更简单、更便宜的解决方案;至于复杂性是真正锁定了客户,还是只是带来了摩擦和烦恼,这取决于复杂性在哪里。

  3. > 在我的职业生涯中,我采取了一种从零开始构建新系统的特殊方法

    = 软件顾问和自由职业者。我不喜欢他们,因为我嫉妒他们能做绿地项目,能做所有的架构决策,却从来没有实际维护过自己的作品(因此也从来没有真正学会如何设计一个可变更和可维护的系统)。

    > 我们能为这种状况做些什么?

    第一:意识到这些规律,并意识到让一个运行了 20 年的系统继续投入生产是一项非同小可的任务,并为每多运行一年而感到自豪。

    第二:不要把它看成是 “以前的开发人员对我进行的 DoS 攻击”,而要把它看成是一个永无止境的数独游戏,它每天都在挑战我、刺激我并给我带来回报。

    1. “第二:与其把它看成是’以前的开发者对我的 DoS 攻击’,我试着把它看成是一个永无止境的数独,它每天都在挑战我、刺激我并支付我报酬”。

      对我来说就是这样,我曾在几个由业余爱好者或平庸的开发人员启动的业务系统中度过了一段美好时光,当我来到这里时,这些系统已经站稳脚跟并运行了 5-10 年。是的,WTF:无处不在,改变这些系统真的很难,需要大量的纪律和细心,但每当你把一些东西投入生产时,你也会使应用程序的其他部分变得更好、更强大和更快。

      对我来说,这才是真正有价值的事情,即使是嘈杂的管理层也无法阻止你在提交日志中添加可爱的小改进,从而逐步将这堆垃圾变成他们更稳定的收入,甚至可能是你自己的收入(如果不行,过段时间你就可以跳槽,到时候再加薪)。我并不在乎他们是否欣赏我的工作,首先,我在乎的是如何把我的技术练好;其次,我在乎的是同行们如何看待我的工作。

      当然,这并不是你故意制作蹩脚软件的理由,但当你以低廉的价格在市场上测试一个想法时,就会出现这种情况。

      1. > 对我来说,这真的很有价值

        同意。很多年前,我花了两年时间来完成一个新项目,它慢慢地但肯定会越来越糟,这让我非常沮丧。现在,我正在做一个已经有七年历史的项目,虽然经历了很多人的手,但每一个小的改进都让我感觉很好,当我偶尔需要做一些黑客工作时,我也不会感到难过,因为总体来说,它比以前更好了。

    2. 数独是一个非常贴切的比喻,谢谢!

      如果我可以稍微延伸一下:有时,当你开始一项新工作时,你所继承的棋盘状态实际上是微妙的无效状态(即它会导致无法求解),而你必须找出 “擦除 “的最小数字数,以最终获得有效的棋盘状态。

      实际上,你每年可能会使用一次橡皮,因为没人喜欢橡皮。所以你只能用大量的笔记来弥补!

      1. 哈哈,的确,老板们一点也不喜欢橡皮:)

  4. 我认为在这里讨论意外复杂性和本质复杂性的概念很重要。如果你的组织的优势在于拥有世界一流的工程技术,那么基本复杂性就是你的朋友:具有巨大基本复杂性的问题领域就是真正的护城河:它使进入市场的门槛居高不下。如果世界上的基本复杂性越低,软件工程领域的收入就会越少,软件工程师的职位也会越少。例如:当技术复杂性的进入门槛过低时,市场就会沦落为竞相逐低的市场(如大量不赚钱的独立游戏)。另一方面,偶然复杂性也不是我们的朋友:如果你维护一个偶然复杂性过高的系统,就会面临一个巨大的风险,即更聪明的竞争者可以用更少的资源创造出至少同样出色的产品。

  5. > 设计良好的系统是一种不稳定的、短暂的状态;而设计糟糕的系统则是一种稳定的、持久的状态。

    这是一种熵:我不会说一个设计糟糕的系统是 “稳定 “的,它和一个设计良好的系统一样不稳定,而且会不断进化,但 “设计糟糕 “的方式要比 “设计良好 “的方式多得多,因此,如果不积极对抗熵,它就会不稳定,但却始终 “糟糕”。

    1. 我认为这是近期权宜之计造成的后果,因为我们没有能力/时间/信心/勇气在必要时进行重大重构。

      在 TableCheck,我们有一个已经开发了 11 年的 Ruby monolith 应用程序。当然,我们的人员流动并不多,在维护任务上花费的时间也很多。

    2. 我不会说这只是 “熵 “的问题,因为无序状态远远多于有序状态。

      确实如此。但同样是软件,文章说有序状态 “易于改变”,而无序状态 “难以改变”。因此,无序状态有更多的 “摩擦”,即从无序状态很难安全地转移到有序状态。但也有可能进一步进入无序状态,但摩擦力更大。

      我曾观察到这样的情况,即在超过一定程度后,人们就放弃了重构,而只是添加代码来解决问题–他们只是将能实现大部分需求的代码包装成一个特殊的案例,以满足他们的特定需求。这反过来又增加了理解和重构的难度。久而久之,就形成了一大堆嵌套的特例和变通方法。

      与这种趋势作斗争更加困难。这是在走上坡路,与阻力最小的道路背道而驰。

      1. > 因此,无序状态有更多的 “摩擦”,即更难安全地进入有序状态。但有可能进一步进入无序状态,摩擦力也会更大。

        不可能安全地进一步进入无序状态。甚至保持不变也不安全。

        虽然我同意摩擦会更多……

  6. 对于第一定律,我有一个相反的看法,我在(20 年的)职业生涯中见过两个例子:

    > 随着时间的推移,系统不可避免地(在很大程度上)退化,这是因为系统的架构设计得不好。大多数系统的架构并不差,但大多数*成功的系统的架构却很差。好的架构需要时间(通常是无利可图的时间),而大多数成功的系统都优先考虑速度。

    总之:利润的需求必然导致糟糕的系统设计。

    在我的职业生涯中,我见过两个例子:一个是在一家大型企业中,一个团队的规模大于需求/资源意外过剩,他们的背景产品价值被低估,没有真正的 OKR。第二个是政府资助的合同(由于腐败的招标程序,这些合同通常会严重偏向相反的方向,但如果有意愿的话,质量通常会有喘息的空间)。

  7. 比起阅读代码,我更善于从文字和图表中理解软件。

    拥有坚固的心智模型–你凭直觉理解的东西–有助于复杂性的理解。并清楚地看到真相。

    要谦虚:在别人看来,我的代码可能很难看,但我理解我的代码比阅读你的代码更快。

    我不能假装知道我建立的系统会比你的好。

    要警惕骄傲。

    1. > 警惕骄傲。

      这本身是个好习惯,但仍有客观的启发式方法来评估软件质量。

      例如简单性:如果要解决完全相同的问题,在不影响性能的情况下,一种解决方案比另一种解决方案要简单得多(意味着更直接),那么显然是一种解决方案获胜。

      自动化测试的数量是衡量软件质量的另一种客观方法。

      1. 感谢您的回复。

        说到软件,我觉得自己是个矛盾体:我不喜欢维护那些总是出问题的东西:依赖关系、系统升级、部署脚本,以及那些并非每次都能 100% 稳定运行的东西。

        因此,我理想中的系统是针对文件或 SQLite 数据库运行一个简单的二进制文件,并且每次都能可靠运行。而不是一个复杂的微服务架构,有很多间接性,并保持网络运行。

        但这需要与我设计多线程软件的爱好相平衡。

        1. 如果我们让一切都完美无瑕,那生活就太无趣了,我们必须想方设法为生活增添色彩,只要我们觉得一切都太顺利了。

          我有个朋友就是这样:喜欢纯函数式编程语言;白天的工作是做总线通信系统的万事通¯_(ツ)_/¯

      2. > 以简单性为例:如果要解决完全相同的问题,在不影响性能的情况下,一种方案比另一种方案要简单得多(也就是说,更直接),那么显然就是赢家。

        不是那么清楚:有人说数组上的 `foreach` 比单行 map/reduce/comprehensions 简单。另一些人的说法则恰恰相反。

        有人说单体比微服务简单。另一些人的说法恰恰相反。

        有人说 Python 更简单。也有人说 Java 更简单。

        我的意思是,之前有人回复了我在另一篇文章中的评论,他认为 ORM 比 SQL 简单。我的看法恰恰相反。

        在大家就 “简单 “的含义达成一致之前,这个标准并没有什么用处。

        1. 我应该大大地强调一下,是我的错,我的目标是涵盖 foreach 与 map 类型的问题,两者本质上是等同的,更多的是风格问题。

          我想说的是删除 20/30 行无用的代码,或者避免复杂的 OOP 模式,因为在“struct“s 上使用几个简单的函数就足够了。我在实践中看到过这两种情况(嗯,我自己也犯过错):它们往往是过度思考或 “试图使代码可重用 “的结果。

          至于微服务与单体,我不认为它们在复杂性方面有可比性。一旦知道了实际需求,一旦知道了每种模式在满足这些需求时的使用情况,我们就可以进行比较了。但在此之前,这有点像 “C++ vs. Rust “类型的辩论:真实情况到底如何?

          关于 ORM 与 SQL,我倾向于同意你的观点:我们通常不能假装 ORM 是完美的(”无漏洞抽象”)黑盒子:在某些用例中,这是真的,但一旦你远离玩具,你就必须同时了解 SQL 和 ORM 是如何工作的。这比仅仅处理 SQL 要复杂得多。

          一般来说,依赖关系也是如此。但它们本质上也是强制性依赖(如 TCP 栈、磁盘驱动器、HTTP 库等)。

  8. 第一定律意味着,不可能通过渐进式改变将一个设计糟糕的系统变成一个设计良好的系统。

    我不同意这种说法,不从零开始改善某些系统的状态当然是可能的。

    1. 同意。

      例如,一旦你对代码库有了很好的掌握,并对未来的需求有了大致的了解,你就可以进行低风险的局部重构,以便轻松实现当前和以后的功能/错误修正等。

      不过,需求并不是系统性的。这意味着,作为开发人员,你并不总是(至少是明确地)可以获得鸟瞰图,也不能根据鸟瞰图采取行动。

    2. 我不这么认为–它只是说设计得不好是一种更稳定的状态。你可以用很小的步子把一块巨石滚上山坡,只要你还能防止它再滚下来(类似于你团队中的其他开发人员/要实现的新需求)。

    3. 我认为我们可以研究一下复杂性理论中的吸引态概念。如果你想做出改变,你需要让你的系统发生足够大的变化,从而进入另一种状态。

      用更通俗的话来说,就是代码库会对抗你的改变。这就意味着,小规模的渐进式改变可能是不够的,你至少需要几次大的飞跃。

    4. 不是不可能,没错。但这是在走上坡路,与阻力最小的道路相反。

      1. 不只是上坡,还能穿过雷声和火焰。

  9. 非常有趣的文章。我觉得这些与其说是软件本身的规律,不如说是文化的规律。换一种说法:

    “只要一根火柴,就能烧毁一片森林

    “一个坏苹果坏了一锅粥”。

    要保持一个系统的良好设计,需要良好的工程文化(正如其他人所说),以及工程、管理、销售……整个公司的紧密配合。

    在控制系统中,通常会通过某种反馈机制来处理这些情况。换一种说法:如果你想快速做出调整,就需要 “感觉 “到问题所在。

    我们很想知道,人类社会/公司是否存在这种 “反馈回路”。或者它就是这样:它存在,但它有一个非常大的延迟,导致系统最终崩溃并重新启动……

    1. > “只需一根火柴,就能烧毁一片森林”

      我觉得这句话很酷,因为(据我所知)健康的森林会经历多次自然的小规模燃烧,清除灌木丛。由此产生的灰烬可以作为肥料帮助重新生长。

      然而,如果人们采取了错误的政策,那么这些小规模的燃烧事件就会被阻止,直到灌木丛累积到一根火柴就能烧毁整片森林的程度。

      与重构、糟糕的开发方法和项目失败的类比不言自明。

  10. 我认为《软件进化的(八)法则》对这一点的描述更为全面:https://en.wikipedia.org/wiki/Lehman%27s_laws_of_software_ev…

    这个话题总是让我想起某人说过的一句话,可能是阿兰-艾普尔教授,”软件是唯一一个维护会使其退化的系统”。

    1. >”软件是唯一一个维护会使其退化的系统”。

      有人还没见过我的偷工减料的电工/杂工

  11. 我不同意 “一个设计良好的系统是一个易于随着时间推移而改变的系统 “的说法。

    在我看来,这是 “通用 “与 “专用 “的分水岭。随着系统越来越优化(针对任何方面:资源使用、带宽、延迟、成本等),它们越来越需要做出限制其他潜在选择的选择。

    这与设计得好坏无关。一个设计良好的系统是一个能很好完成任务的系统,这部分包括能随着时间的推移而发展(或不发展)。这可能包括保持更通用,也可能包括变得更专业。

  12. 17 年前,我建立了一个系统,用于自动整合和预处理来自不同来源的数据。它每天能节省 20 个工时的工作量和停机时间。在我管理该系统的两年里,我为它提供了 12 个小时的支持,而我却因为它的设计有多么 “糟糕 “以及其他团队如何改进它而受到了很多批评。后来我离开了这个岗位。

    离职 6 年后,我接到一个系统管理员的电话。他告诉我,系统主机宕机了,但他们设法恢复了硬盘,并将其还原成了虚拟机。我很惊讶,因为我以为他们早就把这台机器停用了,因为当时那些秃鹰们为了提高他们的 KPI,试图编写一个更好的版本。他说多次努力都失败了,没有人能够复制该系统的功能。他之所以给我打电话,是因为在他们将硬盘装入虚拟机并重新打开所有设备后,系统又重新上线了,而且不费吹灰之力就能完美运行。他感到非常震惊,只想和能够建立一个不会死机的系统的人谈谈。

    这不是最优雅或设计最好的系统,但我设计了它不会死,这正是我所得到的!

  13. > 在我的职业生涯中,我采取了一种从零开始构建新系统的特殊方法

    这是我经常看到的初级程序员的做法。作者有一些经验,其中很多是研究方面的,我很好奇他们在这里是怎么实践的,效果如何。

    我并不认为糟糕的设计是不可避免的。要对抗开发人员的善意和错误动机是很难的。我们常常被逼着接受又快又便宜的设计,而不是又好又出色的设计。

    更新:拼写/语法。

  14. 这是一篇奇异的作品,充满了奇怪的隐喻,并试图通过数学术语等方式让自己听起来比实际情况更聪明。

    >假设一个系统 X 是精心设计的。有人出现了,并把它改成了另一个系统 X’–顾名思义,又快又简单。现在,X’要么继续设计良好;在这种情况下,它可以被快速、轻松地修改为另一个系统 X”;要么它将进入设计不良的状态,因而难以修改。

    ???

    1. 在我看来,这样的表述既简单又正确。奈杰尔-陶也表达了同样的意思

      > 软件有一个彼得原则。如果一段代码是可以理解的,就会有人对其进行扩展,从而将其应用于自己的问题。如果无法理解,他们就会编写自己的代码。代码往往会被扩展到难以理解的程度。

      1. 是啊,这就是这篇好文章的主要道理。

    2. 好吧,我正要去读这篇文章,但我觉得这句话很有见地。设计糟糕的系统往往会一直设计糟糕下去,而设计良好的系统则可以自由变异。这是与生俱来的。

      就我个人而言,我从未想过这一点。

    3. > 试图听起来更聪明

      他们有可能更聪明,在思考和说话时理所当然地使用数学语言。

      1. 当然有可能,但鉴于这是一个 2c 概念的 2 美元段落,我不相信我们看到的就是这种情况。

        1. 你说得有道理,他也许可以不引入微积分符号来表达自己的想法。

    4. 这里真正奇怪的是你的评论,你认为X´是一个注册商标的数学术语……不瞒你说,像你这样的人让我很困惑。

      作者所说的不仅正确,而且表达得很好。

  15. Linux内核就是一个例子,它是一个大型代码库,历经数十年,经历了巨大的范围变化,却没有遇到这些问题。

    最初,它只能在 X86 PC 上运行,而且今天它所支持的大部分硬件都不存在。如今,它可以运行在从小型嵌入式系统到超级计算机的多种架构上。

    最初的一些代码非常粗糙,但现在的代码都非常好,而且一直在进行清理和重构。事实上,长期可维护性是维护者决定接受或拒绝补丁的主要考虑因素之一。

    为什么这对 Linux 有用?因为公司被用来提供资金(现在大多数内核开发者都将此作为他们有偿工作的一部分),而不给他们质量技术控制权。付钱给内核开发人员的公司对开发的内容(大概是对他们很重要的东西)有发言权,但对如何开发没有发言权。

  16. 这些 “定律 “的框架并不完善。软件的 “复杂性 “是观察和改变软件系统的自然或人工智能的函数,这是一个推论–只要有足够的眼睛,所有的错误都是浅层次的。

    复杂性 “的分类是观察者而非被观察者的函数。

  17. 我喜欢这篇文章。这些都源于一个基本规律,即所有增长或变化的系统最终都会衰减和死亡。无论是动物、社会,甚至是软件,死亡都是生命不可避免的一部分。

    这是好事。重生带来活力,如果没有死亡,就不可能有新生。

    1. 有些制度的生命短暂而混乱,有些制度的成长和成熟则历时长久。重要的不是一个系统活了多久,而是它在多大程度上达到了自己的目的。

      Memento mori,不可避免的熵,不断的衰变和解体。但同时,还有一种相反的力量,通常被称为负熵或 “负熵”,但我更愿意将其视为创造力。它是组织事物、花卉、设计、建筑和系统的力量。

  18. 如果这些是软件复杂性的前三个 “基本 “定律,那么盖尔定律适合于哪里?也许是第四定律?

    另外,第二条定律”复杂性是一条护城河 “似乎与 “越坏越好 “相矛盾。

  19. 读完这篇文章,我如释重负。如果同事、外包团队甚至客户对系统原本简洁的设计进行曲解,使其随着时间的推移变得糟糕,那么任何人都很容易感到被冒犯/沮丧。我很喜欢阅读这三条定律,因为它们让我感觉到自己是普遍问题的一部分。软件系统与物理物质没有什么不同:它会衰变。这就是该死的熵。我们谁敢与之抗争?

  20. 软件复杂性三定律啊。

    > 已超过此资源的速率限制

    若有所思地点点头

  21. 关于:第一定律……我最近一直在想一件事,那就是软件与园艺是多么相似。但在园艺中,我们不怕修剪植物,边修边整。在软件中,我们最终只是在上面添加东西,而不考虑整体设计。我们总是偏向于添加而不是移除东西,而保持东西有序的关键在于移除东西、说 “不 “等等。

    1. 我不太同意这种观点。与 knolling [0]类似,重构你正在处理的代码及其周围的代码应该是每个开发流程中自然而然的一部分。如果你发现某个设计选择不再有意义,那就重构。如果代码不常用,就删除。等等,在现代,我们有很多强大的工具供我们使用,只有这样,才能保持代码库的可维护性。永远不会有什么神奇的未来,让你有时间清理和重构那些棘手的部分,时间就是现在。就像园艺一样,如果你在修剪玫瑰花时发现有杂草生根发芽,只要把它拔掉,它就在那里。当然,如果有更多的时间来处理这些杂草,那就更好了,但肯定会有一点帮助。

      [0] https://youtu.be/s-CTkbHnpNQ?si=KYwllK4NJY1bjRa3

    2. 这种 “修剪 “不仅仅是针对功能,更广泛地说,是为了简化和最小化代码库(更少的代码 = 更少的错误)。

      无论软件多么陈旧或臃肿,这种重构都是维护软件必不可少的一部分。

    3. 我想我们大多数人都经历过 “软件开发就像…… “的隐喻阶段。

      在我那个年代,我们通过麦康奈尔和 PragProg 这两个家伙来进行比喻。杰夫-阿特伍德(Jeff Atwood)在 15 年前的一篇博文中也提到过这个问题:

      https://blog.codinghorror.com/tending-your-software-garden/

      1. 这篇文章最上面的评论太明目张胆了

    4. > 让一切井然有序的关键在于清除杂物

      一旦你准备好为那些你想削减的东西放弃收入,请继续!

  22. > 当系统为争夺市场份额而相互竞争时,精致就会被抛到九霄云外,设计者往往会给应用程序想要的一切。

    这对于那些受限于应用程序接口的成熟系统来说是错误的。

    大型复杂应用程序所依赖的一套大型复杂 API 的供应商不会在新的 API 中为应用程序用户提供一切,因为他们担心用户会迁移到另一套 API。

  23. 软件复杂性的第一定律是:软件开发人员没有接受过如何进行专业交流的教育,因此他们会说得太多,忽略、误导和误导彼此,一次又一次地把复杂性搞得一团糟。他们从未意识到,更好的沟通可以解决他们不断重复的恶梦,即他们的工作是由复杂而薄弱的泥团组成的。

    1. 学习如何进行专业沟通的好方法是什么?

  24. 我认为我们能控制的主要事情是,如果我们是绿色应用程序的一部分,就必须确保它尽可能简单。我目前正在重新设计一个中等规模的应用程序,主管决定他们需要功能标志,这意味着在同一个应用程序中保留新旧版本,并为正在进行的重新设计创建一个巨大的独立分支。其中一个分支就足够了,因为他们实际上并不打算再看旧设计。更不用说我马上要合并的同一领域中相同数据的三个不同模型了。另外,我讨厌功能标志,认为它们是有史以来最糟糕的创可贴,可以让设计人员优柔寡断,让业务人员无能为力。

  25. 我不同意第三定律;复杂性以失败为界限。

  26. 这一切的根源是一个哲学问题,它涵盖了我们所做的一切:不可持续的增长。我们在文化上沉迷于 “更多 “的概念。更多的价值。更多的钱。更多功能。更多,更多,更多。这就是我们的处境:濒临生态灾难。不可持续的系统容易崩溃。一切都被嵌入其中。有计划的淘汰和每年一次的手机升级,自然资源被毁于一旦!

    如果我们要生存下去,就必须放慢脚步,与其制造 “更多”,不如制造 “更好”。遵循 Unix 哲学。拥抱 “足够好”,学会停止。

    我在跟谁开玩笑呢?

  27. 我几年前就想出了一条定律。

    几乎所有的编程工作都是在过于复杂的系统上进行的。如果一个系统设计得很好,而且不太复杂,那么他们就不需要雇用你!

  28. 我发现自己每年总要坐下来读几遍《走出焦油坑》[0]。它一直是我职业生涯中最具开创性的著作之一。我仍然记得第一次读到下面这段关于复杂性的文字时的情景,以及它是如何点亮了我脑海中所有的灯泡:

    >> 基本复杂性是(用户所看到的)问题的内在本质。

    >> 意外复杂性是所有其他复杂性–开发团队在理想情况下不必处理的复杂性(例如,性能问题、次优语言和基础架构带来的复杂性)。

    >> 请注意,”基本 “的定义有意比通常的用法更为严格。具体来说,当我们使用 “必要 “一词时,我们指的是对用户问题的严格必要(而不是–也许–对某些特定的、已实施的系统的必要,或甚至–对一般软件的必要)。

    我学到并不断实践和改进的最佳技能是,能够将我们谈论软件问题的方式归结为问题的真正本质。养成这样做的习惯有助于澄清问题本身的轮廓,并改善围绕解决问题的讨论,因为真正重要的东西的界限变得清晰了,然后每个人都知道,从这一点出发,我们所做的每一个选择都是额外的、偶然的复杂性,是我们自己添加到问题中的。

    我经常看到,即使是新开发的软件也会迅速增加整体的复杂性,这是因为做出选择的人没有花时间将问题与软件真正隔离开来,而是将问题框定在语言、框架、架构、基础架构等背景下,然后就开始对着问题乱敲代码。

    如果你还没有读过《走进焦油坑》,它确实改变了我看待和思考软件和问题复杂性的方式。你或许也能从中找到价值。

    [0]: https://curtclifton.net/papers/MoseleyMarks06a.pdf

    1. 正是这样的评论让我来到黑客新闻。

      现在我们该怎么办?我正在做的是

      http:/github.com/civboot/civboot 和 http:/github.com/civboot/civlua

    2. 说得太对了。我注意到,一旦我开始寻求问题的本质,我的能力就会不断提升。我问自己”我从这些信息开始。从输入到 X 的数据转换的本质是什么?

      当我把任务的本质归结为数据转换时,解决方案就从我对问题的理解中产生了,我发现我对工具的选择也很容易从这里过渡到其他地方。正如你所说,问题与软件是 “隔离 “的,这让我们更容易推理。

      遗憾的是,当我试图在我们的行业中倡导这种思维方式时,并没有得到太多的支持。

      题外话:这让我想起中学教育中的一个有趣的观点。你在高中参加过 AP 考试吗?如果参加过,你可能会和我一样,记得老师在学生备考时总是反复强调的一句话:”回答问题”:不管是 AP 英语还是 AP 物理,”回答问题 “这句话我们听了一遍又一遍,直到它成为我们的第二天性–这是很好的建议!因为在这些考试中,学生们最容易犯的错误就是没有真正回答所提出的问题,即使用最美妙的散文来表达,也会导致不及格。

      我认为软件工程通常也是如此。如果你不理解问题,即使是最好、最复杂的工具也无法产生可行的解决方案。

      1. > 遗憾的是,当我试图在我们的行业中倡导这种思维方式时,并没有得到多少响应。

        是的,我明白你的意思。这已经成为我评估公司工程文化的一个主要信号。我很幸运能与一些真正理解这一点的优秀人才共事,我也曾在与那些不理解这一点的人共事的过程中挣扎和煎熬。

        > 如果你不了解问题,即使是最好、最先进的工具也无法产生可行的解决方案。

        我相信我们都见过一些可怕的怪胎,或者是我们自己创造出来的,在技术上我们可以称之为解决特定问题的可行方案……但这并不意味着有人愿意去研究它。要保持复杂性,就必须找到最简单的解决方案,将基本复杂性和偶然复杂性隔离开来。化繁为简很难,需要不断地做好这一点。必须[咳咳]花费必要的时间来隔离问题并清晰地表达出来。如果你不参考技术堆栈和工具就无法隔离和阐明问题,或者你的解释变得模糊不清、错综复杂,那么你实际上还没有确定问题的基本复杂性。你仍然停留在偶然复杂性的领域。而这正是设计和架构软件的可怕之处。

        同样重要的是,在任何一款软件的生命周期内,随着新事物的出现–新的错误、新的功能等–你必须不断重新采用相同的流程,并评估/反思新事物如何适应(或不适应!)现有的架构/设计。如果做不到这一点,设计不佳的软件就会变得无限复杂,产生无穷无尽的 “技术债务”。设计良好的软件会将所有意外的复杂性隔离并封装到自己的空间中,从而为软件的调整和扩展留出余地。设计良好的界面可以让你在与调用者隔离的情况下重构、重塑和扩展问题域的内部结构。这就要求软件团队及其领导层遵守纪律,在先验发生变化时花必要的时间进行调整和重构。这些工作应始终朝着提高团队速度和个人生产力的方向前进。

        > 你在高中时参加过 AP 考试吗?

        是的,当然参加过!我绝对记得你在这里描述的内容。

        1. > 如果你不能在不参考技术堆栈和工具的情况下隔离和阐明问题,或者你的解释变得模糊不清、错综复杂,那么你实际上还没有确定问题的基本复杂性

          100%.我没有太多要补充的,但我非常喜欢我们的讨论。

          你看过迈克-阿克顿的这篇演讲吗?如果没有,也许会引起你的共鸣。https://www.youtube.com/watch?v=rX0ItVEVjHc

          1. 我还没看过那个演讲。我一定会去看看的。谢谢!

  29. 要解决这个问题,我们可能需要放弃危险的乌托邦理想,即复杂性和偏离完美是必须不择手段解决的问题。

    世界是一个复杂的地方,几乎没有任何事物适合简单化的愿景,几乎没有任何其他工程学科会像 IT 行业那样回避渐进式改进和复杂性管理。

    现实世界中有很多道路、供水和污水处理基础设施的例子,这些基础设施的整体系统具有连续性,可以追溯到几个世纪以前,在这些地方出现的每一个问题都能得到及时解决,而没有人通过擦拭和重新设计来重新设计系统,这让这些基础设施的工作人员感到自豪而不是羞愧。

    我们越早摒弃这样的想法,即只要使用 X 工具,以正确的方式,用正确的团队,重新设计,就能最终打造出一个不需要不断维护和重构就能满足用户需求的系统。

  30. 伤得有点太深了。

    优雅的代码和简洁的架构都敌不过人类思维的混乱。

  31. 伤得太深了点。

    优雅的代码和简洁的架构都敌不过人类思维的混乱。

    请注意,就连作者也认为 Kubernetes 和 Zookeeper 这样复杂得毫无意义的系统是最好的设计。

  32. 我曾经写过一个小软件,它能在恒定时间内为每个人做任何事,而且没有任何依赖性。它非常简单。

  33. “超过速率限制”。看来又违反了软件法。

  34. 软件的复杂性与已经、正在或将要开发代码库的开发人员数量成正比。

  35. 当我看到这个标题时,我以为会找到类似于 Ousterhout 的《软件设计书》中关于软件复杂性的 3 个指标。

    文章的实际内容有点让人失望。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注