规范的 PR(Pull Request)是开源协作的核心。一个优秀的 PR 不仅能让维护者快速理解并信任你的改动,还能大大增加被合并的概率。
下面从结构规范、代码规范、描述规范三个维度来拆解。
一、PR 标题规范
标题是维护者第一眼看到的内容,必须简洁清晰。
格式模板
<类型>(<范围>): <简短描述>
类型说明
| 类型 | 说明 | 示例 |
|---|---|---|
| feat | 新功能 | feat(search): 添加中文拼音搜索支持 |
| fix | 修复 bug | fix(cache): 修复缓存过期导致的空指针异常 |
| docs | 文档更新 | docs(readme): 更新安装步骤 |
| refactor | 重构(不改功能) | refactor(logger): 统一日志格式 |
| perf | 性能优化 | perf(index): 优化倒排索引构建性能 |
| test | 测试相关 | test(parser): 增加边界条件测试用例 |
| chore | 构建/工具变动 | chore(deps): 升级依赖版本 |
| revert | 回滚之前的提交 | revert: 回滚 feat(search) 提交 |
示例对比
| ❌ 不好的标题 | ✅ 好的标题 |
|---|---|
fix bug |
fix(consumer): 修复 rebalance 时 offset 提交失败的问题 |
update code |
feat(api): 添加批量删除评论的接口 |
修改了一堆东西 |
refactor(storage): 将文件存储抽象为接口 |
二、PR 描述规范
PR 描述是让维护者理解为什么做、怎么做的的关键。
推荐模板
## 概述
<!-- 用一两句话说明这个 PR 做了什么 -->
## 相关 Issue
<!-- 关联的 issue 编号,如 Fixes #123 -->
Fixes #(issue编号)
## 变更内容
<!-- 列出主要变更点,可以是 bullet points -->
- [ ] 变更点 1
- [ ] 变更点 2
## 测试说明
<!-- 说明你是怎么测试的 -->
- 单元测试: ✅ 通过
- 集成测试: ✅ 通过
- 手动测试场景: xxx
## 影响范围
<!-- 说明这个改动会影响哪些模块/功能 -->
- 影响模块: consumer group
- 兼容性: 向后兼容
## 截图(如适用)
<!-- UI 类改动建议附截图 -->
完整示例
## 概述
为评论搜索添加 IK 中文分词支持,替换原有的 ngram 分词器。
## 相关 Issue
Fixes #2345
## 变更内容
- 新增 IK 分词器配置选项
- 在创建索引时支持选择 `ik_max_word` 或 `ik_smart`
- 添加分词器的单元测试
- 更新配置文档
## 测试说明
### 单元测试
```bash
go test -v ./analyzer/... -run TestIKTokenizer
全部通过 ✅
手动测试
- 创建索引并指定分词器为
ik_max_word - 索引文档 “这个电影很好看”
- 搜索 “好看” → 能匹配到 ✅
- 搜索 “电影” → 能匹配到 ✅
影响范围
- 影响模块: 索引创建、查询解析
- 配置变更: 新增
index.analyzer.ik.enabled配置项(默认 false) - 兼容性: 默认关闭,不影响现有用户
相关文档
## 三、代码规范
### 3.1 提交粒度
**一个 PR 只做一件事**。
| ✅ 好的粒度 | ❌ 坏的粒度 |
|------------|------------|
| 修复一个具体的 bug | 修复三个无关的 bug |
| 添加一个完整的功能 | 加功能和修 bug 混在一起 |
| 重构一个模块 | 重构十个文件 + 加新功能 + 修 typo |
**黄金法则**:如果你无法用一句话说清楚这个 PR 做了什么,那它可能太大了。
### 3.2 Commit 规范
一个 PR 可能包含多个 commit,每个 commit 也应该是原子化的。
```bash
# ✅ 好的 commit 历史
commit abc123: feat(search): 添加 IK 分词器配置
commit def456: test(search): 添加 IK 分词器单元测试
commit ghi789: docs(search): 更新分词器文档
# ❌ 坏的 commit 历史
commit xyz: update
commit abc: fix
commit 123: 改了一堆
3.3 代码风格
| 要求 | 说明 |
|---|---|
| 遵循项目风格 | 不要引入新的代码风格,参考项目现有代码 |
| 运行格式化工具 | 如 gofmt、prettier、black 等 |
| 通过 Linter | 确保没有 Linter 警告 |
| 添加注释 | 复杂逻辑必须有注释解释 |
| 测试覆盖 | 新代码尽量有对应的测试用例 |
3.4 兼容性考虑
// ❌ 破坏兼容性
public void process(Comment comment) {
// 新版本要求 comment 不能为 null
// 旧版本调用方可能传 null
}
// ✅ 保持兼容性
public void process(Comment comment) {
if (comment == null) {
// 降级处理,保持旧行为
return;
}
// 新逻辑
}
四、流程规范
4.1 PR 创建前的自检清单
## PR 提交前检查清单
- [ ] 代码能在本地编译通过
- [ ] 所有测试用例通过(包括新增的)
- [ ] 没有引入 Linter 警告
- [ ] 已经 rebase 了最新的 main/master 分支
- [ ] commit 信息清晰,没有 WIP、tmp 之类的提交
- [ ] PR 描述完整,说明了做什么和为什么做
- [ ] 没有混入无关文件的修改(如 IDE 配置文件)
4.2 PR 创建后的沟通规范
| 场景 | 做法 |
|---|---|
| 维护者提了意见 | 及时回复,讨论修改方案 |
| 需要修改代码 | 不要关掉 PR 重开,直接 push 新 commit |
| 修改完成 | 回复 “已修改,请重新审核” |
| 多轮修改 | 可以 squash 中间 commit,但不要 force push 到正在审核的分支 |
4.3 响应 Review 意见
## ✅ 正确的响应方式
维护者: "这里用 map 代替 slice 会更好"
回复: "已修改,使用 map[string]bool 替代" + 附上修改后的代码链接
## ❌ 错误的响应方式
- 直接关掉 PR 重开
- 忽略意见,等维护者再次提醒
- 回复 "我觉得这样就行"(除非你有充分理由)
五、完整示例对比
❌ 不规范的 PR
标题: fix bug
描述: 如题
Commit 历史:
a1b2c3: fix
d4e5f6: update
g7h8i9: fix again
✅ 规范的 PR
标题: fix(consumer): 修复 rebalance 时 offset 提交失败的竞态条件
描述:
## 概述
在 consumer rebalance 过程中,offset 提交可能因为分区被撤销而失败。
这个 PR 在提交前检查分区是否仍属于当前 consumer。
## 相关 Issue
Fixes #4567
## 变更内容
- 在提交 offset 前增加分区所有权检查
- 添加相关的单元测试
## 测试说明
```bash
go test -race ./consumer/... -run TestRebalance
新增测试用例 TestCommitDuringRebalance 通过
影响范围
- 影响模块: consumer coordinator
- 兼容性: 完全向后兼容
Commit 历史:
abc123: fix(consumer): 添加 rebalance 时的分区所有权检查
def456: test(consumer): 添加 rebalance 竞态条件测试
六、快速记忆清单
| 维度 | 核心要求 |
|---|---|
| 标题 | <类型>(范围): 简短描述 |
| 描述 | 说清楚做了什么、为什么、怎么测的 |
| 粒度 | 一个 PR 只做一件事 |
| Commit | 原子化,信息清晰 |
| 代码 | 遵循风格、有测试、有注释 |
| 兼容性 | 不破坏现有行为 |
| 响应 | 及时、礼貌、有改必应 |
一句话总结:规范的 PR 让维护者不用问你任何问题就能理解你的改动并决定是否合并。维护者的时间是最宝贵的资源,为他们节省时间,你的 PR 就更容易被合并。