小 CL

为什么应该写小 CL?

小 CL 有如下优点:

  • 相对让审核者单独拿出30分钟审核大 CL,不如让他花费几个5分钟审核代码。对审核者而言,后者更容易。

  • 审核更彻底。 发生较大修改时,往往会反复审核、修改,审核者与开发者经常会因为过多的反复而在情绪上受到影响,以致于把精力放在修改上了,却忽略了 CL 中更重要的部分。

  • 引入新缺陷的可能性更小。 如果修改的内容比较少,自然审核人的效率会更高,开发者与审核者都更容易判断是否引入了新的缺陷。

  • 如果被拒绝,浪费的时间更少。 如果开发者花费了很大的精力开发了一个大 CL,直到审核的时候才知道整个开发的方向错了,那么之前的所有时间就全浪费了。

  • 更容易合并代码。 大 CL 在合并代码时会花费很长的时间,在合并时需要花费大量时间,而且在写 CL 期间可能不得不频繁地合并。

  • 更易于设计。 完善小 CL 的设计和修改要容易得多,多次微小的代码质量提高比一次大的设计改变更容易。

  • 减少阻塞审核的可能性。 小 CL 通常是功能独立的部分,你可能正在修改很多代码(多个小 CL),在发送一个 CL 审核时,同时可以继续修改其他的代码,并不会因为当前 CL 的审核没有完成而阻塞。

  • 更容易回退。 一个大 CL 开发的时间比较长,这意味从开发到代码提交这段期间,代码文件的变更会比较多。当回退代码时,情况会变得很复杂,因为所有中间的 CL 很有可能也需要回退。

请注意:审核者有权因为你的 CL 太大而拒绝它。 通常,他们会感谢你为代码做出的贡献,但是会要求你把它拆分多个小 CL。一旦写好了代码之后,要把它拆分成小 CL 通常需要花很多时间,当然,你也可能会花费大量时间与审核者争论为什么他应该接受这个大 CL 。与其如此,不如设计之初就保证 CL 尽量小。

如何定义“小”?

一般而言,一个 CL 的大小就应该是 独立功能的修改。这意味着:

  • 一个 CL 尽量最小化,它只 做一件事。通常它只是一个功能的一部分,而不是整个功能。总体而言,CL 太小或太大都不好,二者取其轻的话,太小稍微好点。可以与审核者一起讨论,找出大多比较合适。

  • 审核者需要理解 CL 中包含的一切(除了以后可能要开发的功能之外),包括 CL 代码、描述、已存在的代码(或之前已经审核过的相关 CL )。

  • 在 CL 代码提交之后,无论是针对用户,还是针对开发人员,系统应该仍旧运行良好。

  • 如果代码难以理解,通常是因为 CL 还不够小。如果新增一个 API,同时应该同一个 CL 中附上这个 API 的使用方法,便于审核者理解如何使用,也方便以后的开发者理解。同时也可能有效防止提交的 API 无人使用。

没有直观的标准判断 CL “太大”应该符合哪些条件。 100行代码通常是一个合理的大小。1000行代码通常太大了,但也不能一概而论,这取决于审核者的判断。修改文件的个数也影响它的“大小”。在一个文件中修改200行可能没问题,如果这200行代码横跨50个文件,通常而言太大了。

记住,当你开始编写代码时,只有你最了解代码,而审核者通常不了解上下文。在你看起来很是一个合适大小的 CL,审核者可能会很困惑。毫无疑问,在写 CL 时,CL 的大小最好比自认为的还要小。审核者通常不会抱怨你的 CL 太小了。

什么时候可以有大 CL?

当然,也有一些例外情形,允许 CL 比较大:

  • 删除一个文件与修改一行没有太大区别, 因为它不会花费审核者太多时间。

  • 有时候,一个大 CL 可能是由可靠的自动代码重构工具生成的,审核者的工作主要是检查它是否做了它应该做的工作。虽然符合以上提到的注意事项(例如合并和测试),这类 CL 也可能比较大。

以文件来拆分

另外一种拆分大 CL 的方法是:对 CL 中涉及的文件进行分组,这就要求不同独立功能的修改需要相应的审核者。

例如:你提交了一个 CL,这个 CL 修改了协议缓冲区,而且另外一个 CL 用到它。因此我们先提交第一个 CL,再提交第二个 CL,并让两个 CL 同时审核。此时,你应分别向两个 CL 的审核者告知另外一个 CL 的内容,以便他们知道上下文。

以代码和配置文件进行拆分。例如,你提交了2个 CL:其中一个 CL 修改了一段代码,另外一个 CL 调用了这段代码或代码的相关配置;当需要代码回滚时,这也比较容易,因为配置或调用文件有时候推送到产品比代码修改相对容易。

单独重构

在修改功能或修复缺陷的 CL 中,不建议把重构也加进来,而是建议把它放到单独的 CL 中。例如,修改类名或把某个类移到其他包内是一个 CL,修复这个类中的某个缺陷是一个 CL,不要把它们合并到一个 CL 中。把它们拆分出来更有利于审核者理解代码的变化。

有些代码清理工作,如修改某个类中的一个变量的名称,可以把它包含在一个功能修改或缺陷修复的 CL 中。那标准是什么呢?这取决开发者与审核者的判断,这种重构是否大到让审核工作变得很困难。

把测试代码包含到对应功能的 CL 中

避免单独提交测试代码。测试代码用以验证代码功能,应该把它与代码提交到相同的 CL 中,虽然它会增大 CL 的代码行数。

然而,独立的测试修改可以放到单独的 CL 中,这与单独重构中的观点比较类似。它包含如下内容:

  • 为过去提交的已存在代码创建新的测试代码。

  • 重构测试代码(例如,引入 helper 函数)。

  • 引入测试框架代码(如,集成测试)。

不要破坏编译

如果同时在审核的有多个 CL,并且这些 CL 之间存在依赖关系,你需要找到一种方式,确保依次提交 CL 时,保证整个系统仍旧运行良好。否则,可能在提交某个 CL 之后,让系统编译错误。此时,你的同事在更新代码后,不得不花时间查看你的 CL 历史并回退代码以确保本地编译没有问题(如果你之后的 CL 提交出了问题,可能会花费更多时间)。

无法将其变小

在某些情形下,好像你没法然 CL 变得更小,这种情况很少发生。如果开发者经常写小 CL,那么他往往都能找到一种把 CL 拆得更小的方法。

如果在写代码之前就估计这个 CL 比较大,此时应该考虑是否先提交一个代码重构的 CL,让已有的代码实现更清晰。或者,与团队其他成员讨论下,看是否有人能帮你指出,怎样在提交小 CL 的前提下实现当前功能。

如果以上所有方法都试过,还是不可行(当然,这种情况比较罕见),那就先与所有的审核者沟通一下,告知他们你将会提交一个大 CL,让他们先有心理准备。出现这种情况时,审核过程往往会比较长,同事需要写大量的测试用例。需要警惕,不要引入新的 bug。

下一章: 如何处理审核者的评论

Last updated