Pull Requests & Code Review

TL;DR

A pull request (PR) is a proposal to merge your branch into another. It's where code review, discussion, and CI checks happen. Use branch protection to enforce reviews, CODEOWNERS for auto-assignment, and choose between merge commit, squash, or rebase merge based on your history preferences.

Explain Like I'm 12

Imagine you wrote an essay and want to add it to a shared class project. Instead of just pasting it in, you hand it to your teacher for review. The teacher reads it, leaves comments ("nice intro!", "fix this paragraph"), and you make edits. Once approved, it gets added to the project. That's a pull request.

The PR Workflow

Pull requests are the heart of GitHub collaboration. Here's the lifecycle from branch creation to merge.

PR lifecycle diagram: create branch, push commits, open PR, automated CI checks run, reviewer comments, author addresses feedback, approval, merge to main

Creating a Pull Request

After pushing your branch, create a PR on GitHub or via the CLI:

# Push your branch
git push origin feature/user-auth

# Create PR with GitHub CLI (gh)
gh pr create \
  --title "Add user authentication" \
  --body "## Summary
- Add JWT-based auth middleware
- Add login/signup endpoints
- Add password hashing with bcrypt

## Test plan
- [ ] Unit tests pass
- [ ] Manual login flow tested"
Tip: A great PR description includes: (1) What changed and why, (2) How to test it, (3) Screenshots for UI changes. Reviewers shouldn't have to read every line of code to understand the intent.

PR Templates

Create .github/pull_request_template.md in your repo to auto-fill the PR description:

## Summary
<!-- What does this PR do? -->

## Changes
-

## Test Plan
- [ ]

## Screenshots
<!-- If applicable -->

Code Review Best Practices

As a Reviewer

  • Review within 24 hours — don't block teammates
  • Focus on logic, not style — let linters handle formatting
  • Ask questions, don't command — "What if this throws?" not "Fix this"
  • Approve with nits — don't block on trivial issues
  • Look for: bugs, security issues, missing tests, unclear naming, edge cases

As an Author

  • Keep PRs small — under 400 lines changed. Split large features into stacked PRs
  • Self-review first — read your own diff before requesting review
  • Respond to all comments — even if just "Done" or "Good point, fixed"
  • Don't force-push during review — it hides the diff between review rounds
Info: GitHub supports three review actions: Comment (feedback without approval/rejection), Approve (looks good), and Request changes (must fix before merging).

Branch Protection Rules

Branch protection prevents direct pushes to important branches. Configure in Settings → Branches → Branch protection rules.

RuleWhat It DoesWhen to Use
Require PR reviewsAt least N approvals before mergeAlways on main
Require status checksCI must pass before mergeWhen you have CI/CD
Require linear historyOnly squash or rebase merges allowedClean history preference
Require signed commitsAll commits must be GPG-signedHigh-security projects
Include administratorsRules apply to admins tooEnforce for everyone
Restrict pushesOnly certain people/teams can pushCritical branches
Warning: Without branch protection on main, anyone with write access can push directly — bypassing code review and CI. Always enable it for production branches.

CODEOWNERS

The CODEOWNERS file automatically assigns reviewers based on which files a PR touches. Place it in .github/CODEOWNERS:

# .github/CODEOWNERS
# Default owner for everything
*                    @org/platform-team

# Frontend code
/src/components/     @org/frontend-team
*.css                @org/frontend-team

# Backend API
/src/api/            @org/backend-team

# Infrastructure
/terraform/          @org/devops-team
Dockerfile           @org/devops-team

# Documentation
/docs/               @techwriter
Tip: Combined with branch protection's "Require review from Code Owners," this ensures the right people always review changes to their domain.

Merge Strategies on GitHub

When merging a PR, GitHub offers three options:

StrategyResultBest For
Merge commitPreserves all commits + adds a merge commitFull history, open-source projects
Squash and mergeCombines all PR commits into oneClean history, WIP-heavy branches
Rebase and mergeReplays commits onto base (no merge commit)Linear history without squashing
Info: You can restrict which merge methods are available in repo settings → General → Pull Requests. Many teams only allow "Squash and merge" for a clean single-commit-per-feature history.

PR Management with GitHub CLI

# List open PRs
gh pr list

# View a specific PR
gh pr view 42

# Check out a PR locally
gh pr checkout 42

# Approve a PR
gh pr review 42 --approve

# Merge a PR (squash)
gh pr merge 42 --squash --delete-branch

# Create PR and auto-fill from commits
gh pr create --fill

Test Yourself

What's the difference between "Squash and merge" and "Merge commit" on GitHub?

Merge commit preserves all individual commits from the PR and adds a merge commit. Squash and merge combines all PR commits into a single commit on the target branch. Squash creates a cleaner history; merge commit preserves granular development history.

What does the CODEOWNERS file do?

CODEOWNERS maps file paths to GitHub users/teams. When a PR modifies matching files, those owners are automatically added as reviewers. Combined with branch protection, it ensures domain experts always review changes to their code.

Name 3 branch protection rules you'd enable on main.

(1) Require pull request reviews — at least 1 approval. (2) Require status checks to pass — CI must be green. (3) Include administrators — rules apply to everyone, no exceptions. Optional: require linear history, require signed commits.

Why should PRs be kept small (under 400 lines)?

Small PRs are: (1) Faster to review — reviewers can focus deeply. (2) Less risky — smaller changes = smaller blast radius. (3) Faster to merge — fewer conflicts with others' work. (4) Better feedback — reviewers are more thorough on short diffs. Studies show review quality drops sharply above 400 lines.

How do you check out someone else's PR to test it locally?

Use the GitHub CLI: gh pr checkout 42 (where 42 is the PR number). This fetches the PR branch and switches to it locally. Without the CLI: git fetch origin pull/42/head:pr-42 && git checkout pr-42.

Interview Questions

Your team's PRs are averaging 1,000+ lines and taking days to review. How would you fix this?

Implement stacked PRs: break features into sequential, small PRs that build on each other. Set a 400-line guideline. Use draft PRs for early feedback. Add PR templates to ensure good descriptions. Automate style/lint checks so reviewers focus on logic. Track PR metrics (time to review, size) to maintain accountability.

Explain the fork-and-PR model used in open-source projects.

Contributors fork the repo (create their own copy), make changes on their fork, then open a PR from their fork to the original repo. This lets anyone contribute without needing write access. Maintainers review and merge. The contributor keeps their fork in sync with git remote add upstream + git pull upstream main.

How would you set up required reviews for a monorepo where different teams own different directories?

Use CODEOWNERS to map directories to teams (e.g., /backend/ @backend-team, /frontend/ @frontend-team). Enable branch protection with "Require review from Code Owners." This ensures a PR touching /backend/ needs backend-team approval, and a cross-cutting PR needs approval from all affected teams.