使用堆叠 PR 重新思考代码审查
这篇文章探讨了左移原则,并表明堆叠 PR 将变得越来越有用。
同行代码审查过程是软件开发的重要组成部分。它有助于维护软件质量并促进对标准、项目要求、风格指南的遵守,并促进学习和知识转移。
代码审查有效性
虽然审查足够小的代码更改的有效性很高,但随着更改规模的增加,其有效性呈指数下降。为了保持有效的必要精神集中度,大量的代码审查会让人筋疲力尽。
通常,审核时间越长,整体审核的效果就越差:
那么为什么我们不能限制拉取请求 (PR) 的大小呢?虽然许多更改可以从小处开始,但突然间,一个小的两行更改可能会发展为 500 行重构,其中包括与审阅者的多次来回对话。
一些工程团队在继续工作时还会维护长期运行的功能分支,这使得审查变得困难。
那么,我们如何取得适当的平衡呢?简单的。使用堆叠 PR。
什么是堆叠 PR?
堆叠式拉取请求会进行较小的迭代更改,并且彼此堆叠在一起,而不是在单个拉取请求中捆绑大型整体更改。堆栈中的每个 PR 仅关注一个逻辑更改,从而使审核过程更易于管理且耗时更少。
我们去年还写了一篇文章,解释了此帮助如何将代码更改表示为叙述,而不是按文件或功能分解内容。
为什么要堆叠 PR?
除了建立更有效的代码审查文化之外,堆叠 PR 还有其他一些好处:
早期代码审查反馈
想象一下您正在实现一个大型功能。与其创建整个功能然后请求代码审查,不如考虑制定初始框架并立即将其提交以获取反馈。
通过尽早获得设计反馈,这可能会节省您无数的时间。
更快的 CI 反馈周期
堆叠 PR 支持左移实践,因为变更是不断集成和测试的,这样可以及早发现和纠正问题。
这些变化被一点点地合并,尽早发现任何问题,而不是合并一个巨大的变化,希望它不会降低产品!
知识共享
代码审查对于后代来说也很精彩。您的代码更改正在叙述您实现功能背后的思维过程,因此,更改的细分可以创建更有效的知识转移。
团队成员更容易理解这些变化,从而促进未来更好的知识共享。
保持畅通无阻
等待代码审查和批准可能是一个令人沮丧的过程。通过堆叠 PR,开发人员可以处理功能的多个部分,而无需等待审阅者批准以前的 PR
有什么问题?
那么,为什么更多的开发人员不使用堆叠 PR 进行代码审查呢?
尽管这种堆叠式 PR 工作流程既解决了保持代码审查可管理性又保证开发人员高效的所需实践,但不幸的是,Git 或 GitHub 本身并没有很好地支持它。
因此,开源社区开发了多种工具,使工程师能够将这种堆叠技术整合到现有的 Git 和 GitHub 平台中。但堆叠 PR 只是故事的一部分。
更新中
当我们获得代码审查反馈并对堆栈的部分进行更改时,我们现在必须重新设置基础并解决所有后续分支的冲突。
让我们举个例子。想象一下,您正在进行一项更改,需要进行架构更改、后端更改和前端更改。
这样,您现在可以先发送一个简单的架构更改以供审核,在审核期间,您可以开始处理后端和前端。使用堆叠 PR,所有这 3 个更改都可以由 3 个不同的评论进行审核。
在这种情况下,您可能有一个如下所示的堆栈,其中demo/schema
、 demo/backend
和demo/frontend
表示彼此堆叠的 3 个分支。
到目前为止,这是有道理的,但是如果您收到一些关于需要创建新提交的模式更改的代码审查评论怎么办?突然你的提交历史看起来像这样:
现在,您必须手动对所有后续分支进行变基并解决每个阶段的冲突。想象一下,如果您有 10 个堆叠的分支,您可能需要解决冲突 10 次。
合并
但这还不是全部,将 PR 合并到堆栈中可能是一场真正的噩梦。您有 3 个选项squash
、 merge
和rebase
来合并 PR。让我们尝试了解每一项的幕后故事。
- 在
squash
提交的情况下,Git 从 PR 的所有现有提交中获取更改并将它们重写为单个提交。在这种情况下,不会维护这些更改的来源历史记录。
merge
提交是一种特殊类型的 Git 提交,由两个或多个提交的组合表示。因此,它的工作方式与squash
提交非常相似,但它也捕获有关其父母的信息。在典型场景中,合并提交有两个父项:基础分支(合并 PR 的位置)上的最后一次提交,以及已合并的功能分支上的顶部提交。
尽管这种方法为提交历史记录提供了更多上下文,但它无意中创建了一个非线性的 git 历史记录,这可能是不可取的。
- 最后,如果进行
rebase
和合并,Git 会将提交重写到基础分支上。因此,与squash
提交选项类似,它将丢失与原始提交相关的任何历史记录。
通常,如果您在堆叠 PR 时使用merge
提交策略,您的生活会简单一些,但大多数团队不鼓励使用该策略来保持 git 历史记录干净。这意味着您可能使用squash
或rebase
基合并。
这会为所有后续未合并的堆叠分支造成合并冲突。
在上面的示例中,假设我们将第一个分支demo/schema
合并到主线中。它将创建一个新的提交D1
,其中包含A1
和A2
的更改。
由于 Git 不知道D1
来自哪里,并且demo/backend
仍然基于A2
,因此尝试在主线之上重新设置demo/backend
会产生合并冲突。
同样,在重新调整demo/frontend
后重新调整demo/backend
也会导致相同的问题。因此,如果您有 10 个堆叠的分支,并且您挤压合并了其中一个,则必须解决这些冲突九次。
我们还只是触及表面;还有许多其他用例,例如重新排序提交、拆分、折叠和重命名分支,在处理堆积的 PR 时可能会产生巨大的管理开销。
这就是为什么我们将堆叠 PR 管理作为 Aviator 的一部分构建的原因。
为什么 Aviator CLI 与众不同
将 Aviator 视为位于现有工具之上的增强层。 Aviator 与 GitHub、Slack、Chrome 和 Git CLI 连接,提供增强的开发人员体验。
Aviator CLI 与其他一切无缝协作! CLI 不仅仅是 Git 之上的一层,而且还了解 GitHub 上堆栈的上下文。让我们考虑一个例子。
创建堆栈
创建堆栈相当简单。除了这种情况外,我们使用av
CLI 创建分支以确保跟踪堆栈。例如,要创建架构分支和相应的 PR,请按照以下步骤操作。
av stack branch demo/schema # make schema changes git commit -a -m "[demo] schema changes" av pr create
由于 Aviator 还连接到您的 GitHub,因此您可以轻松可视化堆栈。
或者,如果您想从终端将其可视化,您仍然可以使用 CLI 命令来实现:
更新堆栈
现在使用堆栈变得轻而易举。您可以向任何分支添加新的提交,并且只需从堆栈中的任何位置运行av stack sync
即可同步所有分支。 Aviator 会自动为您重新设置所有分支的基准,如果存在真正的合并冲突,您只需解决一次即可。
合并堆栈
这就是 Aviator 工具从任何现有工具中脱颖而出的地方。在 Aviator,我们构建了最先进的 MergeQueue 之一来管理大规模自动合并数千个更改。
Aviator 支持与 CLI 和堆叠 PR 的无缝集成。因此,要合并部分或全部 PR 堆栈,您可以使用 CLI av pr queue
将它们分配到 Aviator MergeQueue 或在 GitHub 上发布评论: /aviator stack merge
。
Aviator 自动按顺序处理验证、更新和自动合并所有排队的堆栈。
现在,合并 PR 后,您可以运行av stack sync --trunk
来更新所有 PR 并清除所有合并的 PR。
左移是未来
由于需要将更改分解为更小的部分,堆叠 PR 最初可能看起来需要更多工作。然而,代码审查效率的提高、更快的反馈循环和增强的学习机会肯定会超过这种开销。
随着我们继续采用左移原则,堆叠 PR 将变得越来越有用。
Aviator CLI 提供了一种管理堆叠 PR 的好方法,并且减少了很多繁琐的工作。 CLI 是开源且完全免费的。我们希望您尝试一下,并在我们的讨论板上分享您的反馈。
在 Aviator,我们正在根据第一原则构建开发人员生产力工具,以使开发人员能够更快更好地构建。