1.3.3 最小惊讶原则
意图不明是很糟糕的,但还有一种沟通更糟糕:代码让未来的合作者感到惊讶。你要坚持最小惊讶原则:当人们阅读代码库时,他们几乎不应该对行为或实现感到惊讶(当他们感到惊讶时,应该在代码附近有很好的注释来解释为什么要这样)。这就是为什么沟通意图是至关重要的。清晰、整洁的代码可以减少可能出现的误解。
最小惊讶原则指出程序应该总是以最不让用户感到惊讶的方式回应用户[3]。令人惊讶的行为会导致困惑,困惑会导致错误的假设,错误的假设导致了bug。这就是你得到不可靠的软件的原因。
请记住,即使是你编写的代码完全正确,将来仍然可能会让别人感到惊讶。在我的职业生涯早期,有一个令人讨厌的bug,由于内存损坏而崩溃。把代码放在调试器下或放入太多的print语句会影响时间,这样bug就不会显示(一个真正的“heisenbug”[4])。有成千上万的代码与这个错误相关。
所以我必须手动将代码一分为二,通过分别删除一半代码,看看哪一半确实发生了崩溃。经过两周的折腾,我终于决定检查一个听起来无关痛痒的函数getEvent。事实证明,这个函数实际上设置了一个带有无效数据的事件。不用说,我很惊讶。这个函数在它所做的事情上是完全正确的,但是因为我忽略了代码的意图,我忽略了这个错误至少三天。让你的合作者惊讶会浪费他们很多时间。
很多惊讶都来自复杂性。复杂性有两种类型:必然的复杂性和偶然的复杂性。必然的复杂性是在你的领域内固有的复杂性。深度学习模型必然是复杂的,它们不是你可以在几分钟内浏览和理解其工作原理的东西。优化对象关系映射(ORM)必然是复杂的,必须考虑大量可能的用户输入。你将无法消除必然的复杂性,所以你最好的选择是尝试控制它,以免它蔓延到你的代码库,并最终成为偶然的复杂性。
相反,偶然的复杂性是在代码中产生多余、浪费或混乱语句的复杂性。它是当系统随着时间的推移而演进,开发人员在没有重新评估旧代码以确定其原始断言是否仍然正确时出现的情况。我曾经参与过一个项目,添加一个命令行选项(以及通过编程设置它的相关方法),涉及的文件不少于10个。为什么添加一个简单的值就需要改变整个代码库?
以下情况都属于偶然的复杂性。
•听起来简单的事情(添加用户、更改UI控件等)实现起来并非易事。
•让新开发人员理解你的代码库很难。项目中的新开发人员是你的代码可维护性的最佳指示器——不需要等待数年。
•对添加功能所需的时间估算总是很长,但你还是不能够及时完成。
排除偶然的复杂性,尽可能地隔离必然的复杂性。因为它们会成为你未来合作者的绊脚石。这些复杂性的来源会加剧误解,它们让整个代码库的意图变得模糊不清。
讨论
你的代码库中的偶然的复杂性是什么?如果你被丢进代码库而不能与其他开发人员交流,理解简单的概念会有多大的挑战?如何简化本练习中确定的复杂性(特别是在经常变化的代码中)?
在本书的其余部分中,我将探讨在Python中沟通意图的不同技术。