Pull Request Description Standards
PR Title Conventions
The PR title is the first thing reviewers see. It must communicate the change clearly and concisely.
Format
<type>: <concise description of what changed>
Type Prefixes
| Prefix | Use When |
|---|---|
feat: | Adding new functionality |
fix: | Correcting a bug |
refactor: | Restructuring without behavior change |
docs: | Documentation only |
test: | Adding or updating tests |
chore: | Dependencies, tooling, config |
perf: | Performance improvement |
style: | Formatting, whitespace, naming |
ci: | CI/CD pipeline changes |
revert: | Reverting a previous change |
Title Rules
- •Keep under 72 characters
- •Use imperative mood: "Add search endpoint" not "Added search endpoint" or "Adds search endpoint"
- •Do not end with a period
- •Be specific: "fix: resolve login redirect loop on expired sessions" not "fix: login bug"
- •Include ticket number if applicable: "feat: add user search (PROJ-1234)"
Good vs Bad Titles
Bad: "Updates" Good: "feat: add full-text search to user directory" Bad: "Fix bug" Good: "fix: prevent duplicate charge on payment retry" Bad: "Refactoring the auth module and also fixing some tests and updating docs" Good: "refactor: extract token validation into dedicated service"
PR Description Template
Every PR should follow a structured description. Use this template as a starting point and adapt to your team's needs.
Standard Template
## Summary A 1-3 sentence overview of what this PR does and why. This should be understandable by someone who has not read the code. ## Motivation Why is this change needed? Link to the issue, bug report, or product requirement that motivated this work. Closes #123 ## Changes A bulleted list of the specific changes made: - Added `SearchService` class with full-text query support - Created `/api/users/search` endpoint with pagination - Added search index migration for the users table - Updated API documentation with new endpoint ## Test Plan How was this change tested? Be specific. - [ ] Unit tests for `SearchService` covering empty queries, partial matches, and pagination - [ ] Integration test for the search endpoint with test database - [ ] Manual testing: verified search returns expected results in staging - [ ] Load tested with 10k users in the index ## Screenshots / Recordings If the change has a visual component, include before/after screenshots or a screen recording. For API changes, include example request/response pairs. **Before:** (screenshot or N/A) **After:** (screenshot or N/A) ## Notes for Reviewers Any additional context that will help reviewers: - I chose cursor-based pagination over offset because [reason] - The migration is backward-compatible and can be rolled back - This depends on PR #456 being merged first ## Checklist - [ ] Tests pass locally - [ ] Code follows project style guidelines - [ ] Documentation updated if needed - [ ] No secrets or credentials included - [ ] Migration is reversible (if applicable) - [ ] Breaking changes documented (if applicable)
Minimal Template (for Small Changes)
## Summary Fix null pointer exception when user profile has no avatar set. Closes #789 ## Changes - Added null check in `UserProfile.getAvatarUrl()` - Added test case for users without avatars ## Test Plan - [x] Unit test added for the null avatar case - [x] Existing tests pass
Linking Issues
Automatic Issue Closing
Use GitHub keywords in the PR description body to automatically close issues when the PR merges:
Closes #123 Fixes #456 Resolves #789
- •Place these in the Motivation or Summary section
- •Use one keyword per issue, each on its own line for multiple issues
- •These keywords are case-insensitive
Cross-Repository References
Closes org/other-repo#123
Linking Without Closing
When a PR is related to but does not fully resolve an issue:
Related to #123 Part of #456 See also #789
Draft PRs vs Ready-for-Review
When to Open a Draft PR
Open a draft PR when:
- •You want early feedback on your approach before finishing
- •The work is in progress but you want CI to run
- •You are working on a stacked PR that depends on another PR
- •You want to share progress with the team without requesting formal review
- •You need to collaborate with another developer on the branch
Draft PR Etiquette
- •Prefix the title with
[WIP]or[Draft]in addition to using GitHub's draft status - •State what is done and what remains in the description:
## Status Done: - [x] Database schema and migration - [x] Repository layer Remaining: - [ ] Service layer business logic - [ ] API endpoint - [ ] Tests ## What I Need Feedback On - Is the schema design appropriate for the query patterns we expect? - Should we use a separate table for search metadata?
- •Convert to ready-for-review only when all work is complete
- •Do not request specific reviewers on draft PRs unless you need targeted feedback
Converting to Ready
Before converting a draft PR to ready-for-review:
- • All commits are clean and well-organized
- • Description is complete with all template sections filled
- • CI passes
- • Self-review completed (see checklist below)
- • WIP or Draft prefix removed from title
PR Size Guidelines
Target Size
| Metric | Target | Maximum |
|---|---|---|
| Lines changed | 50-200 | 400 |
| Files changed | 1-8 | 15 |
| Commits | 1-5 | 10 |
| Review time | 15-30 min | 60 min |
Why Small PRs Matter
- •Faster review turnaround (hours not days)
- •Higher quality reviews (reviewers stay focused)
- •Easier to revert if something breaks
- •Less merge conflict risk
- •Better git history for debugging
Breaking Down Large Changes
When a change exceeds size guidelines, split it into a sequence of PRs:
- •Infrastructure first -- Schema changes, new interfaces, configuration
- •Core logic -- Business logic implementation
- •Integration -- Wiring components together, endpoint creation
- •Polish -- Error handling improvements, logging, documentation
Each PR should be independently mergeable and should not break existing functionality.
Acceptable Large PRs
Some PRs are legitimately large:
- •Generated code (migrations, protobuf output, lock files)
- •Bulk rename or reformatting (use a separate commit or PR)
- •Dependency upgrades with lock file changes
- •Initial project scaffolding
Mark these clearly in the description: "Note: This PR is large because it includes generated migration files. The actual hand-written changes are in src/services/ (~80 lines)."
Stacked PRs
Stacked PRs break a large feature into a chain of dependent PRs that build on each other.
When to Use Stacked PRs
- •A feature requires 500+ lines of changes
- •You want reviewers to focus on one logical layer at a time
- •Multiple developers are working on interconnected changes
Stacked PR Workflow
PR #1: feat: add user search database schema
└── PR #2: feat: add search service with query logic
└── PR #3: feat: add search API endpoint and docs
Stacked PR Rules
- •Each PR in the stack must work independently if merged alone (no broken states)
- •Number the PRs and cross-reference them:
## Stack This is PR 2 of 3 in the search feature stack: 1. #100 - Database schema (merged) 2. **#101 - Search service (this PR)** 3. #102 - API endpoint (draft, depends on this PR)
- •Base each PR on the previous PR's branch, not on main:
# PR 1 git checkout -b feature/search-schema # PR 2 (based on PR 1) git checkout -b feature/search-service feature/search-schema # PR 3 (based on PR 2) git checkout -b feature/search-endpoint feature/search-service
- •When an earlier PR is merged, rebase subsequent PRs onto main
- •Review and merge in order -- do not merge PR 3 before PR 1
Updating Stacked PRs After Review
When you need to update PR 1 based on review feedback:
# Make changes on PR 1's branch git checkout feature/search-schema # ... make changes, commit ... # Rebase PR 2 onto updated PR 1 git checkout feature/search-service git rebase feature/search-schema # Rebase PR 3 onto updated PR 2 git checkout feature/search-endpoint git rebase feature/search-service # Force-push all updated branches git push --force-with-lease origin feature/search-schema git push --force-with-lease origin feature/search-service git push --force-with-lease origin feature/search-endpoint
Self-Review Checklist
Before requesting review, go through the PR yourself as if you were the reviewer.
Code Quality
- • No commented-out code left behind
- • No debug logging (console.log, print statements) unless intentional
- • No TODO comments without associated issue numbers
- • Variable and function names are clear and consistent
- • No unnecessary complexity -- could this be simpler?
- • No duplicated logic that should be extracted
Correctness
- • Edge cases handled (null, empty, boundary values)
- • Error cases handled with appropriate messages
- • No race conditions in concurrent code
- • Database queries are efficient (no N+1, proper indexes)
- • Feature flags used for incomplete or experimental features
Security
- • No secrets, API keys, or credentials in the code
- • User input is validated and sanitized
- • Authentication and authorization checks present where needed
- • SQL injection, XSS, and CSRF protections in place
- • Sensitive data is not logged
Testing
- • New code has corresponding tests
- • Tests cover happy path and error cases
- • Tests are deterministic (no flaky tests)
- • Test names clearly describe what they verify
Documentation
- • Public APIs have documentation
- • Complex logic has explanatory comments
- • README updated if setup steps changed
- • CHANGELOG updated for user-facing changes
- • API documentation updated for endpoint changes
Diff Review
- • Reviewed the full diff on GitHub before requesting review
- • No unintended file changes (formatting, unrelated fixes)
- • Commit history is clean and logical
- • No merge commits from pulling main (rebase instead)
Responding to Review Feedback
Etiquette
- •Respond to every comment, even if just with "Done" or "Fixed"
- •If you disagree, explain your reasoning respectfully and propose alternatives
- •Do not resolve comments yourself unless your team convention allows it -- let the reviewer resolve their own comments
- •If a discussion becomes lengthy, move it to a call and summarize the outcome in the PR
- •Push review fixes as new commits during review, squash before merge
Handling Requested Changes
## Review Response - **[Comment about error handling]** Fixed -- added try/catch with proper logging in abc123 - **[Comment about naming]** Renamed `processData` to `validateAndStorePayment` in def456 - **[Comment about test coverage]** Added edge case tests for empty input in ghi789 - **[Comment about architecture]** I'd prefer to keep this in a single service for now -- the traffic doesn't justify the complexity of splitting. Happy to discuss further.
Example: Well-Written PR
# feat: add cursor-based pagination to user search endpoint
## Summary
Replaces the offset-based pagination on `/api/users/search` with cursor-based
pagination to handle our growing user base without performance degradation.
## Motivation
The user directory now has 2M+ records. Offset pagination causes increasingly
slow queries at high page numbers because the database must scan and discard
rows. Cursor-based pagination maintains constant performance regardless of
position.
Closes #1234
## Changes
- Replaced `page` and `per_page` params with `cursor` and `limit`
- Added `next_cursor` and `has_more` to response envelope
- Updated `UserRepository.search()` to use keyset pagination on `(created_at, id)`
- Added database index on `(created_at, id)` for the users table
- Marked old `page` parameter as deprecated with warning header
- Updated API docs with new pagination format
## Test Plan
- [x] Unit tests for cursor encoding/decoding
- [x] Integration tests verifying correct page traversal over 1000 records
- [x] Verified backward compatibility: requests with `page` param still work (with deprecation warning)
- [x] Load test: p99 latency at page 10000 dropped from 2.3s to 45ms
## Notes for Reviewers
- The cursor is a base64-encoded JSON of `{created_at, id}` -- intentionally opaque to clients
- Old `page` parameter will be removed in v3.0 (tracked in #1235)
- Migration `20240115_add_users_pagination_index.sql` is safe to run on production without downtime