作为开发者,我们都非常熟悉代码审查。让另一双眼睛看一下我们的代码是非常好的;它向我们展示了我们的代码的许多方面,否则我们不会注意到。代码审查可以提供信息,也可以起到教育作用。我可以自信地将我所知道的关于良好编程实践的大部分内容归功于代码审查。
被审查者从代码审查中获得的学习量取决于审查的效果如何。因此,审查员有责任在审查过程中尽可能多地吸取经验教训,使他们的审查有价值。
这是一份关于你在审查中应该检查的代码的一些重要方面的指南,你应该从这些检查中得到的期望,以及一些关于工具(如linters、formatters和测试套件)如何帮助简化流程的想法。
代码审查检查表
我们以检查表的形式制定本指南。它列出了一组你需要询问的关于代码的问题。如果其中任何一个问题的答案不是 "是",你应该在PR上留下一个注释。
请注意,这个清单只是作为一个指导原则。你的代码库,就像每一个代码库一样,有它自己特定的需求,所以你可以在这个指南的基础上自由发挥,覆盖其中不适合你的使用情况的部分。
礼节
在审查期间,首先要检查的是PR对基本礼仪的遵守程度。好的PR是由一点一滴的变化组成的,并解决一个定义明确的问题。它们应该是有重点的,并有目的地缩小范围,尽可能地减少合并冲突。简单地说,一个好的PR可以促进被审查。
对于大规模的修改,要建立一个单独的目标分支,然后对该目标分支进行小规模的增量PR,最后将目标分支与主分支合并。做一个巨大的PR会使它更难被审查,而且如果它变质了,可能会出现许多合并冲突。
在审查新开发者的PR时,我也会注意确保他们的提交信息写得很好。
检查表。
- 该PR是原子性的吗?
- 公报是否遵循单一关注原则?
- 提交信息写得好吗?
想法。
在团队中强制执行一种提交信息格式。例如,你可以尝试gitmoji,在提交信息中使用表情符号。例如,错误修复应该以[FIX] ,或🐛表情符号开始,而新功能应该以[FEAT] ,或✨表情符号开始。这使得提交的意图非常明确。
功能和语法
接下来要检查的是PR是否有效,即它是否能工作。PR所改变的代码应该能按预期工作。一个错误修复应该解决它应该修复的错误。一个特性应该提供所需的额外功能,而不会破坏其他东西。
要记住的一件重要事情是,PR所增加的任何新功能都是合理的。确保这一点的一个简单方法是只接受与已经分流的问题相关的PR。这种做法可以最大限度地减少功能爬行。
检查表。
- 该PR是否有效?
- 新功能是否增加了价值,或者是功能恶化的标志?
- PR是否为修改后的代码添加了测试用例?
想法。
在代码中加入全面的测试,可以更容易地检查新功能的工作情况,而PR则更难破坏现有的东西。覆盖的那部分代码不应该在PR中消失。任何新的代码都应该在单元/功能测试中完全覆盖。Python有一个强大的单元测试框架,内置于语言本身。你应该在你的代码库中使用它。
设计
一旦我们确定代码可以工作,下一步就是检查它与现有代码库的整合程度。在这方面,需要检查的一个关键是重复性。通常情况下,添加到 repo 中的代码提供的功能可能已经是代码的一部分,在不同的位置,或者是由框架或 Pip 包提供的东西在使用。这方面的知识只有通过经验才能获得。作为一个资深者,你有责任向为你的 repo 做出贡献的新开发者指出这种重复。
对范式的关注也是必不可少的。许多项目都有预先采用的设计范式,如微服务、Mono repo、或云计算。任何传入的代码都应该与这些范式保持一致。
检查表。
- 代码的规划和设计是否合理?
- 代码是否能与现有的代码很好地配合,而不增加重复?
- 代码在组件的放置方面是否有条理?
模式和习语
Python是一种非常成熟的语言。它是在某些哲学的基础上驱动的,被称为Python的禅。这些理念产生了许多惯例和习惯,新的代码应该遵循这些惯例和习惯。
习惯性代码和非习惯性代码之间的区别是非常微妙的,在大多数情况下,只能凭直觉来判断。就像所有通过经验磨练出来的东西一样,直觉必须从有经验的人,比如你自己,转移到像你的被审查者这样的新手身上。
检查表。
- 代码是否符合该语言的习语和代码模式?
- 代码是否利用了语言的特性和标准库?
想法。
大多数提示器,特别是PyLint,可以帮助你识别与风格指南的偏差,在大多数情况下,甚至可以自动修复它们。Linters的工作速度非常快,可以对代码进行实时修正,使它们成为你的工具链的一个有价值的补充。
可读性
Python被广泛认为是一种非常可读的语言。Python 语法的简单性和标点符号的减少对其可读性有很大贡献。用这种语言编写的代码也是可读的,这是有道理的。
检查表。
- 代码是否清晰、简明?
- 它是否符合PEP-8的要求?
- 是否遵循了所有的语言和项目惯例?
- 是否给标识符起了有意义的、符合风格指南的名字?
想法。
像Black这样的好的代码格式化器可以在格式化代码的一致性和可读性方面有很大帮助。Black还提供了最小的定制,这很好,因为它消除了所有形式的自行车脱落现象。
我们之前谈到过,将Black集成到你的CI管道中,在代码审查时可以创造奇迹。
文档和可维护性
接下来要检查的是代码的可维护性。任何由PR增加或改变的代码都应该是为了方便原作者以外的人维护它而写的。
它最好是自带文档的,也就是写得让任何阅读代码的人都能理解它的作用。这是好的 Python 代码的标志之一。如果代码在设计上必须是复杂的,就应该有充分的文档。在一个理想的世界里,所有的类和函数都会有Python的文档说明,并有完整的例子。
检查表。
- 代码是自文档化的还是文档化的?
- 代码是否没有混淆和不必要的复杂性?
- 控制流和组件关系是否清晰易懂?
想法。
Sphinx是一个文档生成器,可以从Python文档串中导出漂亮的文档。然后,导出的文档可以上传到ReadTheDocs,一个流行的文档托管工具。Sphinx是我绝对喜欢写文档的主要原因之一。
安全性
确保应用程序的安全是至关重要的。接下来要检查的是PR是否保持或提高了项目的安全性。你需要确保这些变化不会增加攻击面或暴露出漏洞。如果PR增加了新的依赖关系,它们有可能是不安全的,在这种情况下,你可能需要检查已知漏洞的版本,并在必要时更新这些依赖关系。
检查清单。
- 代码是否没有可能被利用的实现错误?
- 是否对所有新的依赖关系进行了漏洞审计?
想法。
著名的Python安全分析器之一是Bandit。另外,如果你使用GitHub来托管代码,你绝对应该阅读这个关于为你的代码库设置漏洞检测和Dependabot的指南。
一旦你在GitHub上设置了漏洞检测,你会收到这样的通知...
...包括漏洞的细节和你的仓库中的PR,你可以合并它们来打补丁。
我们最近还谈到了Python开发中常见的安全隐患,以及如何保护你的项目免受其害。
性能、可靠性和可扩展性
最后要检查的是代码在规模上的性能和可靠性。虽然这些无疑是关键指标,但我把它们放在了检查表的底部,因为我相信计划周密、设计合理、编写精良的代码通常也能很好地工作。
检查表。
- 代码是否在时间和空间的复杂性方面进行了优化?
- 它是否根据需要进行扩展?
- 它是否有工具性,如指标报告和故障警报?
想法。
为Python增加一些可靠性的一个很好的方法是使用类型提示和静态类型检查,它可以在运行时之前提供可能的错误提示。Python对类型提示的新的本地支持主要是受Mypy语法的启发,可以在现有项目中逐步采用。
DeepSource
DeepSource是一个自动代码审查工具,它管理端到端的代码扫描过程,并在新的提交或新的拉动请求被推送时自动提出修复请求。
为Python设置DeepSource是一个快速、简单、不费力的过程。只需在 repo 的根目录下添加一个.deepsource.toml 文件,DeepSource 就会立即将其拾起进行扫描。扫描将在你的代码库中找到改进的范围,进行这些改进,并为它发现的变化打开拉动请求。我被这种简单的设置和他们自建的代码引擎的功效所震撼。
🙋♂️**小知识:**你知道DeepSource在OWASP策划的Python源代码分析工具列表中占有重要地位吗?
理想的代码审查
回到我关于代码审查是教育性和信息性的观点,我还想补充一点,代码审查是耗时和资源密集型的。虽然每次审查都需要时间,但更深入和全面的审查会消耗更多时间。
因此,每次审查都必须尽可能地富有成效。这就是所有工具和自动化努力的方向。通过自动化任何可以自动化的东西,通常是审查的平凡部分,如代码风格和格式,我们可以让开发人员专注于重要的东西,如架构、设计和可扩展性。
我想用我的导师与我分享的一个深刻的想法离开你。
一个好的代码审查不仅能改善代码,也能改善编码者。