Git Commit Guidelines
A clean Git history is a form of documentation. When every commit tells a clear, consistent story, git log becomes a powerful audit trail — one that any engineer can use to understand what changed, when, and why. This guide defines the commit message format used across all services in this monorepo.
The Format
Section titled “The Format”All commit messages follow the Conventional Commits specification:
<type>(<scope>): <description>Every component plays a role:
| Component | Required? | Description |
|---|---|---|
type | ✅ Yes | The category of change (see table below) |
scope | Optional | The area of code affected |
description | ✅ Yes | A short, imperative-mood summary |
Commit Types
Section titled “Commit Types”| Type | When to Use |
|---|---|
feat | A new feature visible to users or other services |
fix | A bug fix |
perf | A change that improves performance without altering behavior |
refactor | Code restructuring with no functional change |
test | Adding or updating tests |
docs | Documentation-only changes |
chore | Build system, dependency updates, tooling (no prod code change) |
ci | Changes to CI/CD pipeline configuration |
The Scope
Section titled “The Scope”The scope is optional but strongly encouraged. It helps engineers filter history for a specific module or feature area without grep.
Use the name of the affected module, controller, or feature — whichever is most meaningful:
feat(auth): add refresh token rotationfix(orders): correct discount calculation for bundled itemsrefactor(users): extract permission check into separate servicedocs(readme): update local setup instructionschore(deps): bump TypeORM to 0.3.21Writing a Good Description
Section titled “Writing a Good Description”The description is the most important part. Follow these rules:
-
Use the imperative mood — write as if completing the sentence “This commit will…”
- ✅
add JWT-based authentication - ❌
added JWT-based authentication - ❌
adds JWT-based authentication
- ✅
-
Keep it under 72 characters — GitHub truncates longer subjects in the UI and
git log --oneline -
Don’t end with a period
-
Be specific — vague messages waste everyone’s time:
- ❌
fix bug - ❌
update code - ❌
changes - ✅
fix(auth): resolve token expiration not being checked on refresh
- ❌
Real-World Examples
Section titled “Real-World Examples”Feature Work
Section titled “Feature Work”feat(auth): add JWT-based stateful authenticationfeat(orders): support discount codes in checkout flowfeat(notifications): send email on appointment confirmationfeat(iam): implement role-based permission assignmentBug Fixes
Section titled “Bug Fixes”fix(auth): resolve token expiration not enforced on refreshfix(orders): correct total price when multiple discounts appliedfix(users): prevent duplicate email registrationfix(ws): reconnect WebSocket on authentication token refreshRefactoring & Housekeeping
Section titled “Refactoring & Housekeeping”refactor(users): extract permission validation into dedicated servicerefactor(appointments): simplify availability conflict detectiontest(orders): add unit tests for discount calculation edge casesdocs(architecture): add sequence diagram for auth flowchore(deps): upgrade NestJS to v11 and TypeORM to 0.3.21ci(github-actions): add lint check to PR workflowMulti-Line Commit Bodies
Section titled “Multi-Line Commit Bodies”For significant changes, add a body and optional footer after a blank line:
feat(iam): implement fine-grained resource-level permissions
Replace role-only authorization with a policy-based system that supportscontextual, resource-level permission checks. This is required forcompliance with the upcoming multi-tenant access control requirements.
Closes #142Breaking-Change: RequirePermission decorator now requires explicit contextThe body should explain why the change was made, not what it does — the diff shows the what.
Common Anti-Patterns
Section titled “Common Anti-Patterns”| ❌ Bad Commit | Why It’s Bad | ✅ Better |
|---|---|---|
fix bug | No scope, no description of what bug | fix(auth): prevent login with expired session token |
WIP | Meaningless in shared history | Squash before merging |
small changes | Tells reviewers nothing | refactor(dto): simplify CreateOrderDTO field validation |
URGENT FIX | Urgency doesn’t belong in history | fix(orders): correct null check causing checkout crash |
update | What was updated? | docs(api): update response format for paginated endpoints |
💡 Tip: If you’re struggling to write a short commit message, it’s often a sign the commit is doing too much. Consider splitting it into smaller, focused commits.
Summary Checklist
Section titled “Summary Checklist”Before committing, verify:
- Message follows
type(scope): descriptionformat - Type is one of the approved types
- Description uses imperative mood
- Description is under 72 characters
- No vague terms like “update”, “fix”, or “changes”
- If it’s a breaking change, it’s noted in the footer