原文:Greg Foster - 2023.07.25

大多数工程师都有一种直觉,那就是小的代码更改总是比大的更好。逻辑论证也很简单——小的 pull requests 更容易 review,出现错误的可能性更小,从构思到部署的速度也更快。

关于这个问题,我很喜欢几篇论文 - 如果想进一步阅读,可以参考:

但是,什么样的更改才算小呢?PR 会不会 小?如果一个 PR 比一个大的 PR 更好,那么 好多少 呢?

主张:理想的 PR 长度为 50 行

通过数据分析,我们发现,理想的代码更改应该是 50 行。

50 行代码更改的 review 和合并速度比 250 行的快大约 40%。它们被撤销的可能性比 250 行的小 15%,每行更改的 review 评论数量多 40%。如果你的平均 PR 有 50 行,那么你发布的总代码可能比编写 200 多行 PR 的队友多 40%。

50 行是速度、review 评论、撤销率和总编码量的最佳选择。如果你愿意接受一个范围,我会推荐每个 PR 25-100 行。根据数据,我们发现 PR 越小,review 时间、合并时间和每行评论的时间都会变得更好。但是,也有一个限制:如果少于 25 行,就会开始遭受更高的撤销率,以及更低的代码发布总量。

我们来讨论一下为什么,并用一些数据来支持这一说法。

我们的样本集

(注:Graphite 是一个 PR 增强工具。)

本文中所有基于数据的陈述,都是使用与 Graphite 同步的私有/公开 PRs 和仓库得出的。为了找出理想的 PR 大小,我查看了四个主要指标,以及它们与 PR 大小的相关性:

  • review/合并时间
  • 撤销率
  • 行内评论的平均数量
  • 一年内改变的代码总量

注意事项

与所有收集的数据一样,从数字推断含义时,有一些需要注意的事项:

  • 我使用了不一致的、非线性的桶大小,对应于直观的 PR 大小范围。线性桶将过于精细,而指数桶大小会丧失太多细节。
  • 我使用的是中位数 PR 大小,而不是平均 PR 大小,以避免异常的重构偏离数据。
  • 定义撤销的 PR 是标题中包含 “Revert” 一词的 PR。这个假设似乎是安全的,因为生成的 GitHub 撤销,会自动在标题前加上这个词。
  • 我们发现 Graphite 用户通常会创建较小的 PR,因为许多组织使用基于主干的开发风格(这种风格鼓励创建较小的 PR)。

review 时间和合并时间

让我们深入研究数据。从 review 时间和合并时间开始,我们可以看到最小的 PR 比 2000-5000 行的 PR 快近五倍。直观上,这是有道理的 - 更小的 PR 意味着更少的代码行,更少的代码意味着破坏性或细致的更改的机会更小,这反过来又导致了更快的 review。

令人着迷的是,超过 5k 行后,PR 又开始变得更快了。我只能假设这是盲目的 review 通过、被认为安全的重构、包的添加或生成更改的组合;也许当作者和 review 者甚至无法滚动到更改的底部时,他们都会开始耸耸肩。

请注意,假设我们更关心每个 PR 的时间,而不是每行的时间。如果我们关心的是尽快完成一个 PR,数据显示要尽可能地缩小它。但如果希望尽快完成大量的代码,那么一个 2000 行的 PR 大约每小时合并 12 行,而一个 10 行的 PR 大约每小时合并 0.25-2 行。

按 PR 大小的撤销率(Revert)

撤销率显示了同样的高级结论:较小的 PR 比较大的 PR 撤销的次数更少,被撤销次数最少的 PR 是那些 25-50 行的。但再一次,图的边缘很有趣。不足 10 行的 PR 被撤销的次数明显多于 10-100 行的 PR。如果必须猜测,我会说不足 10 行的 PR 可能涉及到一些危险的配置更改,但我很好奇,如果在对编程语言进行标准化(每种语言分别统计)后,这个发现是否仍然成立。

一旦 PR 开始超过 1 万行代码,它们似乎变得“更安全”了。我怀疑这是因为 PR 大小的极端包括重构,可能包含更少的功能更改,因此破坏的机会稍微减小。或者,由于情绪抵触和合并冲突,工程师可能会对撤销超过 1 万行的 PR 变得越来越不情愿。

按 PR 大小划分的平均行内评论数量

取决于你的优化的目标是什么(快速合并还是深入的代码 review ),可以选择是否将 PR 分割成更小的变更集合。如果想在单个 PR 上获得最多的反馈,那么选择 1k-2k 行代码。如果想获得盲目 review 通过的最高机会,保持在 10 行以下。当你所关心的只是尽可能少的争论就能推进一个特定更改时,了解此信息是有用的。

我们在这里也看到,大的 PR 开始逐渐减少参与度。你的 review 者愿意阅读的代码量有一个实际的限制——我怀疑 2000 行是从“阅读”一个 PR 变成“浏览”一个 PR 的点。

如果你关心的是最大化代码在长期内的参与度和反馈,尽可能地写小 PR。它们更容易理解,并且可以平均在每 39 行代码中拥有一条评论。另一方面,如果你讨厌书面反馈,那么让 PR 超过 1 万行,你将开始在每改动 6000 行代码时只收到零星的评论——不过,对此要持保留态度,因为人们很少为了获得反馈或他人参与而提交 PR。

总代码产出

有些人可能会想,写小的 PR 是否会导致总的代码产出减少。我们都希望能高效地工作,但有时需要提高代码产量。持续写小于 20 行的 PR 将对你的净编码能力产生显著影响——但有趣的是,写大于 100 行的 PR 也会产生影响。最高产量的编码者和仓库的中位数更改大小只有 40-80 行。我怀疑这是因为代码量等于更改大小 * 更改速度。更改太小,更快的合并时间并不能弥补。太大,你开始被更慢的 review 和合并周期拖累。

结论

一般来说,科技公司团队中的普通开发者应该以 50 行的中位数 PR 大小为目标——这通常是我们在 Graphite 坚持的 PR 大小。显然,这带来了一些在上文中讨论过的边缘情况,特定的更改可能需要在大小上做出调整,但是要知道,这样做可能会对 review 质量、速度以及更改被撤销方面产生显著的成本。