Skip to content

필사 모드: 写出会被快速合并的 PR 和提交信息

中文
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

引言 — PR 不是代码,而是沟通

PR 提交上去,却好几天都没人来审阅——这种经历大概每个人都有过。反过来,有的 PR 一提交就被打上"LGTM",马上就合并了。这是代码能力的差距吗?未必如此。能快速合并的 PR,除了技术之外,还有一门沟通的技术

核心的视角是这样的。你打开一个 PR 的那一刻,并不只是写了代码,而是向审阅者发出了一个请求——"请理解并批准这个变更"。审阅者不知道你脑子里在想什么,他手里只有 diff 和你的说明。所以一个好的 PR,归根结底是让审阅者用最少的努力获得最大确信的 PR

这篇文章整理了写出这类 PR 和提交信息的实用习惯。如果 Git 命令还不太顺手,不妨先去本站的 Git 实验场用眼睛熟悉一下分支和提交的流程,再回来读也不迟。

小而目的单一的 PR

如果只能选一个效果最大的习惯,那无疑是把 PR 保持得小。在一个 1000 行的 PR 和十个 100 行的 PR 之间,审阅质量更高的一方压倒性地是后者。

原因在于人类的注意力。审阅者面对一个大 diff 时,会发生两件事。第一,注意力下降,漏掉 bug。第二,因为负担太大,审阅会被一拖再拖。研究和现场经验都表明,审阅发现缺陷的比率会随着变更规模增大而急剧下降。

所以原则是一个 PR 只装一个目的

  差的 PR: "用户资料功能"
    - 新增 API 端点
    - 顺手替换了无关的日志库
    - 全文重新调整了缩进风格
    - 修了 3 个错别字
  → 审阅者: 我该看什么? 这个风格改动是不是藏着 bug?

  拆成 3 个好的 PR:
    PR 1: 新增资料 API 端点        (功能)
    PR 2: 替换日志库              (基础设施)
    PR 3: 风格/错别字整理         (杂项)
  → 每个都能独立审阅、合并、回滚

把功能变更、重构和格式化混进同一个 PR,审阅者就得费力分辨哪些行是"真正的变更"、哪些只是"挪了个位置",很快就会疲惫。把格式化单独提交,重构拆到独立的 PR 里——光是这样做,审阅速度就会明显加快。

提交信息: Conventional Commits

提交信息也有一套广泛使用的惯例。Conventional Commits 规定在提交的第一行用前缀标出变更的种类。

<类型>(<范围>): <摘要>

feat(auth): add password reset via email
fix(api): handle null user in settlement job
docs(readme): clarify local setup steps
refactor(cart): extract price calculation
test(order): add cases for partial refunds
chore(deps): bump lodash to 4.17.21

常用的类型有 feat(功能)、fix(修 bug)、docs(文档)、refactor(不改变行为的改进)、test(测试)、chore(构建、依赖等杂项)。

这套规约的好处不止于统一格式而已——它让机器也能读懂提交。因为能区分 featfix,工具就能自动升版本号(语义化版本控制),自动生成变更日志(CHANGELOG)。对人也有好处,只要扫一眼 git log,这个项目发生过什么事情一目了然。

摘要行(第一行)还有几条惯例。用祈使语气写("add"而不是"added"),控制在 50 字左右,末尾不加句号。可以把它想成填空——"应用这个提交之后,会___"。

正文里写"为什么",而不是"改了什么"

这里是区分初级和中级的分水岭。新手的提交信息记录的是改了什么,但这一点 diff 本身已经展示过了。真正需要的是为什么改

  弱的信息(改了什么 - diff 里已经有):
    fix: change timeout from 30 to 60

  好的信息(为什么 - diff 里没有):
    fix(upload): raise timeout to 60s for large video uploads

    结账后将原始视频(最大 2GB)传给编码服务器时,
    30 秒的超时经常被打破,导致上传失败。
    实测 p95 传输时间为 48 秒,因此留出余量将其提到 60 秒。
    根本性的解决方案(分块上传)另外在 issue #482 中处理。

下面这条信息,对六个月后再来看这段代码的人(通常是未来的你自己)是决定性的。它提前回答了"为什么恰好是 60 秒?30 秒不行吗?"这个问题,还说明了这不是根本解决方案,并链接了后续 issue。代码能说明它做了什么,但为什么会变成这样,只有人能记录下来。而提交正文,正是这份记录的位置。

归纳成规则就是: 摘要行之后空一行,下面的正文描述背景、原因和权衡。琐碎的提交(比如改错别字)可以不写正文,但只要提交里包含了某个决策,就一定要留下"为什么"。

上传前先做自我审查

在提交 PR 之前、在发给别人之前,从头到尾把自己的 diff 读一遍这个习惯,效果好得惊人。这就是把自己当成审阅者,去读自己的代码。

# 提交前,把自己改了什么整体再看一遍
git diff main...HEAD

