Home/Blog/Pull Request Best Practices: Code Review, Templates, and Merge Strategies
Software Engineering

Pull Request Best Practices: Code Review, Templates, and Merge Strategies

Master pull request workflows with best practices for code review, PR templates, merge strategies, automated checks, and team collaboration patterns.

By Inventive HQ Team
Pull Request Best Practices: Code Review, Templates, and Merge Strategies

Pull requests are where code becomes software. They're the gateway between individual work and shared codebases, the checkpoint for quality, and the primary collaboration touchpoint for development teams. This guide covers how to create, review, and merge pull requests effectively.

Anatomy of a Great Pull Request

┌─────────────────────────────────────────────────────────────┐
│                     PULL REQUEST                             │
├─────────────────────────────────────────────────────────────┤
│  TITLE: [Type] Brief description of change                   │
│         feat: Add user authentication flow                   │
├─────────────────────────────────────────────────────────────┤
│  DESCRIPTION:                                                │
│  ├── What changed and WHY                                   │
│  ├── How to test                                            │
│  ├── Screenshots (if UI)                                    │
│  ├── Related issues: Fixes #123                             │
│  └── Checklist                                              │
├─────────────────────────────────────────────────────────────┤
│  METADATA:                                                   │
│  ├── Reviewers: @teammate                                   │
│  ├── Labels: feature, needs-review                          │
│  ├── Milestone: v2.0                                        │
│  └── Project: Q1 Sprint                                     │
├─────────────────────────────────────────────────────────────┤
│  CHECKS:                                                     │
│  ├── ✓ CI/CD Pipeline                                       │
│  ├── ✓ Code Coverage                                        │
│  ├── ✓ Security Scan                                        │
│  └── ✓ Required Reviewers                                   │
└─────────────────────────────────────────────────────────────┘

PR Title Conventions

Use consistent prefixes for scannable history:

PrefixUsageExample
feat:New featurefeat: Add dark mode toggle
fix:Bug fixfix: Resolve login timeout issue
docs:Documentationdocs: Update API reference
refactor:Code restructurerefactor: Extract auth logic to service
test:Test changestest: Add unit tests for user service
chore:Maintenancechore: Update dependencies
perf:Performanceperf: Optimize database queries
style:Formattingstyle: Fix linting errors

Pull Request Templates

Create .github/PULL_REQUEST_TEMPLATE.md in your repository:

## Description
<!-- What changed and why? Link to design docs if applicable -->

## Type of Change
- [ ] Bug fix (non-breaking change fixing an issue)
- [ ] New feature (non-breaking change adding functionality)
- [ ] Breaking change (fix or feature causing existing functionality to change)
- [ ] Documentation update

## How Has This Been Tested?
<!-- Describe the tests you ran and how to reproduce -->
- [ ] Unit tests
- [ ] Integration tests
- [ ] Manual testing

## Screenshots (if applicable)
<!-- Add screenshots for UI changes -->

## Checklist
- [ ] My code follows the project's style guidelines
- [ ] I have performed a self-review
- [ ] I have added tests that prove my fix/feature works
- [ ] New and existing unit tests pass locally
- [ ] I have updated documentation as needed
- [ ] My changes generate no new warnings

## Related Issues
<!-- Link related issues: Fixes #123, Relates to #456 -->

Multiple Templates for Different PR Types

Create .github/PULL_REQUEST_TEMPLATE/ directory:

.github/
└── PULL_REQUEST_TEMPLATE/
    ├── feature.md
    ├── bugfix.md
    ├── hotfix.md
    └── documentation.md

Contributors select the appropriate template when creating PRs.

Code Review Best Practices

For Authors

Before requesting review:

┌─────────────────────────────────────────────────────────────┐
│                   SELF-REVIEW CHECKLIST                      │
├─────────────────────────────────────────────────────────────┤
│  □ Review your own diff in "Files changed" tab              │
│  □ Remove debug code, console.logs, commented code          │
│  □ Check for typos in strings, comments, variable names     │
│  □ Verify tests pass locally                                │
│  □ Run linter and fix issues                                │
│  □ Add comments on complex sections to guide reviewers      │
│  □ Ensure PR description explains the WHY                   │
│  □ Link related issues                                      │
│  □ Request specific reviewers who know this code            │
└─────────────────────────────────────────────────────────────┘

Responding to feedback:

  • Respond to every comment (even just "Done" or "Good catch")
  • Explain your reasoning if you disagree
  • Batch related changes into single commits
  • Re-request review after addressing feedback

For Reviewers

Review mindset:

