How to write good commit messages and pull requests — a team guide
This will be a practical, structured post focused on clarity, consistency, and real-world examples.
Clear commit messages and pull requests are not just “nice to have”-they directly affect how fast your team can review, debug, and ship. When done well, they turn your version history into documentation. When done poorly, they create friction and confusion.
Why this matters
A good commit history answers three questions quickly:
- What changed?
- Why did it change?
- How does it relate to the rest of the work?
A strong pull request (PR) answers:
- What problem is being solved?
- How was it solved?
- What should reviewers focus on?
If your teammates can answer those without asking you, you are doing it right.
Writing clear commit messages
Use a consistent structure
A widely adopted format is Conventional Commits:
<type>(optional-scope): short summary
Common types:
- feat: New feature
- fix: Bug fix
- refactor: Code change without behavior change
- chore: Maintenance (deps, configs)
- docs: Documentation
- test: Tests
Example:
feat(auth): add password reset via email
Keep the summary tight
- Use present tense: “add”, not “added”
- Keep it under ~72 characters
- Focus on what, not how
Bad:
fixed stuff
Better:
-
fix(cart): prevent duplicate items on rapid click### Writing descriptive commit bodies
Use the body when the change is not obvious.
Structure:
- Why the change was needed
- What approach was taken
- Any trade-offs or edge cases
Example:
fix(cart): prevent duplicate items on rapid click
Users could add the same item multiple times by clicking quickly
before the UI updated.
This introduces a temporary client-side lock while the add-to-cart
request is in flight.
Does not yet handle server-side race conditions.
Link commits to issues
Always connect work to a ticket or issue when possible.
Closes #142Fixes PROJ-87
Example:
feat(search): add fuzzy matching for product names
Improves search results when users misspell product names.
Closes #142
This creates traceability between code and intent.
Commit strategy: squash vs merge vs rebase
Squash and merge
Best for:
- Clean history
- Feature branches with many small commits
Result:
- One commit per PR
Pros:
- Easy to read history
- Reduces noise
Cons:
- Loses granular commit detail
Merge commits
Best for:
- Preserving full development context
Result:
- Keeps all commits + a merge commit
Pros:
- Full traceability
- Useful for complex features
Cons:
- History becomes noisy
Rebase and merge
Best for:
- Linear history without merge commits
Pros:
- Clean, readable history
- Keeps individual commits
Cons:
- Requires clean commit hygiene
- Can rewrite history if misused
Practical recommendation
- Use squash for most feature PRs
- Use merge commits for long-lived or complex branches
- Use rebase for teams comfortable with strict commit discipline ### Crafting pull request descriptions
A good PR description removes guesswork for reviewers.
Core structure
Include:
- What: Brief summary of the change
- Why: Problem being solved
- How: Key implementation details
- Scope: What is included and what is not
Example:
### What
Adds password reset functionality via email.
### Why
Users currently cannot recover accounts without contacting support.
### How
- Added reset token generation on backend
- Integrated email service for reset links
- Created reset form UI
### Scope
Includes backend + frontend changes.
Does not include rate limiting yet.
Use screenshots and visuals
If your PR affects UI, include before/after visuals.
- Screenshots for static UI
- GIFs for interactions
- Short videos for flows
Example note:
- “Before: error message not visible”
- “After: inline validation shown under input”
This reduces reviewer effort dramatically.
Include testing notes
Tell reviewers exactly how to verify your changes.
Example:
### Testing
1. Go to /login
2. Click "Forgot password"
3. Enter a valid email
4. Check email for reset link
5. Verify password update works
Edge case:
- Try invalid email and confirm error message appears
This ensures consistent validation and speeds up approval.
Add a review checklist
Help reviewers focus on what matters.
Example:
### Review checklist
- Logic correctness
- Edge cases handled
- Naming clarity
- No unnecessary complexity
- Tests included
Optional but helpful for larger PRs.
Make PRs easier to review
Keep PRs small
- Aim for under ~400 lines when possible
- Split large features into smaller PRs
Separate concerns
Do not mix:
- Feature logic
- Refactoring
- Formatting changes
Use meaningful commits inside PRs
Even if squashing later, clean commits help reviewers follow along.
Real examples
Weak commit + PR
Commit:
update stuff
PR:
- “This adds the feature we talked about.”
Problems:
- No context
- No traceability
- No guidance for reviewers ### Strong commit + PR
Commit:
feat(profile): allow users to upload avatar images
PR:
### What
Adds avatar upload functionality to user profiles.
### Why
Users want to personalize their accounts.
### How
- Added file upload endpoint
- Integrated cloud storage
- Updated profile UI to display avatars
### Testing
1. Go to profile page
2. Upload an image
3. Refresh page and confirm persistence
### Notes
- Max file size: 5MB
- Only JPEG and PNG supported
Closes #203
Outcome:
- Reviewer understands everything without asking questions
- Faster approval
- Better long-term documentation ### Final principle
Write for the person reviewing your code at 5 PM on a Friday-or for yourself six months later. If they can understand your work without digging, you have done it well.
Would you like this adapted into a team checklist or a reusable PR template?
Rizwan Saleem — https://rizwansaleem.co
Top comments (0)