本节中的页面包含基于长期经验的代码审查最佳方法的建议。所有这些页面代表一个完整的文档,分成许多单独的部分。您不必阅读所有这些页面,但是许多人发现阅读整个文档集对他们自己和他们的团队非常有帮助。
另请参见CL Authors Guide,它为正在审查CLS的开发人员提供了详细的指导。
代码审查的主要目的是确保Google代码库的整体代码运行状况随着时间的推移而得到改善。所有代码审查的工具和过程都是为此而设计的。
为了做到这一点,必须平衡一系列的权衡。
首先,开发人员必须能够在他们的任务上取得进展。如果您从不提交对代码库的改进,那么代码库就永远不会改进。此外,如果审阅者使得任何更改都很难进行,那么开发人员就不会有动力在将来进行改进。
另一方面,评审人员的职责是确保每个CL的质量不会随着时间的推移而降低。这可能很棘手,因为随着时间的推移,代码基通常会随着代码健康程度的降低而降低,特别是当团队受到很大的时间限制时,他们觉得必须走捷径才能完成目标。
此外,审查者对他们正在审查的代码拥有所有权和责任。他们希望确保代码库保持一致、可维护性,以及"代码审查中要查找的内容"中提到的所有其他内容。
因此,我们得到以下规则作为我们在代码审查中期望的标准:
一般来说,审查者应该赞成在CL处于一定程度上改善了正在处理的系统的整体代码健康的状态时批准CL,即使CL并不完美。
这是所有代码审查指导原则中的最高原则。
当然,这是有限制的,例如,如果一个CL在他们的系统中添加了一个审查者不想要的特性,那么即使代码设计得很好,审查者也肯定会拒绝批准。
这里的关键点是,没有所谓的"完美"代码——只有更好的代码。评审人员不应要求作者在批准之前打磨CL的每一小块。更确切地说,审核者应该根据他们所建议的更改的重要性来平衡前进的需要。审查者不应追求完美,而应追求持续改进。一个整体上提高了可维护性、可读性系统的可理解性不应该因为它不完美而延迟几天或几周。"
评论者应该随时留下评论,表达一些可以更好的东西,但如果它不是很重要,在它前面加上"nit:"这样的前缀,让作者知道这只是他们可以选择忽略的一个亮点。
注意:本文档中没有任何内容证明在CLS中进行检查会使系统的整体代码运行状况恶化。只有在紧急情况下才会这样做。
代码审查有一个重要的功能,它可以教给开发人员一些关于语言、框架或一般软件设计原则的新知识。留下评论来帮助开发人员学习新东西总是没问题的。随着时间的推移,共享知识是改善系统代码健康的一部分。请记住,如果您的评论纯粹是教育性的,但对满足本文档中描述的标准并不重要,在它前面加上"nit:",或者表明它不是作者必须在这个CL中解析它的。
在代码评审的任何冲突中,第一步都应该是开发人员和评审人员根据本文档的内容以及CL Authors Guide和本评审人员指南中的其他文档达成共识。
当达成共识变得特别困难时,审阅者和作者之间进行面对面的会议或VC会有所帮助,而不是仅仅试图通过代码审阅注释来解决冲突。(如果这样做,请确保将讨论的结果记录在CL上的注释中,以供将来的读者参考。)
如果不能解决问题,最常见的解决方法是升级。通常情况下,升级路径是进行更广泛的团队讨论,让TL参与进来,请代码维护者做出决定,或者请工程师经理帮忙。不要因为作者和审阅者无法达成一致而让CL坐视不管。
注意:在考虑每一点时,一定要考虑到代码审查的标准。
在评审中最重要的事情是CL的总体设计。CL中各种代码的交互是否有意义?这个更改是属于您的代码库,还是属于库?它是否与您系统的其他部分集成良好?现在是添加此功能的好时机吗?
这个CL做了开发人员想要做的事情吗?开发人员想要给这个代码的用户带来什么好处吗?"用户"通常既是最终用户(当他们受到更改的影响时),也是开发人员(将来必须"使用"这个代码)。
大多数情况下,我们希望开发人员能够很好地测试CLS,以便他们在进行代码审查时能够正确地工作。但是,作为审查者,您仍然应该考虑边缘情况,查找并发问题,尝试像用户一样进行思考,并确保仅通过阅读代码就可以看到没有错误。
如果你愿意的话,你可以验证CL——对于审查者来说,检查CL的行为最重要的时候是当它具有面向用户的影响时,比如UI更改。当你只是阅读代码时,很难理解一些更改将如何影响用户。对于这样的更改,如果太不方便在CL中打补丁并亲自尝试,你可以让开发人员给你一个功能演示。
另一个在代码审查期间考虑功能特别重要的时候是,是否有某种并行编程正在进行。在CL中,理论上可能导致死锁或争用条件。这些类型的问题很难通过运行代码来检测,通常需要有人(开发人员和审查人员)。仔细地想一想,以确保问题不会被引入。(请注意,这也是在可能出现争用条件或死锁的情况下不使用并发模型的一个很好的理由—这样做可能会非常复杂。代码检查或理解代码。
CL是否比它应该的更复杂?检查CL的每一层——是不是个别行太复杂了?是不是函数太复杂了?是不是类太复杂了?“太复杂"通常意味着"代码读者不能很快理解”。它也可以意味着"开发人员在试图调用或修改这些代码时可能会引入错误。"
一种特殊类型的复杂性是过度工程,开发人员已经使代码比它需要的更通用。或者添加了系统目前不需要的功能。评论者应该特别警惕过度设计。鼓励开发人员解决他们知道现在需要解决的问题。而不是开发人员推测未来可能需要解决的问题。未来的问题,应该是解决一旦它到达,你可以看到它的实际形状和要求,在物理宇宙。
根据变更要求进行单元、集成或端到端测试。通常,测试应该与生产代码添加在同一个CL中,除非CL正在处理紧急情况。
确保CL中的测试是正确的、合理的和有用的。测试本身不进行测试,而且我们很少为我们的测试编写测试—人类必须确保测试是有效的。
当代码被破坏时,这些测试真的会失败吗?如果代码在它们下面更改,它们会开始产生误报吗?每个测试是否都会产生简单而有用的断言?这些测试是否在不同的测试方法之间适当地分离?
请记住,测试也是必须维护的代码,不要因为测试不是主二进制的一部分就接受测试的复杂性。
开发人员是否为每一项都选择了好的名称?一个好的名称足够长,可以充分地传达该项是什么或做什么,而不会太长而变得难以阅读。
开发人员是否用可理解的英语写了清晰的注释?所有的注释都是必需的吗?通常,注释在解释某些代码存在的原因时是有用的,而不应该解释某些代码正在做什么。如果代码不够清楚,不能解释它自己,那么代码应该变得更简单。也有一些例外(正则表达式和复杂的算法通常从解释它们所做的事情的注释中获益良多)。例如但是大多数注释是为了获取代码本身不可能包含的信息,比如决策背后的推理。
它也可以是有帮助的,看看意见,在此之前的CL。也许有一个TODO,可以删除现在,一个意见,反对这一变化正在作出的,等等。
请注意,注释与类、模块或函数的文档不同,后者应该表达一段代码的用途、应该如何使用以及使用时的行为。
我们有谷歌的风格指南,适用于所有的主要语言,甚至大多数的小语种。确保CL遵循适当的风格指南。
如果你想改进一些在风格指南中没有的风格点,在你的注释前加上"nit:",让开发人员知道这是一个你认为可以改进代码的挑剔的地方,但不是强制性的。不要仅仅根据个人的风格偏好来阻止提交CLS。
CL的作者不应该将主要的样式更改与其他更改结合在一起。这会使您很难看到CL中的更改内容,使合并和回滚更加复杂,并导致其他问题。例如,如果作者想要重新格式化整个文件,让他们只将重新格式化的内容作为一个CL发送给您,然后再发送另一个CL及其功能更改。
如果CL更改了用户生成、测试、与代码交互或发布代码的方式,请检查它是否也更新了相关文档,包括READMES、G3DOC页面和任何生成的参考文档。如果CL删除或不推荐代码,请考虑是否也应删除文档。如果缺少文档,请询问它。
看看你被分配审查的每一行代码。有些东西,比如数据文件、生成的代码或大型数据结构,你有时可以扫描,但不能扫描人类编写的类、函数或代码块,并认为它们里面的东西是好的。显然,有些代码比其他代码更值得仔细检查——这是你必须做出的判断——但你至少应该确保自己理解所有代码在做什么。
如果你很难读懂代码,而这又减慢了审查的速度,那么你应该让开发人员知道这一点,并在你试图审查之前等待他们澄清。在谷歌,我们雇佣了优秀的软件工程师,而你就是其中之一。如果你不能理解代码,其他开发人员也很可能不会理解。所以当你要求开发人员澄清代码时,你也在帮助未来的开发人员理解代码。
如果您理解代码,但不认为有资格进行某些部分的审查,那么请确保CL上有一个合格的审查者,特别是在安全性、并发性、可访问性、国际化等复杂问题上。
在广泛的上下文中查看CL通常是有帮助的。通常,代码审查工具只会向您显示正在更改的部分周围的几行代码。有时,您必须查看整个文件,以确保更改确实有意义。例如,您可能只看到添加了四行新代码,但当您查看整个文件时,您会看到这四行代码位于一个50行的方法中,现在确实需要将其拆分为更小的方法。
在整个系统的上下文中考虑CL也是有用的。这种CL是改善了系统的代码健康,还是使整个系统变得更复杂、测试更少等等?不接受降低系统代码健康的CL。大多数系统通过许多累积的小更改而变得复杂,因此重要的是防止新更改中出现哪怕是很小的复杂性。
如果你在CL中看到了一些好的东西,告诉开发人员,特别是当他们以一种很好的方式处理你的评论时。代码审查通常只关注错误,但他们也应该对好的实践给予鼓励和赞赏。从指导的角度来说,告诉开发人员他们做对了什么比告诉他们做错了什么更有价值。
在进行代码审查时,您应该确保:
确保检查要求您检查的每一行代码,查看上下文,确保您正在改进代码的健康状况,并称赞开发人员所做的好的事情。
既然您知道了要查找的内容,那么管理分散在多个文件中的审查的最有效方法是什么?
看看CL的描述和CL通常做的事情。这个改变有意义吗?如果这个改变本来不应该发生,请立即解释为什么这个改变不应该发生。当你拒绝这样的改变时,向开发者建议他们应该做什么也是一个好主意。
例如,您可能会说:“看起来您在这方面做了一些很好的工作,谢谢!但是,我们实际上正在朝着删除您正在修改的FoowIdGet系统的方向前进,因此我们现在不想对它进行任何新的修改。您是否应该重构我们新的BarWidget类?”
请注意,评审人员不仅拒绝当前的CL并提供了替代建议,而且他们还礼貌地提供了替代建议。这种礼貌非常重要,因为我们希望表明,作为开发人员,我们相互尊重,即使我们不同意。
如果你得到了很多你不想做的变更,你应该考虑重新工作你团队的开发过程或者外部贡献者的发布过程,这样在写CLS之前有更多的沟通。在人们做了大量现在不得不扔掉或彻底重写的工作之前告诉他们"不"是更好的。
找到这个CL的"主要"部分的文件。通常,有一个文件具有最多的逻辑更改,它是CL的主要部分。首先查看这些主要部分。这有助于给出CL的所有较小部分的上下文,通常可以加快代码审查。如果CL太大,您无法确定哪些部分是主要部分,请询问开发人员您应该首先查看哪些内容,或者要求他们将CL分成多个CLS。
如果您发现CL的这一部分存在一些主要的设计问题,那么您应该立即发送这些注释,即使您现在没有时间查看CL的其余部分。实际上,查看CL的其余部分可能是浪费时间,因为如果设计问题足够严重,那么很多其他正在查看的代码就会消失,而且也不重要了。
有两个重要的原因需要立即发送这些主要的设计意见:
一旦你确认CL整体上没有大的设计问题,试着找出一个逻辑顺序来浏览这些文件,同时确保你不会错过任何一个文件。通常在你看完主要文件后,最简单的方法是按照代码审查工具给你的顺序浏览每个文件。有时在你读主要代码之前先读测试也是有帮助的。因为这样你就知道改变应该是什么了。
在谷歌,我们优化的是一个开发团队共同开发产品的速度,而不是单个开发人员编写代码的速度。单个开发的速度很重要,但它没有整个团队的速度重要。
当代码审查速度较慢时,会发生以下几种情况:
如果你不是在一个集中的任务,你应该做一个代码审查后不久,它来了。
一个工作日是响应代码审查请求所需的最长时间(即第二天早上的第一件事)。
遵循这些指导方针意味着典型的CL应该在一天内进行多轮评审(如果需要的话)。
有一次,对个人速度的考虑超过了对团队速度的考虑。如果你正在进行一项专注的任务,比如写代码,不要打断自己去做代码审查。研究表明,一个开发人员在被打断后可能需要很长时间才能回到开发的平稳流程中。因此,对团队来说,在编码时打断自己实际上比让另一个开发人员等待代码审查更昂贵。
相反,在你的工作中等待一个突破点,然后再回复你的审查请求。这可能是当你当前的编码任务完成后,午饭后,从会议回来后,从微型厨房回来后等等。
当我们谈到代码审查的速度时,我们关心的是响应时间,而不是CL完成整个审查并提交所需的时间。理想情况下,整个过程也应该很快,但单个响应的速度比整个过程的速度更重要。
即使有时要花很长时间才能完成整个评审过程,但在整个过程中,评审人员的快速响应大大减轻了开发人员对"慢"代码评审的挫折感。
如果您太忙而不能在收到CL时对其进行全面审查,您仍然可以发送一个快速响应,让开发人员知道您什么时候会得到它,建议其他可能能够更快响应的审阅者,或者提供一些初步的广泛评论。(注意:这并不意味着您应该中断编码,即使是发送这样的响应—在您工作中的一个合理的断点发送响应。)
重要的是,审核员要花足够的时间进行审核,以确保他们的"LGTM"意味着"此代码符合我们的标准"。但是,理想情况下,每个人的反应都应该很快。
在处理时区差异时,尽量在作者还在办公室的时候联系他们。如果他们已经回家了,那么在他们第二天回到办公室之前,尽量确保你的评论已经完成了。
为了加速代码审查,在某些情况下,即使审查者在CL上留下了未解决的注释,也应该给予LGTM/Approval。这是在以下情况下执行的:
如果不清楚,审阅者应该指定他们想要的这些选项。
当开发人员和审阅者处于不同的时区时,带注释的LGTM特别值得考虑,否则开发人员将等待一整天才能获得"LGTM,Approval"。
如果有人发送给你一个很大的代码审查,以至于你不确定什么时候才能有时间审查它,你的典型反应应该是要求开发人员将CL分成几个较小的CL,它们相互构建,而不是一个很大的CL必须一次审查完。这通常是可能的,并且对审查者非常有帮助,即使它需要开发人员额外的工作。
如果一个CL不能分解成更小的CL,而你又没有时间快速地检查整个CL,那么至少要对CL的总体设计写一些注释,并将其返回给开发人员以进行改进。作为一个审查者,你的目标之一应该是始终解除开发人员的阻塞,或者使他们能够快速地采取某种进一步的行动,而不会牺牲代码的健康状况来这样做。
如果您遵循这些指导原则并且对代码评审非常严格,您应该会发现,随着时间的推移,整个代码审查过程会变得越来越快。开发人员了解健康代码所需的内容,并从一开始就向您发送CLS,这非常好,只需要越来越少的审查时间。审核员要学会快速响应,不要在审核过程中增加不必要的延迟。但不要为了想象中的速度改进而在代码审查标准或质量上妥协——这实际上不会使任何事情发生。更快,从长远来看。
还有一些紧急情况,CLS必须非常快速地通过整个审查过程,而且质量指导方针会放松。但是,请查看什么是紧急情况?对于哪些情况实际上符合紧急情况的描述,哪些不符合。
一般来说,保持礼貌和尊重是很重要的,同时也要对你正在审查的代码的开发人员非常清楚和有帮助。这样做的一个方法是确保你总是对代码发表评论,而从不对开发人员发表评论。你不必总是遵循这种做法,但当你说一些可能令人不安或有争议的事情时,你一定要使用它。例如:
坏:“为什么你在这里使用线程时,显然没有任何好处可以从并发获得?”
好:“这里的并发模型增加了系统的复杂性,但没有任何实际的性能优势。因为没有性能优势,所以最好的方法是使用单线程而不是多线程。”
关于上面的"好"的例子,你会注意到一件事,它帮助开发人员理解你为什么要做评论。你不需要总是在你的评论中包含这些信息,但有时候,对你的意图、你遵循的最佳实践或你的建议如何改善代码健康给出更多解释是合适的。
一般来说,修复CL是开发人员的责任,而不是审查者的责任。您不需要为开发人员进行详细的解决方案设计或编写代码。
不过,这并不意味着审查者不会提供帮助。一般来说,你应该在指出问题和提供直接指导之间取得适当的平衡。指出问题并让开发人员做出决定通常有助于开发人员学习,并使代码审查变得更容易。它还可以产生更好的解决方案,因为开发人员比审查者更接近代码。
然而,有时直接的指令、建议甚至代码更有帮助。代码审查的主要目标是尽可能获得最好的CL。次要目标是提高开发人员的技能,这样他们需要的审查就会越来越少。
如果你要求开发人员解释一段你不理解的代码,这通常会导致他们更清晰地重写代码。偶尔,在代码中添加注释也是一种适当的回应,只要它不仅仅是解释过于复杂的代码。
仅在代码审查工具中编写的解释对将来的代码读者没有帮助,只有在少数情况下才可以接受,例如,当您在审查一个您不太熟悉的领域时,开发人员会解释一些普通代码读者已经知道的内容。
有时,开发人员会对代码审查进行反驳,他们要么不同意你的建议,要么抱怨你总体上太严格了。
谁是对的?谁是对的
当开发人员不同意您的建议时,首先考虑一下他们是否正确。通常,他们比您更接近代码,因此他们可能对代码的某些方面有更好的见解。他们的论点有意义吗?从代码健康的角度来看有意义吗?如果有,让他们知道他们是正确的,让问题消失。
然而,开发人员并不总是正确的。在这种情况下,审阅者应该进一步解释为什么他们认为他们的建议是正确的。一个好的解释可以证明对开发人员的答复的理解,以及关于为什么要求更改的额外信息。
特别是,当审查者认为他们的建议将改善代码的健康状况时,他们应该继续倡导更改,如果他们认为由此产生的代码质量改进证明了所要求的额外工作是合理的。
有时候需要几个回合的解释才能让你真正理解一个建议。只是要确保总是保持礼貌,让开发人员知道你听到了他们说的话,你只是不同意。
审查者有时认为,如果审查者坚持要改进,开发人员会感到不安。有时开发人员确实会感到不安,但它通常是简短的,他们变得非常感谢你后来帮助他们提高了他们的代码质量。通常情况下,如果你在评论中保持礼貌,开发人员实际上根本不会变得不安,而担心只是审阅者的想法。令人不快的地方通常更多的是注释的编写方式,而不是审查者对代码质量的坚持。
推回的一个常见来源是开发人员(这是可以理解的)希望完成任务。他们不想通过另一轮的审查,只是得到这个氯在。所以他们说他们会在以后的CL中清理一些东西,因此你现在应该LGTM这个CL。有些开发人员对此很在行,他们会立即编写一个后续的CL来修复这个问题。但是,经验表明,随着开发人员编写原始CL后的时间越长,这种清理就越不可能发生。实际上,通常情况下,除非开发人员在当前CL之后立即进行清理,否则这种情况永远不会发生。这并不是因为开发者不负责任,而是因为他们有很多工作要做,而清理工作在其他工作的压力下丢失或被遗忘了。因此,通常最好坚持让开发人员现在就清理他们的CL,在代码进入代码库并完成之前。让人们"事后清理"是代码基退化的一种常见方式。
如果CL引入了新的复杂性,除非是紧急情况,否则必须在提交之前对其进行清理。如果CL暴露了周围的问题,而这些问题现在无法解决,开发人员应该为清理工作归档一个bug,并将其分配给他们自己,这样它就不会丢失。他们还可以选择在引用归档的bug的代码中编写TODO注释。
如果您以前的代码审查相当宽松,而现在改为严格审查,一些开发人员会大声抱怨。提高代码审查的速度通常会使这些抱怨消失。
有时候,这些抱怨可能需要几个月的时间才能消失,但最终,开发人员往往会看到严格的代码审查的价值,因为他们看到了他们帮助生成的伟大代码。有时候,一旦发生了什么事情,让他们真正看到了你通过严格而增加的价值,最响亮的抗议者甚至会成为你最坚定的支持者。
如果您遵循上述所有方法,但仍然遇到无法解决的您与开发人员之间的冲突,请参阅代码审查标准,了解有助于解决冲突的指导方针和原则。