Overview
A pull request is the handoff point between a branch and main. A well-run PR process produces a legible main history, clear reviewer responsibility, and no surprise regressions. A poorly run process produces a log full of “fix”, “wip”, and “update stuff” commits with no trail for debugging. This page covers the mechanics of a PR from open to merge. Read github for the branching model and github-branch-protection for the rules enforced on main.
Use Conventional Commits for PR titles and squashed commit messages
Every PR title follows the Conventional Commits format because the squash-merged title becomes the commit message on main.
feat(tooling): add GitHub CLI cheatsheet
fix(ops): correct CNAME plugin path for custom domain
chore(deps): bump actions/checkout to v4
docs(coding): add Python typing page
The type signals intent: feat for new behavior, fix for bug corrections, chore for maintenance, docs for documentation, refactor for structural changes with no behavior delta, test for test-only changes. The scope names the area of the codebase. Keep the summary under 72 characters. See git for the full commit message rules.
Squash merge by default; preserve history only when it earns it
Configure the repository to default to squash merge and disable the other strategies to prevent divergent team behavior. Squash merge collapses a branch into one commit on main with the PR title as the subject.
Use merge commits only for long-running branches where the individual commits carry meaning worth preserving in the mainline log. That scenario is rare. A branch alive for less than a week earns a squash.
Disable rebase merge to avoid a three-way split between merge, squash, and rebase that confuses contributors about what history will look like after they click the button.
Open PRs as drafts; promote when CI is green
Open the PR as a draft on the first push. This signals “in progress” and prevents reviewers from starting a review that will change. Promote to ready when all CI checks pass and the description is complete.
A ready PR description has two required sections: a summary of what changed and why, and a test plan the reviewer can follow. The test plan is a checklist. “Manually tested” is not a test plan; “Opened /tooling/github-actions and verified all links resolve” is.
Link the issue the PR closes with Closes #123 in the description body. GitHub closes the issue automatically on merge. If no issue exists for a trivial change, skip the link; do not create a placeholder issue.
Route reviews with CODEOWNERS
Create .github/CODEOWNERS to map file paths to required reviewers. GitHub adds the matching owners as required reviewers when a PR touches those paths.
# Whole repo default
* @org/core-team
# Ops files require a second ops owner
/content/ops/ @org/ops-team
# CI workflow changes require a CI owner
/.github/ @org/devops-team
CODEOWNERS entries are evaluated top-to-bottom; the last matching rule wins. Keep the file short. Routing every directory to a different team creates overhead that slows merges without improving review quality.
Enable auto-merge for PRs that are ready and fully green
Auto-merge merges the PR automatically as soon as all required checks pass and the required approvals are met. Enable it on PRs that represent complete, approved work waiting on CI.
gh pr merge --auto --squashAuto-merge is safe when github-branch-protection enforces required status checks and required reviewers. Without those guards, auto-merge merges a PR the moment it is approved, regardless of CI state.
Review code, not authors; approve only when confident
A PR review is a code read, not a social transaction. Approve when you have read the diff and believe it is correct, complete, and maintainable. Comment on lines you have questions about. Request changes when you see a bug, a missing test, or a design choice that needs discussion.
Avoid approving with comments that imply the code needs changes; that puts the author in an ambiguous state. Either request changes or approve and trust the author to address your comments before merge.
A “ship it” comment with no code read is noise. A short “LGTM, the cache key hash is correct, one nit on the comment in line 42” is useful.