Chapter 04

GitHub PR 与 Issue 写作

PR 描述决定你的代码能不能在 24 小时内被合并——内容质量比代码质量更先被读到。

4.1 PR 标题:和 commit subject 同一套规则

PR 标题在 GitHub 上扮演两个角色:(a) 在 PR 列表里被快速扫读;(b) 默认会作为 squash merge 时的 commit message。所以它的写作规则和 commit subject 完全一致:祈使句、动词原形、Conventional Commits 风格。

# Good
feat(auth): add OAuth2 PKCE flow for mobile clients
fix(payments): retry callback on transient 5xx errors
refactor(db): extract connection pool config

# Bad
Authentication improvements                  # 名词,不知道改了什么
Added OAuth2                                  # 过去式
[WIP] new feature for auth                    # WIP 不该出现在 ready 的 PR
解决了一个 bug                                # 中文
Some changes                                  # 等于没说

额外建议:标题前加 [WIP] / Draft: / RFC: 表示状态:

Draft: feat(api): explore GraphQL gateway
RFC: pluggable authentication backend
[WIP] performance: experiment with B-tree storage

但更现代的做法是:使用 GitHub 自带的 Draft PR(点击 "Create draft pull request"),就不用加前缀了。

4.2 PR 描述:Why / What / How / Test plan

大型项目通常都有 PR template,几乎都遵循以下四段:

# 标准 PR template

## Why
- <问题背景>
- <需求来源 / 用户痛点>

## What
- <改了什么>
- <关键决策>
- <不在范围内的事项>

## How
- <实现思路 / 架构变化>
- <迁移步骤(如有)>

## Test plan
- [ ] <手动验证步骤 1>
- [ ] <手动验证步骤 2>
- [ ] Added unit tests in `path/to/test`
- [ ] CI green

## Screenshots / Videos
<UI 类必备>

Closes #1234

Why 段落怎么写

Why 是 PR 描述里最重要但最常被中国程序员忽略的部分。它的写作公式:

实例:

## Why

