Conventional Commits: A History Humans Can Read
Tired of WIP? You want to automate changelog generation with actual separation of concerns? Your OCD is triggered looking at your team's lack of homogeneity in commits messages? Conventional Commits are going to be your best friends. Made popular in the Angular project, it's a simple set of rules for your commit messages that makes your log readable, and unlocks powerful automation.
By formatting our commit messages in a standard way, we can automatically generate changelogs, calculate the next version number, and immediately understand the impact of every change.
The Structure
The structure of a conventional commit is simple but strict:
<type>(<scope>): <description>
[optional body]
[optional footer(s)]Let's break that down.
1. Type
This tells us the kind of change you made. The two most important types directly affect the project's version number:
feat: A new feature for the user. (Corresponds to aMINORversion bump in SemVer)fix: A bug fix for the user. (Corresponds to aPATCHversion bump in SemVer)
We also use other types for internal changes that don't affect the version number. Here's a list of notable ones:
| Type | Description |
|---|---|
feat | A new feature |
fix | A bug fix |
docs | Documentation only changes |
style | Code style changes (formatting, semi-colons, etc.) |
refactor | A code change that neither fixes a bug nor adds a feature |
perf | A code change that improves performance |
test | Adding missing tests or correcting existing ones |
build | Changes that affect the build system or dependencies |
ci | Changes to our CI configuration files and scripts |
chore | Other changes that don't modify source or test files |
revert | Reverts a previous commit |
What Users See
While all these types are useful for developers, feat and fix are typically the only commits appearing in a user-facing changelog. The rest are for internal tracking. BREAKING CHANGE footers, regardless of type, will always be highlighted.
2. Scope (Our Special Rule)
The scope provides context for the change. It tells us what part of the codebase is affected.
Don't skip the scope
For our workflow, the scope is not optional. At the minimum, specify the layer of your app you are working on, but if you are using a ticketing system (e.g., Jira, Azure DevOps, etc.), your could also use it for the ID of the task you are working on. This is easily parsable and links every change back to a specific requirement.
✅ Correct: feat(JIRA-123): ...
❌ Incorrect: feat: ...
3. Description
A short, imperative summary of the change in 50 characters or less. This is the first line of the commit that shows up on shortened git logs.
Ideally, it should use an "imperative style" to clearly states the change made.
- Do:
Add new login button - Don't:
Added a new login buttonorAdds new login button
4. Body (Optional)
If your change is complex, you can provide a longer description in the body. Explain the "what" and "why", not the "how."
5. Footer (Optional but Powerful)
The footer is used to reference issues or, most importantly, to signal a breaking change.
To indicate a breaking change, start a new line in the footer with BREAKING CHANGE: followed by a description of what broke. This will trigger a MAJOR version bump.
feat(GG-21): Add doors
Doors present in every room.
Add a front door to the main entrance.
BREAKING CHANGE: Added lock to the front door.You can also output some metadata for your changelog. Follow the git trailers format for maximum compatibility:
Authored-by: Maelle <alicia@example.com>Putting It All Together: Examples
Example 1: A New Feature
feat(GG-123): Add user logout button to settings page
Users can now log out from their account by clicking the new button on the settings page.Example 2: A Bug Fix
fix(GG-456): Prevent form submission with invalid email
The registration form no longer accepts email addresses without an '@' symbol, preventing bad data from being sent to the API.Example 3: A Refactor with a Breaking Change and git trailer footers
refactor(GG-789): Rework authentication middleware
The authentication logic has been moved from the controller layer to dedicated middleware. This simplifies the API controllers and centralizes the auth flow.
BREAKING CHANGE: The `/auth/token` endpoint now returns an object `{ "accessToken": "..." }` instead of a raw token string. All clients must be updated to handle the new response structure.
Authored-by: Maelle <alicia@example.com>
Signed-off-by: Sciel <sciel@example.com>
Signed-off-by: Lune <lune@example.com>The Practical Payoff
This isn't just about keeping a tidy house. This discipline is what makes automation possible, and it will save you from future headaches.
Automated Changelogs: Stop wasting time writing release notes by hand. A conventional log allows a script to generate an accurate, meaningful changelog for every release, separating new features from bug fixes automatically.
Predictable Versioning: Tools like GitVersion can parse this history and determine the next correct SemVer version. A
feat(...)would increment the minor digit, while afix(...)increments the patch digit... And anyBREAKING CHANGEwould, of course, increment the major digit.A Maintainable History: Six months from now, when you're hunting for a regression, a
git logthat you can actually read is invaluable. You'll know what changed, why it changed, and how big the blast radius was, without having to decipher a dozen "WIP" messages.
For the complete specification, see the official Conventional Commits website.