View on GitHub

google_eng_practice

谷歌(Google)工程实践文档(Google's Engineering Practices documentation)

小 CL

目录

为什么应该写小 CL?{#why}

小 CL 有如下优点:

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

如何定义“小”?

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

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

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

什么时候可以有大 CL?

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

以队列的方式,一个接一个提交 CL

一种拆分 CL 的方式是:在不阻塞自己的代码的前提下,编写完一个 CL 后,提交它便于审核,然后立即 基于 第一个 CL开始写下一个 CL。大多数版本控制系统都允许你以这种方式工作。

以文件来拆分

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

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

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

水平拆分

考虑创建共享代码或桩代码,用于帮助隔离技术栈各层之间的变化。这不仅有助于加快开发速度,而且还鼓励层之间的抽象。

例如:你创建了一个计算器程序,它包含客户端、API、服务和数据模型层。共享原型签名可以抽象服务层和数据模型层。同样,API桩可以将客户代码的实现从服务代码分离开,确保它们相互独立。类似的想法还可以应用于更细粒度的函数类级别的抽象。

垂直拆分

与分层的水平拆分方式正交,可以将代码拆分成更小的、全栈的、垂直功能。这些功能中的每一个都可以独立并行实现。这使得当某些轨道在等待审核或反馈时,另一些轨道可以继续前进。

回到水平拆分中的计算器示例。现在你想支持新的运算符,如乘法和除法。在实现时,你可以把乘法和除法实现为单独的垂直或子功能,即使他们有些功能重合,如 共享按钮样式或共享验证逻辑。

水平和垂直拆分

更进一步,你还可以把两种方式相结合,以如下表来实现。在下表中,每个单元格是一个独立的 CL。从模型(底部)开始开发,最后是顶部的客户端。

功能: 乘法 功能: 除法
Client Add button Add button
API Add endpoint Add endpoint
Service Implement transformations Share transformation logic with multiplication
Model Add proto definition Add proto definition

单独重构

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

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

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

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

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

不要破坏编译

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

无法将其变小

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

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

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

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