┌─────────────────────────────────────────────────────────────┐
│                    REVIEW PRIORITIES                         │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   1. CORRECTNESS (Must fix)                                 │
│      └── Does it work? Logic errors? Edge cases?            │
│                                                              │
│   2. SECURITY (Must fix)                                    │
│      └── Injection risks? Auth issues? Data exposure?       │
│                                                              │
│   3. DESIGN (Should discuss)                                │
│      └── Architecture fit? Maintainability? Patterns?       │
│                                                              │
│   4. READABILITY (Nice to have)                             │
│      └── Clear names? Good comments? Understandable?        │
│                                                              │
│   5. STYLE (Automate instead)                               │
│      └── Formatting? Linting rules? Let tools handle it     │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Effective review comments:

<!-- Bad: Vague -->
This is wrong.

<!-- Good: Specific with suggestion -->
This will throw a NullPointerException if `user` is null (line 42).
Consider adding a null check:
```java
if (user != null && user.isActive()) {

Use camelCase.

Our codebase uses camelCase for consistency with the existing API client. (Better: configure linter to catch this automatically)


**Review comment prefixes:**

| Prefix | Meaning | Action Required |
|--------|---------|-----------------|
| `blocking:` | Must fix before merge | Yes |
| `suggestion:` | Consider this approach | Optional |
| `question:` | Need clarification | Response needed |
| `nit:` | Minor nitpick | Optional |
| `praise:` | Good work! | None |

### Review Workflow

┌─────────────────────────────────────────────────────────────┐ │ REVIEW LIFECYCLE │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Author creates PR │ │ │ │ │ ▼ │ │ ┌─────────────────┐ │ │ │ Request Review │───► Reviewers notified │ │ └────────┬────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────┐ │ │ │ Review Period │───► Comment, Approve, or │ │ └────────┬────────┘ Request Changes │ │ │ │ │ ┌───────┴───────┐ │ │ ▼ ▼ │ │ Approved Changes Requested │ │ │ │ │ │ │ ▼ │ │ │ Author addresses │ │ │ feedback │ │ │ │ │ │ │ ▼ │ │ │ Re-request review │ │ │ │ │ │ └───────┬───────┘ │ │ ▼ │ │ ┌─────────────────┐ │ │ │ MERGE │ │ │ └─────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘


## Merge Strategies

### Comparison

┌─────────────────────────────────────────────────────────────┐ │ MERGE STRATEGIES │ ├─────────────────────────────────────────────────────────────┤ │ │ │ MERGE COMMIT │ │ main: A───B───C───────M─── │ │ \ / │ │ feature: D───E───F │ │ │ │ • Preserves all commits │ │ • Shows branch history │ │ • Creates merge commit (M) │ │ │ ├─────────────────────────────────────────────────────────────┤ │ │ │ SQUASH AND MERGE │ │ main: A───B───C───S─── │ │ │ │ • Combines all PR commits into one (S) │ │ • Clean, linear main history │ │ • Loses individual commit granularity │ │ │ ├─────────────────────────────────────────────────────────────┤ │ │ │ REBASE AND MERGE │ │ main: A───B───C───D'───E'───F'─── │ │ │ │ • Replays commits on top of main │ │ • Linear history, no merge commits │ │ • Rewrites commit hashes │ │ │ └─────────────────────────────────────────────────────────────┘


### When to Use Each

| Strategy | Best For | Avoid When |
|----------|----------|------------|
| **Merge commit** | Feature branches with meaningful commit history | Main has many merge commits already |
| **Squash and merge** | Feature branches with messy commits (WIP, fixup) | Commits should be preserved individually |
| **Rebase and merge** | Clean branches with atomic commits | Commits are already on main (double commits) |

### Configuring Default Strategy

In GitHub repository settings:

1. Go to **Settings > General**
2. Scroll to **Pull Requests**
3. Configure allowed merge methods:
   - Allow merge commits
   - Allow squash merging ✓ (default for most teams)
   - Allow rebase merging

You can also set a default merge button behavior.

## Automated Checks

### Required Status Checks

Configure in **Settings > Branches > Branch protection rules**:

```yaml
# Example CI workflow for PR checks
name: PR Checks
on:
  pull_request:
    branches: [main]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run linter
        run: npm run lint

  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: npm test

  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build
        run: npm run build

  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run security scan
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

PR Size Warnings

Automatically warn on large PRs:

name: PR Size Check
on: pull_request

