代码简洁性
推理与选择
软件系统最重要的属性之一是,无需运行就能理解它将做什么的能力。这个概念通常被称为系统的“可推理性”。基本上,你希望在不先看到系统运行的情况下,就能对其结构、行为和结果做出判断。
要理解为什么这一点很重要,可以想象一个由一百个不同部分组成的系统。为了简化,我们假设它是一个实际的物理系统,而不是计算机。假设我们有一个生产汽车的自动化工厂,从原材料到成品车有100个步骤。每个部分都对输入材料进行某些改变以产生输出产品。我们可以有多种方式来配置这个系统及其各个部分:我们可以让每个部分执行多个动作,根据所采取的动作不同,我们选择的下一台机器实际上也会不同。例如,假设我们正在将金属转换为圆棒。每辆汽车需要不同数量的圆棒,而我们的圆棒可以由5种不同的金属制成。所以,这台机器有一个程序,每次得到一根钢条时,就决定它将制造哪种圆棒。这取决于一天中的时间和我们汽车的当前需求。然后,根据制造出的圆棒类型,该圆棒会进入五台不同下一台机器中的一台。
现在想象一下,整个系统中的每一台机器都是这样的——它接受一组复杂的输入,产生一组复杂的可能输出,这些输出又前往一组复杂的可能下一台机器。这不仅使得人类无法在任何给定时间对整个系统的确切行为做出判断(即推理),甚至难以推理各个部件的行为。
现在想象另一种设置,其中每台机器接受一个输入,提供一个输出,并且每台机器只“与”另一台机器“对话”(即,它的输入总是来自一台特定的机器,其输出总是前往另一台单一的机器)。虽然可能很难一次性考虑整个系统(因为它仍然是100台机器),但查看每个独立部件很容易,并由此推理出各个部件以及整个系统的逻辑行为。
这是简洁性的核心部分——对类似这样的系统进行推理的能力。当你查看软件系统的任何独立部分时,你应该能够在不运行该部分的情况下,对其行为、保证、结构和潜在结果做出判断。该部分如何与系统其余部分交互应该非常清楚——要么我们应该确切知道什么调用它以及它调用什么,要么我们应该理解创建该部分使用边界的结构。例如,这就是为什么许多编程语言中“私有”和“公共”函数的概念能增强系统的可推理性——它们是告诉我们什么可能发生、什么不可能发生的边界。当你查看函数或类的实际实现时,通过阅读代码和注释,应该很容易理解它正在执行的操作。例如,这就是为什么对函数和变量命名如此重要——因为良好的命名允许读者推理系统的行为和边界。
选择
不过,要使系统具备这种品质,还有另一个非常重要的组成部分。为了解释这一部分,想象一下我们想象中的汽车工厂里的每台机器都不是自动化的,而是由一个人来操作。这更像是一位正在键入实际代码的软件工程师,“运行”他们的IDE、计算机、编译器、编程语言等“机器”。
在我们第一个例子中,我们有做出复杂决策的复杂机器,想象一下以前自动化机器做出的所有选择,现在都必须由一个人来做。也就是说,每次一块金属进入我们的机器,一个人必须查看它,决定它是什么类型的金属,决定制造什么类型的圆棒,所有这些都要基于查看汽车的当前需求并记下一天中的时间。现在,在真正的工厂里,其中一些实际上可能是可以接受的。这至少为一个人创造了一份有趣的工作。但即使在那里,你也可以看到你会为许多错误和不良结果打开大门。
将这与我们后面的例子进行比较,在那里我们有简单的机器,具有简单的输入和输出。它们对人来说操作起来如此容易,以至于一个人可能可以操作多台机器,并且你几乎可以消除所有出错或产生不良结果的可能性。
现在考虑到,在编程中,程序员通常操作着数十或数百个这样的“机器”(就他们维护的类和函数而言)。因此,对于复杂的汽车工厂来说,一个更好的类比是让一个人操作所有一百台机器。正如你所见,如果系统的每个部分都向操作员提供了太多必须做出的决策,那么制造我们的“汽车”很快就会变得不可能。即使你能做到,你制造汽车的速度也会极其缓慢,并且会让操作机器的人精疲力竭。你瞧,这正是那些必须维护具有那种复杂程度的软件系统的团队所经历的情况。
然而,当我们将人类加入我们的“工厂”时,我们引入的关键点是什么?我们引入了决策(人类用头脑做的事情)和选择(呈现给人类的选项)这两个因素。
有些思想流派认为,所有开发人员都应该随时被授权对其软件系统做出所有可能的决策。这听起来很棒,因为它听起来像是为聪明人提供了智力自由——这是我们所有人都想要的。然而,如果你将这个原则推行得太远,你实际上最终会为你的开发人员创造一个复杂的汽车工厂——一个存在如此多选择需要做出的系统,以至于他们要么变得瘫痪,要么注定会做错,要么开发出其他人难以理解的、极其不一致的系统。
那么这里的解决方案是什么,是剥夺所有人的所有选择,把他们变成执行首席架构师意志的没有思想的自动机器吗?嗯,我相信有些软件架构师会喜欢这样,但实际上,这有点极端的解决方案。答案是,要认识到哪些选择对开发人员来说是重要的、能够做出的,哪些是不重要的。
这取决于你在软件团队中的角色以及你在软件生命周期中所处的阶段。例如,如果你刚刚创办一家新公司并且你是第一个开发人员,那么你能够选择关于公司将要运行的基本平台(你正在使用的语言、框架、库等)的几乎所有方面,这一点很重要。但即便如此,你也不希望这些框架和库向你呈现你不需要做出的决定。想象一下,如果一个编译器停下来询问你应该如何优化每一段代码。这对你有帮助吗?能提高你的生产力吗?这实际上对你的公司或你试图实现的目标来说是净收益吗?我不这么认为。
然后,在项目生命周期的不同阶段,一旦你确定了所使用的语言和特定框架,你通常不会允许一个随机的初级开发人员为他们的代码库部分选择不同的语言或框架。这是一个他们不需要花时间去做的决定——对他们来说,顺其自然更有效率。即使可能存在更好的语言或框架可以使用,仅仅为了实现这位初级开发人员的一个功能而重写整个系统,似乎不是对你资源的良好利用。
总的来说,如果你能移除足够多开发人员不需要有的选择,你实际上可以在整个公司的范围内节省相当多的开发人员时间。想象一下,如果你公司的每个团队在开始开发系统之前,都必须花两周时间对不同框架进行审查。现在想象一下,你采用了一个即使不完美但足够好的框架(也就是说,它能够满足所有将要使用它的人的业务需求),并且没有人再需要做那个决定了。你为整个公司节省了多少工程时间?这是巨大的——从长远来看,这比你几乎可以做的任何其他生产力改进都要大。
现在,重要的是要记住,有些决定是开发人员需要做出的。他们绝对需要能够决定其系统的业务逻辑如何运作——这是他们能够完成工作的核心要求。过去有些框架和库根本不允许人们编写他们需要的系统,这种程度的限制对生产力是有害的。例如,想象一下你的公司采用了一个支持HTTP但某种程度上从根本上无法支持SSL(即,没有HTTPS)的框架。当出于安全目的需要加密连接时,那将是灾难性的。所以这将是一个非常糟糕的限制。
有时候,这是一条非常棘手的路线,但总的来说,我发现从长远来看,倾向于删除选择实际上会让开发人员更快乐,因为这让他们更有效率。当你剥夺人们某些选择时,一开始会非常困难,因为他们觉得你在影响他们的个人自由。在某种程度上,短期内,你确实在这样做。但事实是,你正在试图提供更多的创作自由——从根本上说,这是那位开发人员真正想要的自由。限制选择的目的应该是为了提高创建系统的能力。你不是在扼杀生产,而是以某人根本不需要做出的选择的形式,删除了干扰、障碍和困惑。
-Max
分享
点击在 Facebook 上分享(在新窗口中打开) Facebook 点击在 LinkedIn 上分享(在新窗口中打开) LinkedIn 点击在 Hacker News 上分享(在新窗口中打开) Hacker News 点击在 Reddit 上分享(在新窗口中打开) Reddit 点击在 Threads 上分享(在新窗口中打开) Threads 点击在 X 上分享(在新窗口中打开) X
3 条评论
Steven Gordon, PhD 说:
2020年8月7日 下午2:11 在实践中,几乎总是存在对不透明遗留子系统的某种依赖,这使得推理和选择这两个目标都无法完全实现。在理论上是很好的目标,但你需要足够的容错能力,以便不完全满足这些目标不会导致你陷入欺骗性推理或选择策略过于僵化。
就推理而言,我宁愿有一套组织良好的自动化测试来理解,而不是整个代码库。自动化测试使得实际验证你推理所基于的假设变得微不足道,而不是被可能早已离开或隐藏在对不透明遗留子系统依赖中的过于聪明的开发人员所害。
回复
Max Kanat-Alexander 说:
2020年8月8日 晚上11:54 我明白你的意思,但我确实认为,通常情况下,个别开发人员有能力使他们正在处理的系统部分足够简单以便推理。确实,底层平台的复杂性可能使这变得非常具有挑战性,尤其是当它们的设计没有考虑到这些原则时。但在实际应用中,我们可以设计出这些系统的部件能够以合理的准确性被推理的系统。
即使是容错性的重点,理想情况下也涉及对系统进行推理的能力——它在错误条件下的行为。
能够通过测试来推理系统是很好的,这是一个非常有用的工具,我同意。它们在验证你所拥有的推理方面也做得很好,这是真的。但我希望代码本身,当你查看它时,是预期读者能够推理的东西,并且当集成到更大的系统中时,不会向用户呈现他们不需要做出的选择。
-Max
回复
Kursith 说:
2020年10月10日 凌晨12:33 然而现在一切都在自动化。我想知道工程师们做什么?这篇博客对我很有帮助,让我了解到技术是如何在人类创造力的推动下发展的。感谢分享知识。它确实使具有机械知识的软件开发者受益。
回复
发表回复
取消回复
联系
关于 书籍:《理解软件》 书籍:《代码简洁性》
输入你的邮箱… 订阅
Max Kanat-Alexander
11月23日
最近有很多关于如今有多少代码是“由”AI“编写”的讨论。然而,随着时间的推移,我认为这些指标甚至变得比一开始更没有用。当我使用 Claude Code 编写时,我的代码几乎 100% 是“由”Claude“编写”的。然而,这只有在我和代理之间进行大量来回沟通之后才会发生。是的,从理论上讲,Claude“编写”了代码,但绝不是由它自己完成的。我认为我们混淆了“AI自己做了不需要人努力的事情”和“AI帮助人类执行了一项任务”这两个概念。
此外,仅仅看“有多少代码是由AI编写的”,完全无法看出Claude在任何一个特定任务上对我的生产力是净正面还是净负面影响。我曾花了三个小时与Claude来回沟通,生成了50行我本可以在45分钟内自己编写的代码。但另一次,我花了十分钟与Claude交互,完全解决了一个问题,否则我将花费三个小时的研究才能仅仅理解如何解决。Claude在这两种情况下都“编写”了100%的代码。我相信,今天的AI助手正在为软件工程师提供巨大的价值,并且它们正在改变我们的工作方式。但它们并非在所有情况下对所有人都提供同等的价值——有时它们甚至是净负面的。仅仅跟踪“有多少代码是由AI编写的”并不能帮助任何人看到这一点,也无法帮助他们真正弄清楚如何让他们的开发人员更有效率、更有效、更快乐。 阅读更多 347 47 分享
Max Kanat-Alexander
11月3日
我很高兴地宣布,我将于11月20日在纽约市举行的AI工程师代码峰会上发表演讲。过去几年,关于软件工程的未来是什么,存在很多不确定性。在这次演讲中,我将讨论我们可以应用的原则,以确保无论发生什么,那个未来对你的企业来说都是美好的。
希望在那里见到你! 阅读更多 112 13 分享
Max Kanat-Alexander
11月1日
从2011年到2015年左右我们停止定期举行每周会议,我几乎每周都参加Google代码健康小组的周会。这是一个非常棒的会议,有一个几乎总是出席的核心小组,加上每周一些想要就其所在区域的代码质量、测试或重构问题获得帮助的一次性与会者。
大多数时候,这个会议作为一个支持小组,为那些关心代码质量并希望在其所在区域为此做点什么的人服务。他们会来,抱怨他们如何关心但难以让别人也关心。我们会支持他们,说我们真的关心,他们应该坚持下去,然后他们就会回到自己的区域,做出伟大的事情。实际上,令人惊讶的是,那个会议的许多与会者后来成为了谷歌一些最资深的技术负责人。(关于长期职业成功的一个很好的注记:如果你专注于做正确的事情,尽管有反对或困难,从长远来看,这几乎总是对你的职业有利的事情。我们确实在代码健康小组中一次又一次地看到这种情况发生。)
然而,几乎每周,都会有人出现在会议上,问我们同样的问题:“我如何客观地衡量代码复杂性?” 一周又一周,我们会给他们同样的答案:“没有办法做到这一点,请不要这样做。”
这个小组的负责人代表了世界上在代码质量和重构领域经验最丰富的工程师中的一部分,随着时间的推移,我们都得出了相同的结论:代码复杂性度量行不通。所有试图制定定量指标(圈复杂度、统计静态分析失败次数、确定内聚和耦合的算法)的尝试都失败了——它们将团队引向错误的道路,并不能解决真正阻碍开发人员的重要问题。
最终,我意识到为什么这些指标行不通,并且永远不会行得通。这是因为代码“简单”的定义是:易于阅读、理解和正确修改。这里的“阅读”和“理解”本质上是主观的。只有人类才能告诉你某样东西是否易于阅读。
有些人曾希望现代LLM能够在这方面提供帮助。我愿意相信当我看到它时,但现在这实际上是所有模型都不擅长的一个领域:告诉你你是否产出了高质量的输出。它们在发现特定问题并以代码审查的形式提供具体指导方面是没问题的。但它们完全缺乏经验丰富的软件工程师在制作真正简单的软件系统方面所具有的判断力。无论你多么告诉它们“要简单”或“编写好的代码”,它们根本没有能力做到这一点。
所以这仍然是事实:代码质量本质上是主观的,如果你想理解它,你需要关于它的主观数据。这是我见过的唯一有效的方法。 阅读更多 103 24 分享
Max Kanat-Alexander
10月15日
我非常高兴今年能在开发者生产力工程峰会2025上发表主题演讲之一。我谈到了是什么造就了优秀的开发者体验。特别是,这个演讲涵盖了我们在所有开发者体验工作中试图优化的三件核心事情。希望它有所帮助!:) https://lnkd.in/g-AJtJ2b 阅读更多 什么造就了优秀的开发者体验? lnkd.in 8 59 分享
加载更多
© 2025 版权所有。由 The Fox 提供支持。
管理同意 联系 关于 书籍:《理解软件》 书籍:《代码简洁性》
返回顶部