The login endpoint currently has no rate limit. We've seen
~50k failed credential-stuffing attempts per hour during peak
spikes (see incident #INC-2451). Without rate limiting, a single
malicious IP can saturate the auth service and degrade login
latency for legitimate users.

This PR introduces a sliding-window rate limiter on /login.

What 段落怎么写

What 列出 diff 的关键点,重点是分类而不是逐文件描述:

## What

- Adds `RateLimiter` interface and `RedisSlidingWindowLimiter` impl
- Wires the limiter into the `/login` handler
- Adds env vars `LOGIN_RATE_LIMIT` and `LOGIN_RATE_WINDOW`
- Updates `docs/auth.md` with the new behavior
- **Out of scope**: rate limiting on other endpoints (separate PR)

"Out of scope" 一行很重要——它告诉 reviewer 你主动选择了不做某事,避免对方提出"为什么不顺便也加上 X"。

How 段落怎么写

How 解释架构选择和关键 tradeoff:

## How

We chose a sliding window over a token bucket because it gives
smoother throttling under bursty traffic. Storage is in Redis
to allow horizontal scaling of the auth service.

Migration: none. The limiter is gated behind the feature flag
`auth.rate_limit_enabled` (default false). We'll roll it out to
1% → 10% → 100% over a week.

Test plan 段落

Test plan 是 PR 里的"合同"——你向 reviewer 承诺这些测试都通过了:

## Test plan

- [x] Unit tests in `internal/auth/limiter_test.go` (95% coverage)
- [x] Integration test against local Redis
- [x] Manual: hammer /login with `hey -n 1000 -c 50`; observed 429 after limit
- [x] Manual: confirmed feature flag off → no rate limit applied
- [x] CI green
- [ ] **Pending**: load test in staging (will run after merge)

checkbox 必须真的去勾。Reviewer 会看到没勾的 box 就追问。

4.3 Issue 引用关键词

GitHub 识别一组特殊关键词,PR 合并时会自动关闭对应 issue:

关键词语义合并后
Closes #123关闭自动 close issue
Close #123同上自动 close
Closed #123同上自动 close
Fixes #123修复自动 close(语义偏 bug)
Fix #123同上自动 close
Resolves #123解决自动 close(正式语)
Refs #123引用仅链接,不关闭
See also #123参见仅链接
Part of #123部分实现仅链接(用于多 PR 实现一个 epic)
Reverts #123回滚 PR仅链接

跨仓库引用:

Closes octocat/hello-world#123
Refs https://github.com/torvalds/linux/issues/4567
// trap

"Closes" 必须放在 PR 描述的 body(不能只在 commit message 里)才能触发关闭。多个 issue 必须每个都加关键词:Closes #1, #2 ❌;Closes #1, closes #2 ✓。

4.4 Issue 三大类型

类型 1:Bug Report(bug 报告)

这是最常见、也最容易写差的 issue 类型。模板:

## Description

<一句话描述 bug,避免 "doesn't work">

When uploading a file larger than 100 MB to /api/upload, the server
returns 502 instead of streaming the upload. This started after v2.4.0.

## Steps to reproduce

1. Run `./scripts/start-local.sh`
2. `curl -F file=@large.bin http://localhost:8080/api/upload`
   where `large.bin` is > 100 MB
3. Observe response

## Expected behavior

Server should stream the upload and return 200 with the file metadata.

## Actual behavior

Server returns 502 Bad Gateway after ~30 seconds. Logs show:
```
upstream prematurely closed connection
```

## Environment

- App version: v2.4.1 (commit a1b2c3d)
- OS: Ubuntu 22.04 LTS
- Browser: N/A (curl)
- Reverse proxy: nginx 1.24

## Additional context

Bisected to commit 7f3a9c2 ("refactor: switch to streaming uploads").
Reverting that commit fixes the issue.

Bug Report 必备字段

字段英文典型措辞
描述Description / Summary"When X, Y happens instead of Z."
复现步骤Steps to reproduce / Repro steps编号列表,命令可直接复制
预期Expected behavior"Should return 200 with..."
实际Actual behavior"Returns 502 with error..."
环境Environment / System info版本、OS、浏览器等
截图 / 日志Screenshots / Logs关键线索
变通Workaround"As a workaround, users can..."
附加Additional contextgit bisect 结果、相关 issue 等

类型 2:Feature Request(功能请求)

## Problem

When deploying to multiple environments (dev/staging/prod), I have
to maintain three nearly-identical config files. Any change must be
copy-pasted to all three, which is error-prone.

## Proposed solution

Add support for environment-specific config overlays, e.g.:

```yaml
# config.yaml (base)
db:
  pool: 10

# config.production.yaml (overlay)
db:
  pool: 50
```

The CLI would merge them when started with `--env=production`.

## Alternatives considered

- Templating with envsubst — works but obscures the final config
- External tool like Helm — overkill for our scale

## Additional context

Similar pattern in <link to another tool>; Kubernetes uses kustomize
for the same problem.

Feature Request 写作要点

类型 3:Question / Discussion

很多大型项目(Vue、Node.js、TypeScript)已经把"问问题"挪到 GitHub Discussions 而不是 Issues。但如果项目还允许在 Issues 里问,模板:

## Question

I'm trying to use library X with framework Y. The README mentions
Z is supported, but I can't find an example showing how.

## What I tried

- Followed the basic README setup
- Looked at the `/examples` folder — no Y example
- Searched closed issues — found #345 but it's outdated

## Code / Setup

```js
// minimal repro
import { x } from 'lib-x'
// ...
```

## Question

Is Y still supported? If yes, is there a recommended pattern?

4.5 礼貌话术:请求与拒绝

请求别人做事

语气句型场景
正式Could you please ...对 maintainer / 不熟的人
正式Would you mind ...请求小帮助
正式I would appreciate it if ...需要明显帮助
中性Could you ...同事 / 同级
中性Would it be possible to ...不确定能否做到
中性Any chance you could ...知道对方很忙时
非正式Mind ...?熟悉的同事
非正式Quick favor — could you ...小事

对比例子:

# 中式直译(看起来命令式)
You should review my PR.
Please reply me as soon as possible.

# 地道写法
Could you take a look at this PR when you have a chance?
A reply this week would be much appreciated, but no rush.

拒绝别人请求

拒绝是中国程序员最难写的英语场景。规则:软化 + 说明原因 + 给替代方案

句型用法
I'm afraid we won't be able to ...正式拒绝,含遗憾
Unfortunately, this isn't something we can ...制度性原因
I don't think this is the right fit for ...方向不匹配
I appreciate the suggestion, but ...感谢后转折
This falls outside the scope of ...超出范围
We've considered this before and decided to ...历史决策
Closing as ... — feel free to reopen if ...关闭 issue 时

实例:拒绝一个 feature request

Thanks for the suggestion! I can see why this would be useful.

Unfortunately, this isn't something we plan to add to core. Keeping
the surface area small is one of our explicit goals — every option we
add becomes a maintenance burden across all platforms.

That said, this is a great use case for a plugin. We expose enough
hooks (see `docs/plugins.md`) that you should be able to implement
this externally. Happy to review if you draft something!

Closing for now, but feel free to reopen if you hit walls building
the plugin.

这种回复的效果:拒绝了请求,但保留了对方的尊严,提供了出路,也展示了团队的设计哲学。

道歉 / 推迟回复

# 推迟回复
Sorry for the delay — we're heads-down on the v3 release this week.
I'll get back to you by Friday.

# 因不熟悉而推迟
This is a great question but it's outside my area. Let me ping
@expert-name who knows this part better.

# 重要 issue 暂时无人手
Apologies for the slow response. We're aware of this and tracking
it in #4321; we just don't have bandwidth this quarter.

4.6 Labels / Milestones / Projects 的英文规范

常见 Label 词表

# 类型
bug, enhancement, feature, documentation, question,
duplicate, invalid, wontfix, help wanted, good first issue

# 优先级
priority: critical, priority: high, priority: medium, priority: low
P0, P1, P2, P3

# 状态
needs-triage, needs-info, needs-review, in-progress,
blocked, ready-for-review, ready-to-merge

# 模块(项目自定义)
area: auth, area: api, area: ui, area: docs

# 影响范围
breaking-change, security, performance, accessibility (a11y)

# 难度
easy, medium, hard, complex

# 版本
v2.x, v3.x, backport-v2

# 其他常见
stale, on-hold, discussion, RFC

Milestone 命名

v2.4.0
v2.4.0 - April 2024
2024 Q2
Sprint 23
Backlog
Icebox

Backlog(待办)和 Icebox(冷冻)是约定俗成:前者是"还没排期",后者是"无限期搁置"。

4.7 PR 全流程英语模板

开 PR:标题 + 描述

Title: feat(auth): add OAuth2 PKCE flow

(use template above)

请求 review

@reviewer1 @reviewer2 ready for review when you have a moment.
@reviewer1 could you take a primary pass; @reviewer2 a secondary?
This is small and self-contained — should be quick.

对 review 回复

# 接受意见
Good catch, fixed in <commit-sha>.
Done. Renamed to `userProfile` everywhere.

# 解释为何不改
I considered that, but went with the current approach because <reason>.
Happy to discuss further if you feel strongly.

# 标记为后续 PR
Agreed this needs work, but it's pre-existing and out of scope here.
Filed #4998 to track.

# 推迟决定
Let me sleep on this and get back to you tomorrow.

合并前的 nudge

Friendly ping — would love to get this in before the v2.5 freeze on Friday.
Bump — anything blocking this from merging?
Last call for comments before I merge.

合并后通知

Merged. Will be in v2.5 (release next Monday).
Thanks @reviewer1 @reviewer2 for the thorough review!

4.8 本章小结

下一章我们专门研究 Code Review 这个最考验英语功底的协作场景。