jobs:
  size-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Check PR Size
        run: |
          ADDITIONS=$(gh pr view ${{ github.event.pull_request.number }} --json additions -q '.additions')
          DELETIONS=$(gh pr view ${{ github.event.pull_request.number }} --json deletions -q '.deletions')
          TOTAL=$((ADDITIONS + DELETIONS))

          if [ $TOTAL -gt 500 ]; then
            echo "::warning::Large PR detected ($TOTAL lines changed). Consider splitting into smaller PRs."
          fi
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Auto-labeling

Automatically label PRs based on files changed:

# .github/labeler.yml
frontend:
  - 'src/components/**'
  - 'src/pages/**'
  - '**/*.css'

backend:
  - 'src/api/**'
  - 'src/services/**'

documentation:
  - '**/*.md'
  - 'docs/**'

tests:
  - '**/*.test.ts'
  - '**/*.spec.ts'
# .github/workflows/labeler.yml
name: Labeler
on: pull_request

jobs:
  label:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/labeler@v5
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}

CODEOWNERS for Automatic Reviewers

Create .github/CODEOWNERS:

# Default owners for everything
* @org/core-team

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

# Backend code
/src/api/ @org/backend-team
/src/services/ @org/backend-team

# Infrastructure
/terraform/ @org/devops-team
/.github/workflows/ @org/devops-team
Dockerfile @org/devops-team

# Security-sensitive code requires security team review
/src/auth/ @org/security-team @org/backend-team
/src/encryption/ @org/security-team

# Documentation
/docs/ @org/docs-team
*.md @org/docs-team

# Package management
package.json @org/core-team
package-lock.json @org/core-team

Enable CODEOWNERS enforcement:

  1. Settings > Branches > Branch protection
  2. Enable "Require review from Code Owners"

Team Collaboration Patterns

Handling Stacked PRs

When features depend on other PRs not yet merged:

┌─────────────────────────────────────────────────────────────┐
│                    STACKED PRs                               │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   main                                                       │
│     │                                                        │
│     └──► feature/auth-base (PR #1)                          │
│              │                                               │
│              └──► feature/auth-oauth (PR #2)                │
│                      │                                       │
│                      └──► feature/auth-ui (PR #3)           │
│                                                              │
│   Workflow:                                                  │
│   1. Create PR #1 against main                              │
│   2. Create PR #2 against feature/auth-base                 │
│   3. Create PR #3 against feature/auth-oauth                │
│   4. Review and merge PR #1                                 │
│   5. Retarget PR #2 to main (GitHub auto-updates)           │
│   6. Review and merge PR #2                                 │
│   7. Retarget PR #3 to main                                 │
│   8. Review and merge PR #3                                 │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Draft PRs for Early Feedback

Use draft PRs when:

  • You want early feedback on approach
  • CI needs to run during development
  • You're sharing progress with stakeholders
  • You need to collaborate on complex changes
# Create draft PR with GitHub CLI
gh pr create --draft --title "feat: Add auth" --body "WIP: Authentication system"

# Mark ready for review when done
gh pr ready

Keeping PRs Updated

# Option 1: Merge main into your branch
git checkout feature-branch
git fetch origin
git merge origin/main
git push

# Option 2: Rebase onto main (cleaner but requires force push)
git checkout feature-branch
git fetch origin
git rebase origin/main
git push --force-with-lease

PR Metrics and Team Health

Track these metrics to improve PR workflows:

MetricTargetWhy It Matters
Time to first review< 4 hoursFast feedback keeps developers productive
Review cycles1-2 roundsMore indicates unclear requirements
PR size< 400 LOCLarge PRs get worse reviews
Time to merge< 2 daysLong waits indicate bottlenecks
Review participationBalancedAvoid relying on one reviewer

Measuring with GitHub Insights

# Using GitHub CLI to analyze PR metrics
gh pr list --state merged --json number,createdAt,mergedAt,additions,deletions \
  --jq '.[] | "\(.number): \(.additions + .deletions) lines, \(((.mergedAt | fromdate) - (.createdAt | fromdate)) / 3600 | floor) hours"'

Quick Reference

PR Author Checklist

□ Self-review completed
□ Tests added/updated
□ Documentation updated
□ PR title follows conventions
□ Description explains what and why
□ Screenshots for UI changes
□ Related issues linked
□ Appropriate reviewers assigned

Reviewer Checklist

□ Understand the context (read description first)
□ Check for correctness and edge cases
□ Look for security issues
□ Verify tests are meaningful
□ Assess maintainability
□ Provide actionable feedback
□ Approve, request changes, or comment

Need Expert IT & Security Guidance?

Our team is ready to help protect and optimize your business technology infrastructure.