为了防止「头铁」群众真的去重构它,在这段代码的开头,有这样一段注释:
不要尝试简化此代码。
请看着它起飞。
这个控制器是故意以非常详细的方式编写的。你会注意到:
1. 每个『if』语句都有一个匹配的『else』(除了客户端 API 调用的简单错误检查)
2. 在这里我们要明确地解释一下
我们把这种代码叫做『航天飞机风格』。航天飞机风格是为了确保每个分支和条件都得到考虑——就像在 NASA 编写航天飞机的航天器应用程序代码一样。
最初,这个控制器的工作被分成了三个控制器来做。该控制器是为简化 PV 子系统付出巨大努力的结果。在这种努力中,很明显我们需要确保在代码中处理和计算每个条件,即使它导致了无操作代码分支。
因此,控制器代码可能看起来过于冗长,有太多注释和「分叉」。但是,这里记录了大量的业务知识和内容,以确保未来的维护者能够正确地推理绑定行为的复杂性。因此,对此文件的更改应保持航天飞机风格。
该文件的 GitHub 地址:https://github.com/kubernetes/kubernetes/blob/ec2e767e59395376fa191d7c56a74f53936b7653/pkg/controller/volume/persistentvolume/pv_controller.go
1700 行的 if 嵌套语句。
作为流行的开源系统,Kubernetes 常被用于自动容器化应用程序的部署和管理。其开发者称,Kubernetes 拥有 15 年承担 Google 生产工作负载的经验。对于人工智能领域的开发者来说,Kubernetes 可以帮助我们部署深度学习模型(参见:如何使用 Kubernetes 轻松部署深度学习模型)。
航天飞机的风格
听说它是美国航天局 NASA 的代码风格?在 Hackernews 上,众多网友们在通读全文代码后给出了一致好评。
@Klathmon 表示:
我爱死这个了!这简直就是软件开发里的「爵士乐」。它打破了所有的固有「规则」,但目的明确,比按照「规则」来的结果要好得多。
乍一看,你会觉得这个文件太大了,里面充斥着许多分支和嵌套 if 语句,还有很多「无意义的注释」,仅描述周围一行或几行代码是做什么的。注释里还有很多 logic,与实际代码相比,这些 logic 很快就会过时或者出错。
但同时,这样做使得维护和管理代码变得简单多了,因为你不用把 logic 拆分成几十甚至上百个文件。它包含了大量对这个文件需要进行的固有复杂工作,而它的注释做得又好又多,做任何改变都可以轻易让注释一起更新。
@munchbunny 说:
不能更同意了。我日常要处理的代码不是这个级别,但也有低级别的特点,同样也比典型的后端代码更接近 metal,被许多的前端和后端代码调用。如果有「甚至更后端代码」的东西,这就是个不错的例子。如果你忽略了一个细微的差别,一群愤怒的开发人员会在部署代码时冲到你办公桌前,而如果你没有快速修复它,就会出现一群愤怒的顾客。
对于任务关键型代码内部循环中的代码来说,主要的时间成本并不是写代码或者理解代码,而是根据所服务场景的细微差别进行优化,检查更改代码带来的直接影响,然后检查更改的二阶和三阶影响。
当你的代码非常细化,以至于你做的每个改变都有三阶影响时,带有精心注释的代码会更容易维护。因为假如你拼凑了十个不同的文件,需要花费精力弄清正确的路径,就更有可能忽略其中的细微差别。
关于注释
很多网友对于带大量注释的代码表示很有用、不反感,并且那些不带注释的代码会带来很大的麻烦。
@EB66 说:
我完全同意。对于那些不可避免很复杂的代码,我也喜欢这种风格。
我非常喜欢简洁且语句/命名是表达性的代码,但有时候注释是必要的,以便清楚地阐明逻辑或使用案例。表达性代码能直接传达的含义有限。精心设计的注释可以大大地减少其他开发人员在不熟悉的代码基础上所耗费的时间。
关键是要更新注释。没有比不准确的注释更恶心的事了。检查代码时一定要检查修改过的代码行的注释。
@ascar 说:
解释使用案例或目的的过时注释还是比没有注释强。它能给你提供背景信息,比如代码的演变及其目的。
只读注释可能比读没有注释的代码更糟糕,所以一些开发人员反感过时的注释,因此就有了一般化(过于简略)的注释。
注释是关于代码的附加信息,没有事实来源,所以,要同时阅读代码和注释。
@nickharr 说:
我有 25 年以上用多种语言编写、查看、注释和检查代码的经验,所以我会说这的确是一个好东西——不管编程的「风格」(或者广义上来说的语言)如何。
好的代码注释可以对生产力产生巨大的影响——无论是对个人、团队还是企业来说都是如此。它有助于存储知识(当前和之前的团队/个人之间容易丢失的信息)。
我个人花了太多时间对那些没有注释的代码进行逆向工程。有时候,经验丰富的程序员或开发人员会走捷径:压缩程序和函数,这些程序和函数是基于他们明确了解的语言和/或领域,但没有对它们进行注解……
在基本层面上,注释应该告知、教育、概述和帮助他人理解我们写代码时创造的有时很复杂的路径和函数。
有些人认为好的代码不需要注释,某种程度上这是对的,但这一点并不适用于每个代码库。有些代码很复杂、笨拙,就像意大利面,有时甚至难以理解。
我一直努力教经验较少的开发人员以带点幽默(可能的话)的方式作出高效的好注释——让我们能够快速理解代码,欣赏前人所做的努力,并笑对复杂的挑战。
我个人并不真正关心代码和注释的比率。有时,代码注释可能比代码本身更有价值。而有时,它们只是帮助你完成工作;只要快速、高效、没毛病,就是不错的代码。
文件大小
@Klathmon 说:
我从事前端网页应用开发,其中最复杂的问题在于如何实现代码库的快速迭代,大部分情况下都不是实际的业务问题。但尽管「deep functionality and small interfaces」听起来很不错,大多数情况下,导出的仅有少量函数的大型文件是不容易维护的。将所有代码放到同一个文件中,需要你在做出任何修改之前确保透彻地理解它们。
@taneq 说:
他们是有目的地这样做的,为了确保开发人员在修改之前能理解整个文件,因为其功能非常关键,很容易被混淆。此外,将一个逻辑严密的文件分割成小型文件(而不是改写成更小的逻辑模块)只会导致不必要的文件管理问题。
新手的膜拜
@jml7c5 说:
作为一个初学程序员,我非常震惊这竟然不是编程的标准惯例。一般的源文件应该提供语境、主题背景、解释概念的参考资料/博客/书籍指南、关于代码如何匹配程序整体的信息,以及(最重要的)文件中体现的思考过程(即为什么选择这种做法而不是其它,面临的挑战,权衡等等)。
我还是想不通,每一位程序员都需要从零开始学习使用一个代码库。除非肯花费数小时来做测试,想改善一个 non-trivial 的程序是不可能的。致开源开发者:如果你想要让人们对项目做出贡献,这些类型的注释是很基本的,更不要说它们能帮助节省非常多的时间。
@qlk1123 说:
「作为一个初学程序员,我非常震惊这竟然不是编程的标准惯例。」我同意这一点,如果这能成为社区标准,当然会有很多人受益。但是,你不认为一个好的社区文化可以让人们为此保持良好的习惯吗?
我的日常工作是 Linux 内核开发人员。我发现源代码仅仅是「What」部分,注释中应该声明「How/Why」部分。并且如果这些还不能使我搞明白源代码,我会直接电邮作者问更深层次的「Why」部分。大多数情况下信息都是足够的。
模仿需谨慎
Kubernetes 之外,最近另一个因为代码而被热议的程序可能就是在 Steam 上火起来的独立国产游戏《太吾绘卷》了。据好奇的程序员介绍,其早期版本中内含数千个 if 语句。由于游戏主创是中文系出身,随后又投身建筑行业,半路出家写游戏代码——最后居然玩起来没有什么大问题,代码的「糟糕」样式成为了一个梗,为游戏的流行起到了助推作用。
这些著名的案例虽然可以运行,但没有强大的逻辑(运气)是玩不转的。科班出身的程序员们一般就不要模仿了。