11 KiB
Commit Message Guide
A comprehensive guide to writing clear, consistent, and maintainable commit messages using the Conventional Commits format.
Why Good Commit Messages Matter
- Documentation: Git history becomes a changelog
- Code Review: Easier to understand what changed and why
- Debugging: Quickly find when bugs were introduced
- Automation: Enables automatic changelog generation and versioning
- Professionalism: Shows attention to detail and respect for maintainers
Conventional Commits Format
<type>(<scope>): <subject>
[optional body]
[optional footer(s)]
Components
Type (required): Category of change Scope (optional): What section of codebase is affected Subject (required): Brief description of change Body (optional): Detailed explanation Footer (optional): Issue references, breaking changes
Types
Primary Types
feat: New feature for the user
feat(auth): add OAuth2 support for Google and GitHub
fix: Bug fix for the user
fix(api): resolve memory leak in worker shutdown
docs: Documentation changes only
docs(readme): update installation instructions for v2.0
style: Code style changes (formatting, missing semicolons, etc.)
style(components): format code with Prettier
refactor: Code change that neither fixes bug nor adds feature
refactor(auth): extract middleware to separate module
perf: Performance improvement
perf(database): add index to user_id column
test: Adding or updating tests
test(auth): add OAuth flow integration tests
Supporting Types
build: Changes to build system or dependencies
build(deps): upgrade React to v18.2.0
ci: CI configuration changes
ci(github): add automated release workflow
chore: Other changes that don't modify src or test files
chore(deps): update dev dependencies
revert: Revert a previous commit
revert: feat(auth): add OAuth2 support
This reverts commit abc123def456.
Subject Line Rules
The 7 Rules
- Limit to 50 characters (hard limit: 72)
- Use imperative mood ("Add" not "Added" or "Adds")
- Capitalize first word after the colon
- Don't end with punctuation
- Focus on WHAT changed (body explains WHY)
- Be specific but concise
- Start with type (if using Conventional Commits)
Examples
✅ Good:
feat(api): add user authentication endpoint
fix(ui): resolve button alignment on mobile
docs(contributing): clarify PR submission process
refactor(utils): simplify date formatting logic
❌ Bad:
Fixed stuff # Too vague
Added new feature to the authentication system that allows users to login with OAuth # Too long
updated code # Not specific, wrong tense
feat(api): add user authentication. # Don't end with period
Feat(api): add auth # Don't capitalize type
Optional Scope
The scope provides context about what part of the codebase changed.
Common Scopes
feat(auth): ... # Authentication
feat(api): ... # API layer
feat(ui): ... # User interface
feat(database): ... # Database
feat(docs): ... # Documentation
feat(tests): ... # Tests
fix(deps): ... # Dependencies
build(webpack): ... # Build tooling
Project-Specific Scopes
Check the project's recent commits for conventions:
git log --oneline -20
When to Omit Scope
Scope is optional. Omit when:
- Change affects multiple areas
- Project doesn't use scopes
- Scope would be too generic
feat: add dark mode support
docs: update README
Body (Optional)
The body provides detailed explanation of WHAT and WHY (code shows HOW).
Rules
- Separate from subject with blank line
- Wrap at 72 characters per line
- Explain motivation and context
- Contrast with previous behavior
- Use bullet points for multiple points
Example
feat(api): add rate limiting to authentication endpoints
Users can now make up to 100 authentication attempts per hour
per IP address. This prevents brute force attacks while allowing
legitimate users to retry failed login attempts.
Implementation details:
- Uses Redis for distributed rate limiting
- Configurable via RATE_LIMIT_MAX environment variable
- Returns 429 status with Retry-After header when exceeded
- Resets hourly at top of the hour
Previous behavior allowed unlimited attempts, which was identified
as a security vulnerability in audit #456.
When to Include Body
Include a body when:
- Change is complex or non-obvious
- Design decisions need explanation
- Previous behavior needs context
- Multiple related changes in one commit
- Security or performance implications
When to Skip Body
Skip the body when:
- Change is self-explanatory
- Subject line tells the complete story
- Trivial changes (typos, formatting)
Footer (Optional)
Footers provide metadata about the commit.
Breaking Changes
Use BREAKING CHANGE: footer for breaking changes:
feat(api)!: change authentication endpoint path
BREAKING CHANGE: The authentication endpoint has moved from
/api/auth to /api/v2/auth. Update your API calls accordingly.
Migration guide available at docs/migration/v2.md
Note the ! after the type indicates a breaking change.
Issue References
Link issues using keywords for automatic closing:
fix(ui): resolve mobile layout issues
Fixes #123
Closes #456, #789
Relates to #234
Closing Keywords:
Closes #123Fixes #123Resolves #123- Also: close, fix, resolve (case insensitive)
Co-authored-by
Credit co-authors:
feat(api): add GraphQL support
Co-authored-by: Jane Doe <jane@example.com>
Co-authored-by: John Smith <john@example.com>
Other Footers
Reviewed-by: Name <email>
Signed-off-by: Name <email>
Acked-by: Name <email>
See-also: #123
Complete Examples
Example 1: Simple Feature
feat(ui): add dark mode toggle
Adds a toggle button in settings to switch between light and dark
themes. User preference is saved to localStorage and persists
across sessions.
Closes #234
Example 2: Bug Fix
fix(api): prevent race condition in cache invalidation
The cache invalidation logic wasn't thread-safe, causing occasional
race conditions when multiple workers tried to invalidate the same
key simultaneously.
Solution:
- Added mutex locks around the critical section
- Implemented timeout for lock acquisition (5s)
- Added retry logic with exponential backoff
- Updated tests to verify thread-safety
Fixes #456
Example 3: Breaking Change
feat(api)!: migrate to TypeScript and update endpoint contracts
BREAKING CHANGE: All API endpoints now return ISO 8601 date
strings instead of Unix timestamps. Update client code to parse
dates accordingly.
Additionally, authentication now requires JWT tokens in the
Authorization header instead of session cookies.
Migration guide: docs/migration/v3.md
Closes #567
Example 4: Refactoring
refactor(auth): extract middleware to separate module
No functional changes, but auth logic is now easier to test and
maintain. Consolidated duplicate code from 5 route handlers into
reusable middleware functions.
Files affected:
- New: middleware/authenticate.js
- Updated: routes/*.js (5 files)
- Tests: tests/middleware/auth.test.js
Relates to #301 (technical debt epic)
Example 5: Documentation
docs(api): add examples for authentication flows
Added code examples for:
- Basic authentication with username/password
- OAuth2 flow with Google
- API key authentication
- JWT token refresh
Examples include curl commands and JavaScript fetch() snippets.
Closes #678
Example 6: Multiple Related Changes
fix(auth): resolve multiple OAuth edge cases
- Handle expired refresh tokens gracefully
- Prevent account linking when email doesn't match
- Add rate limiting to token refresh endpoint
- Log failed OAuth attempts for security monitoring
Each issue was related to OAuth implementation and fixing them
separately would have caused merge conflicts.
Fixes #123, #456, #789
Tips for Writing Good Commit Messages
Do:
✅ Write in imperative mood ("Add" not "Added")
feat: add user profile page
✅ Be specific about what changed
fix(api): resolve timeout in user search endpoint
✅ Explain WHY in the body
refactor(db): switch to connection pooling
The previous approach created a new connection for each request,
which caused performance issues under load. Connection pooling
reduces overhead and improves response times by 40%.
✅ Use the body for complex changes ✅ Reference issues and PRs ✅ Follow project conventions
Don't:
❌ Use past tense
feat: added user profile page ❌
❌ Be vague
fix: bug fix ❌
update: changes ❌
❌ Write novels in the subject line
feat(api): add new user authentication endpoint with OAuth2 support for Google and GitHub that also includes rate limiting ❌
❌ Skip the type (if project uses Conventional Commits)
Add user profile page ❌
❌ Use abbreviations or jargon unnecessarily
fix(db): rm dup recs via opt idx ❌
❌ Combine unrelated changes in one commit
feat: add dark mode, fix typo in README, update dependencies ❌
Real-World Examples from Popular Projects
React
feat(react-dom): Add support for CSS Layers
Implements support for @layer, enabling better CSS encapsulation.
Fixes #24556
Node.js
doc: add missing types to request and response
Added TypeScript type definitions for several Request and Response
methods that were previously missing from the declarations.
Fixes: #12345
Refs: #67890
Kubernetes
fix: prevent pod creation with invalid security context
Adds validation to reject pods with both `runAsUser: 0` and
`allowPrivilegeEscalation: false`, which is an invalid combination.
Closes #12345
Commit Message Checklist
Before committing, verify:
- Type is correct (feat, fix, docs, etc.)
- Subject line under 50 characters (max 72)
- Imperative mood ("Add" not "Added")
- First word capitalized
- No period at end
- Body explains WHY (if needed)
- Body wrapped at 72 characters
- Blank line between subject and body
- Issues referenced in footer
- Breaking changes noted with BREAKING CHANGE:
- Follows project conventions
Tools & Automation
Commitizen
Interactive tool for writing commits:
npm install -g commitizen
git cz
Commitlint
Lint commit messages:
npm install --save-dev @commitlint/cli @commitlint/config-conventional
Husky
Git hooks to enforce commit message format:
npm install --save-dev husky
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit ${1}'
Resources
Specifications:
- Conventional Commits: https://www.conventionalcommits.org/
- Git Commit Guidelines: https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project
Tools:
- Commitizen: https://github.com/commitizen/cz-cli
- Commitlint: https://commitlint.js.org/
Further Reading:
- "How to Write a Git Commit Message": https://chris.beams.io/posts/git-commit/
- Angular Commit Guidelines: https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit