Git — Pull Requests & Code Review
Pull requests (PRs) are the cornerstone of collaborative code review on GitHub, GitLab, and Bitbucket. A PR is a proposal to merge changes from one branch into another, accompanied by discussion, review, and automated checks.
Effective PR workflows combine technical Git practices with human processes — clear descriptions, focused changes, respectful reviews, and automated validation. This section covers the complete lifecycle from creation to merge.
The standard PR workflow moves through distinct stages from local development to production merge.
| 1 | # Stage 1: Create a feature branch from main |
| 2 | git checkout main |
| 3 | git pull origin main |
| 4 | git checkout -b feat/user-authentication |
| 5 | |
| 6 | # Stage 2: Make changes with atomic commits |
| 7 | git add src/auth/ |
| 8 | git commit -m "Add JWT token validation middleware" |
| 9 | git add src/api/ |
| 10 | git commit -m "Add login endpoint with rate limiting" |
| 11 | |
| 12 | # Stage 3: Keep branch up to date |
| 13 | git fetch origin |
| 14 | git rebase origin/main |
| 15 | # Resolve conflicts if any |
| 16 | |
| 17 | # Stage 4: Push and create PR |
| 18 | git push -u origin feat/user-authentication |
| 19 | |
| 20 | # Stage 5: Address review feedback |
| 21 | # ... make changes, add commits ... |
| 22 | git add . |
| 23 | git commit -m "Address PR feedback: fix error handling" |
| 24 | git push |
| 25 | |
| 26 | # Stage 6: Squash before merge (if required) |
| 27 | git rebase -i origin/main |
| 28 | # ... squash fixup commits ... |
| 29 | |
| 30 | # Stage 7: Merge (via GitHub UI or CLI) |
| 31 | gh pr merge --squash |
info
A well-crafted PR reduces review time, catches issues earlier, and serves as documentation for future developers. Follow these practices for every PR.
PR Title & Description
The title should be a clear, concise summary. The description should explain the what, why, and how.
| 1 | ## Title format: |
| 2 | feat: Add JWT authentication to API endpoints |
| 3 | |
| 4 | ## Description template: |
| 5 | ### Summary |
| 6 | Implements JWT token-based authentication for all |
| 7 | protected API routes. Replaces the previous session-based |
| 8 | auth for better scalability. |
| 9 | |
| 10 | ### Changes |
| 11 | - Add JWT middleware for token validation |
| 12 | - Create login/signup endpoints |
| 13 | - Add token refresh mechanism |
| 14 | - Update all protected routes |
| 15 | |
| 16 | ### Testing |
| 17 | - Unit tests for middleware: npm test auth/middleware |
| 18 | - Integration tests for login flow |
| 19 | - Manual testing with Postman collection |
| 20 | |
| 21 | ### Screenshots |
| 22 | [Link to screenshots or screen recording] |
| 23 | |
| 24 | ### Related Issues |
| 25 | Closes #123, #456 |
PR Size Guidelines
PR size directly impacts review quality. Smaller PRs get reviewed faster and find more bugs.
| 1 | # Ideal PR characteristics: |
| 2 | - Single concern (one feature, fix, or refactor) |
| 3 | - Under 400 lines changed (ideally < 200) |
| 4 | - Less than 10 files modified |
| 5 | - No unrelated formatting changes |
| 6 | - Includes tests for new code |
| 7 | |
| 8 | # When PRs get too large: |
| 9 | 400-800 lines → Schedule a review session |
| 10 | 800+ lines → Break into multiple PRs |
| 11 | 1500+ lines → Team rarely reviews thoroughly |
best practice
Code review is a collaborative quality gate. Both authors and reviewers have responsibilities to make the process effective and respectful.
Reviewer Checklist
Review Etiquette
| 1 | # Example review comment |
| 2 | **Reviewer:** |
| 3 | > src/auth/middleware.ts:45 — The token blacklist check |
| 4 | > here creates a database query on every request. Since |
| 5 | > blacklisted tokens are rare, consider using an in-memory |
| 6 | > cache (Redis) instead. This will keep latency under 5ms. |
| 7 | |
| 8 | **Author response:** |
| 9 | Good catch. Switched to Redis cache with 10-minute TTL. |
| 10 | Updated in commit a1b2c3d. |
| 11 | |
| 12 | **Reviewer:** |
| 13 | nit: src/utils/format.ts:12 — Use template literals |
| 14 | instead of string concatenation for consistency with |
| 15 | the rest of the codebase. |
| 16 | |
| 17 | **Author:** |
| 18 | Fixed in e4f5g6h. |
best practice
GitHub offers three merge strategies when merging a PR. Each affects the base branch's history differently.
| Strategy | History | When to Use |
|---|---|---|
| Create merge commit | Preserves all commits, adds merge commit | Feature branches with meaningful commits |
| Squash and merge | Combines all commits into one | Messy WIP commits, single-feature PRs |
| Rebase and merge | Applies commits individually onto base | Linear history preference |
| 1 | # Squash and merge (GitHub UI) - all commits become one |
| 2 | # Before: feat/* ----a----b----c |
| 3 | # After: main ----a----b----c----[squash commit] |
| 4 | |
| 5 | # Rebase and merge (GitHub UI) - individual commits |
| 6 | # Before: feat/* ----a----b----c |
| 7 | # After: main ----a----b----c |
| 8 | |
| 9 | # Manual squash via interactive rebase: |
| 10 | git rebase -i origin/main |
| 11 | # Change "pick" to "squash" for fixup commits |
| 12 | # Write a clean commit message |
| 13 | |
| 14 | # Manual merge with --squash: |
| 15 | git checkout main |
| 16 | git merge --squash feature-branch |
| 17 | git commit -m "Add login feature" |
| 18 | |
| 19 | # Require linear history (GitHub setting): |
| 20 | # Settings > Branches > Require linear history |
info
Merge conflicts on GitHub occur when your branch and the target branch have diverged. GitHub highlights conflicted files in the PR UI, but resolution must happen locally.
| 1 | # When GitHub shows "This branch has conflicts" ... |
| 2 | |
| 3 | # Option 1: Rebase your branch onto target |
| 4 | git checkout feat/user-auth |
| 5 | git fetch origin |
| 6 | git rebase origin/main |
| 7 | # Fix conflicts, then: |
| 8 | git add resolved-file.ts |
| 9 | git rebase --continue |
| 10 | git push --force-with-lease |
| 11 | |
| 12 | # Option 2: Merge target into your branch |
| 13 | git checkout feat/user-auth |
| 14 | git fetch origin |
| 15 | git merge origin/main |
| 16 | # Fix conflicts, then: |
| 17 | git add resolved-file.ts |
| 18 | git commit |
| 19 | git push |
| 20 | |
| 21 | # Option 3: Use GitHub's web editor |
| 22 | # For simple conflicts, GitHub offers a web-based |
| 23 | # conflict editor under "Resolve conflicts" button. |
| 24 | # Only suitable for small, straightforward conflicts. |
warning
Continuous Integration (CI) checks run automatically on every PR push. They validate code quality, test coverage, security vulnerabilities, and build success before merging.
| 1 | # Example GitHub Actions workflow for PR checks |
| 2 | name: PR Checks |
| 3 | on: pull_request |
| 4 | |
| 5 | jobs: |
| 6 | lint: |
| 7 | runs-on: ubuntu-latest |
| 8 | steps: |
| 9 | - uses: actions/checkout@v4 |
| 10 | - run: npm ci |
| 11 | - run: npm run lint |
| 12 | |
| 13 | test: |
| 14 | runs-on: ubuntu-latest |
| 15 | steps: |
| 16 | - uses: actions/checkout@v4 |
| 17 | - run: npm ci |
| 18 | - run: npm test |
| 19 | - run: npm run test:coverage |
| 20 | |
| 21 | build: |
| 22 | runs-on: ubuntu-latest |
| 23 | steps: |
| 24 | - uses: actions/checkout@v4 |
| 25 | - run: npm ci |
| 26 | - run: npm run build |
best practice
Draft pull requests signal that work is in progress and not ready for final review. They are useful for early design feedback, previewing CI results, and collaborating on complex changes.
| 1 | # Create a draft PR via GitHub CLI |
| 2 | gh pr create --draft --title "WIP: Auth refactor" --body "Early design feedback wanted" |
| 3 | |
| 4 | # Mark a PR as ready for review |
| 5 | gh pr ready |
| 6 | |
| 7 | # Draft PR characteristics: |
| 8 | # - Cannot be merged |
| 9 | # - Does not request reviews automatically |
| 10 | # - CI checks still run |
| 11 | # - Clearly marked as "Draft" in the UI |
| 12 | # - Team members can still comment |
| 13 | |
| 14 | # When to use draft PRs: |
| 15 | # - Design exploration: "Is this approach right?" |
| 16 | # - CI preview: "Check if tests pass on this idea" |
| 17 | # - Collaborative WIP: "Help me finish this" |
| 18 | # - Blocked PR: "Waiting on dependency, but code is visible" |
pro tip
GitHub Actions and other tools can automate PR management tasks, keeping the workflow efficient and enforcing team policies.
| 1 | # Auto-label PRs based on branch prefix |
| 2 | name: PR Labeler |
| 3 | on: |
| 4 | pull_request: |
| 5 | types: [opened] |
| 6 | |
| 7 | jobs: |
| 8 | label: |
| 9 | runs-on: ubuntu-latest |
| 10 | steps: |
| 11 | - uses: actions/labeler@v5 |
| 12 | with: |
| 13 | repo-token: ${{ secrets.GITHUB_TOKEN }} |
| 14 | |
| 15 | # PR checklist automation |
| 16 | name: PR Checklist |
| 17 | on: |
| 18 | pull_request: |
| 19 | types: [opened, edited] |
| 20 | |
| 21 | jobs: |
| 22 | checklist: |
| 23 | runs-on: ubuntu-latest |
| 24 | steps: |
| 25 | - uses: mheap/require-checklist-action@v2 |
| 26 | with: |
| 27 | requireChecklist: true |
| 28 | |
| 29 | # Auto-merge when conditions met |
| 30 | name: Auto Merge |
| 31 | on: |
| 32 | pull_request_review: |
| 33 | types: [submitted] |
| 34 | |
| 35 | jobs: |
| 36 | automerge: |
| 37 | runs-on: ubuntu-latest |
| 38 | if: github.event.review.state == 'approved' |
| 39 | steps: |
| 40 | - run: gh pr merge --auto --squash "$PR_URL" |
| 41 | env: |
| 42 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 43 | PR_URL: ${{ github.event.pull_request.html_url }} |
For Authors
For Reviewers
pro tip
✗ Giant PRs
+2,500 additions, 85 files changedLarge PRs get superficial reviews. Break them into logical chunks (200-400 lines each). Use stacked PRs for sequential dependencies.
✗ Lazy commit messages
git commit -m "fix stuff" / "update" / "changes"Commit messages in PRs become part of the permanent history. Write descriptive messages. Squash and rewrite before merging if needed.
✗ Review burnout
10 open PRs, 5 unreviewed for 3 daysSet aside dedicated review time daily. Rotate reviewers to share context. Consider a "no PR left behind" policy with daily standup check-ins.
✗ Merging without CI passing
Bypassing branch protection rulesBroken PRs that bypass CI create cascading failures. Require all checks to pass in branch protection settings. Allow admin bypass only for emergencies with documented exceptions.