自我审查抓到的东西通常都很琐碎,但恰恰是会让审阅者恼火的那类: 留下了调试用的 printconsole.log,顺带混进来注释掉的死代码,不小心提交了临时文件,或者提交信息里有错别字。这些如果被审阅者发现并留下评论,就会多一次往返,合并也因此推迟。

自我审查提前消除了这次往返。想象一下"审阅者会在这里问什么",提前在代码旁边给出可能被问到的问题的回答。比如"这部分看起来可能有点奇怪,但外部 API 只会以这种格式响应,没有别的办法"这样的自我注释,能大大节省审阅者的时间。

好的 PR 说明: 背景、变更、测试

PR 说明是审阅者在读 diff 之前看到的第一个画面。在这里把方向交代清楚,审阅会顺畅很多。推荐按三个维度来组织。

  ## 背景 (Context / Why)
  为什么需要这个变更。有什么问题或需求促成了它。
  关联 issue 的链接。让审阅者不需要背景知识也能理解。

  ## 变更 (What changed)
  改了什么、怎么改的。如果 diff 很大,按文件/区域简短说明。
  如果有重要的设计决策,也说明其依据。

  ## 测试 (How it was tested)
  是怎么验证的。新增的测试、手动确认的步骤、截图等。
  让审阅者相信"这确实能跑"的依据。

这三个维度依次回答了审阅者必然会有的三个问题——"为什么要做这个?""改了什么?""真的能跑吗?"。尤其不要漏掉测试这一节。如果是 UI 变更,一张前后对比的截图胜过一百句解释。审阅者一旦确认"这个人验证过了",就会安心,并倾向于批准。

对审阅者的共情

贯穿这一切习惯的一个态度,就是对审阅者的共情。提交 PR 时,想象屏幕另一端有一个时间紧张的同事。任何能降低他认知负荷的行为,都会让你的 PR 更快通过。

具体来说是这样:

  • 引导阅读顺序: "先看 parser.py,再看使用它的 main.py,这样更容易理解"——这样一句话,就能成为审阅者的路标。
  • 对重大决策提前说明依据: 有争议的选择(比如"为什么用 Y 而不是 X")要写在说明里,不要等审阅者来问。
  • 让 PR 大小匹配审阅者的时间: 紧急的热修复更要小。即便是大功能,也需要拆成可审阅的单元这份体贴。
  • 不要防御性地对待反馈: 审阅评论针对的是代码,不是人格。"好的,已经采纳了"比争辩更快。

审阅归根结底是人与人之间的事。持续提交能帮到审阅者的 PR,信任会逐渐积累,下一个 PR 也会通过得更快。

用堆叠 PR 拆分大工作

"保持小"这个原则和"必须做出一个大功能"这个现实经常发生冲突。这时有用的技巧是堆叠 PR(stacked PRs)。把大工作拆成一串相互依赖的小 PR,后一个 PR 叠在前一个之上。

  main
   └── PR 1: 数据库 schema + 迁移        (独立审阅)
        └── PR 2: 在 PR 1 之上的仓储层
             └── PR 3: 在 PR 2 之上的 API 端点
                  └── PR 4: 在 PR 3 之上的 UI 接入

每个 PR 都以紧邻它下面的那个 PR 作为基准分支。这样一来,审阅者依次审查四个 200 行的片段,每个片段都建立在前面的上下文之上理解——比一次性扔出 800 行要好得多。前面的 PR 合并之后,把后面 PR 的基准改回 main 继续。

堆叠 PR 在管理上需要一点勤快,因为前面的 PR 一旦被修改,后面的 PR 就得跟着变基。但大多数团队工具都能帮上忙,而"可审阅的大小"带来的收益,足以覆盖这份成本。一旦看出某个功能有变大的苗头,从一开始就按堆叠来设计,比日后再费力拆分一个庞大的 PR 要轻松得多。

结语

快速合并的 PR,秘诀不在于华丽的代码,而在于对审阅者的体贴。小而目的单一的 PR、用 Conventional Commits 整理出的历史、包含"为什么"的提交正文、自己动手做的自我审查、包含背景·变更·测试的说明,以及用于大工作的堆叠 PR——这一切指向的都是同一件事: 让审阅者用最少的努力获得最大的确信。

写代码只是一半的工作,剩下的一半,是让别人理解并信任这份代码。如果 Git 工作流还不熟悉,可以去 Git 实验场亲手操作分支、合并和变基,培养手感。工具用顺手之后,就能把更多精力花在打磨好的 PR 上。

参考资料

현재 단락 (1/73)

PR 提交上去,却好几天都没人来审阅——这种经历大概每个人都有过。反过来,有的 PR 一提交就被打上"LGTM",马上就合并了。这是代码能力的差距吗?未必如此。能快速合并的 PR,除了技术之外,还有一...

작성 글자: 0원문 글자: 4,108작성 단락: 0/73