Git commit message conventions for this repository.
All contributors are expected to follow these directives before opening a pull request.
- Commit Format
- Commit Types
- Scopes
- Breaking Changes
- Commit Body & Footer
- Rules
- Branching Model
- Examples
type(scope)!: short description
[optional body]
[optional footer(s)]
| Part | Required | Description |
|---|---|---|
type |
✅ Yes | Category of change (see types below) |
(scope) |
No | Module or layer affected, e.g. (auth), (wallet) |
! |
No | Marks a breaking change — triggers a major version bump |
short description |
✅ Yes | Imperative, lowercase, no period, max 72 characters |
body |
No | Explains why, not what. Separated from subject by a blank line |
footer |
No | BREAKING CHANGE:, Closes #123, Co-authored-by: |
Introduces a new feature visible to the user or API consumer.
Triggers a minor version bump (1.x.0).
feat(auth): add JWT refresh token rotation
feat(products): expose bulk price update endpoint
feat: add order history pagination
Fixes a bug in the codebase through the normal development cycle.
Triggers a patch version bump (1.0.x).
fix(auth): handle expired tokens on /me endpoint
fix(wallet): prevent negative balance on concurrent transfers
fix(repo): correct N+1 query in OrderService
A fix merged directly to main outside the normal release cycle.
Reserved for production emergencies that require immediate deployment.
Must be back-merged to develop after landing on main.
hotfix(payments): correct Paystack webhook signature validation
hotfix(auth): revoke leaked service account credentials
hotfix(wallet): resolve race condition in debit endpoint
hotfix for routine bugs. If it can wait for the next release cycle, use fix.
A code change that neither fixes a bug nor adds a feature.
Restructuring, extracting, renaming — no behaviour change.
refactor(service): extract transfer logic into TransferDomainService
refactor: replace raw JDBC with Spring Data JPA
refactor!(api): rename /v1/users to /v1/accounts
Changes to documentation only — README, Swagger/OpenAPI annotations, Javadoc, wiki pages.
No production code changes.
docs(api): add OpenAPI examples for transfer endpoint
docs: update local development setup instructions
docs(arch): add system architecture diagram to wiki
Adding or updating tests. No changes to production logic.
Use even when fixing a previously broken test.
test(wallet): add Testcontainers integration test for transfer
test(auth): cover JWT expiry edge case in JwtUtilTest
test: add @WebMvcTest suite for ProductController
Routine maintenance with no production code changes.
Dependency bumps, build config, CI pipeline, tooling updates.
chore: bump spring-boot to 3.3.2
chore(ci): add Docker layer caching to GitHub Actions workflow
chore(deps): upgrade jackson-databind to 2.17.1
A code change that measurably improves performance.
Caching, query optimisation, indexing — no behaviour change.
perf(products): add index on category_id to fix full table scan
perf(cache): cache wallet balance in Redis with 60s TTL
perf(db): switch to batch inserts for transaction history writes
Formatting, whitespace, missing semicolons.
Changes that do not affect meaning or logic whatsoever.
style: apply Google Java Format across all source files
style(controller): reorder imports per project convention
style commits must contain no logic changes. Run your formatter in a dedicated commit, separate from any functional change.
Reverts a previous commit. The description must reference the commit being undone.
revert: feat(auth): add biometric unlock endpoint
revert: "chore: bump jackson to 2.17" — broke deserialization on nested types
Include the original commit hash in the body:
revert: feat(auth): add biometric unlock endpoint
Reverts commit a3f92bc.
Reason: caused 401s on all authenticated routes in staging.
Scope is optional but strongly recommended. Use the module, package, or architectural layer the change touches. Keep scope names short and consistent across the team.
| Scope | Use for |
|---|---|
auth |
Authentication, JWT, session management |
wallet |
Wallet balance, funding, debits |
payments |
Payment gateway integrations (Paystack, Flutterwave) |
products |
Product catalogue, categories, pricing |
orders |
Order creation, fulfilment, history |
users |
User profile, registration, account management |
notifications |
Email, SMS, push notifications |
api |
Global API config, versioning, routing |
db |
Database schema, migrations (Flyway/Liquibase) |
config |
Application config, environment properties |
ci |
GitHub Actions, pipelines, deployment scripts |
docker |
Dockerfile, docker-compose, container config |
Add new scopes as your codebase grows. Document them in this table.
A breaking change is any change that forces API consumers or dependent services to update their code.
Mark breaking changes by appending ! after the type (and scope), and add a BREAKING CHANGE: footer in the commit body.
refactor!(api): rename /v1/users to /v1/accounts
BREAKING CHANGE: the /v1/users/* route namespace has been removed.
All clients must update their base URL to /v1/accounts/*.
The old route returns 301 redirects until 2025-09-01.
Breaking changes trigger a major version bump (x.0.0) in semantic versioning.
Examples of breaking changes:
- Removing or renaming a public API endpoint
- Changing a response field name or type
- Removing a previously required request parameter
- Changing authentication scheme
Use the body to explain why the change was made, not what it does — the diff already shows that.
fix(wallet): prevent negative balance on concurrent transfers
Two simultaneous debit requests were both passing the balance check
before either completed the write, resulting in negative balances.
Added a pessimistic row-level lock (SELECT FOR UPDATE) on the wallet
row inside the transaction to serialize concurrent debits.
Closes #214
Supported footers:
| Footer | Purpose |
|---|---|
BREAKING CHANGE: <description> |
Signals a breaking change |
Closes #<issue> |
Links and closes a GitHub issue on merge |
Fixes #<issue> |
Links and closes a bug report |
Refs #<issue> |
References an issue without closing it |
Co-authored-by: Name <email> |
Credits a co-author (e.g. pair programming) |
-
Imperative mood. Write
add, notaddedoradds. The subject line completes the sentence: "This commit will…" -
Lowercase subject. The description starts with a lowercase letter:
fix(auth): handle expired tokens, notFix(auth): Handle expired tokens. -
No period at the end of the subject line.
-
72-character limit on the subject line. Use the body for anything longer.
-
One logical change per commit. If you find yourself writing "and" in the subject line, split it into two commits.
-
hotfixis an emergency lane. It bypasses the normal PR →developflow and lands directly onmain. Never use it for a bug that can wait for the next sprint. -
Never commit directly to
mainordevelop. All changes go through a feature branch and pull request — excepthotfix, which goes tomaindirectly and is then back-merged todevelop. -
styleand logic never mix. Run formatters in a dedicated commit before or after any functional change. -
Reference what you revert. A
revertcommit must include the original commit hash and a reason in the body. -
BREAKING CHANGEfooter is mandatory whenever!is used. The!alone is not enough for automated tooling to generate changelogs correctly.
main ←── production. tagged releases only.
│
└── develop ←── integration branch. all PRs target here.
│
├── feat/wallet-transfer
├── fix/jwt-expiry-handling
├── chore/bump-spring-boot
└── refactor/extract-transfer-domain-service
Normal flow:
-
Branch from
develop— name the branch after the commit type and a short slug:git checkout -b feat/wallet-transfer develop
-
Commit with the conventions above. Push and open a PR against
develop. -
At least one peer review before merge. Squash-merge is preferred to keep history linear.
-
Release: merge
develop→main. Tag the release commit:git tag -a v1.4.0 -m "release: v1.4.0"
git push origin v1.4.0
Hotfix flow:
main ──→ hotfix/paystack-signature ──→ main (tag v1.4.1)
└──→ develop (back-merge)
git checkout -b hotfix/paystack-signature main
# make the fix
git commit -m "hotfix(payments): correct Paystack webhook signature validation"
# PR → main, merge, tag, then:
git checkout develop && git merge main
Complete commit messages showing all conventions in practice.
feat(auth): add role-based access control to wallet endpoints
Users with ROLE_USER can only access their own wallet.
Admins with ROLE_ADMIN can read any wallet.
Closes #87
fix(db): add missing index on transactions.wallet_id
Full table scans on the transactions table were causing p99 latency
spikes of 4s+ on the /history endpoint under load.
Refs #102
hotfix(payments): correct currency conversion rounding error
Integer division was truncating sub-kobo amounts on NGN→USD
conversions. Replaced with BigDecimal arithmetic throughout.
Fixes #311
refactor!(api): consolidate user and account endpoints under /v1/accounts
BREAKING CHANGE: /v1/users/* is removed. Clients must migrate to
/v1/accounts/*. A 301 redirect shim will remain until 2025-10-01.
chore(ci): cache Maven dependencies between GitHub Actions runs
Reduces average CI build time from 4m 20s to 1m 45s by restoring
~/.m2 from cache on cache hit.
test(wallet): add Testcontainers integration test for concurrent transfers
Covers the race condition fixed in commit b2e91fa. Uses two threads
firing simultaneous debit requests to assert balance never goes negative.
Refs #214
Conventions based on the Conventional Commits 1.0.0 specification.