Conventional Commits is a standardized approach to version control that enhances clarity, consistency, and collaboration among developers. We’ll understand what Conventional Commits are, explore how they work, and explain the three main benefits you gain by using them.
You develop an application, a library, a microservice, or a monorepo full of services and libraries or (god forbid) a huge monolith. No matter the type of application you develop, if you “do it right” you are using some version control (90% it is git).
The question is – how do you manage your version history? How do you report bug fixing, feature addition, a chore done on the workspace, or even a breaking change? More importantly, how easy it is to see that?
Table of Contents
What do Git Messages Looks Like Without Standards?
The picture above illustrates git commit sequence. It has only one clear line which is good but… what can we tell from the commit messages? What does fix path
mean? What path? In what component?
One cannot answer that just by looking at the commit history.
Nor can a machine do that. Remember that for later.
What Does it Look Like with Conventional Commit?
Here’s how a sequence of commit messages written according to Conventional Commits standard look like:
Going bottom up we can see we have added a feature to the fab
component, fixed something in typography, added a feature to the date-picker, did some chore in the docs
, another feature related to accordion item
and tree item
(this is actually a mistake in writing conventional commit).
All in all, it is clear just by looking at the commits what was done in every push, don’t you agree?
But we don’t usually go around looking at commit messages, right? Let’s see what the real benefits are.
The Benefits of Using Conventional Commits
There are three main benefits to conventional commits.
Auto-generated changelog
There are many tools around that can generate a change log according to conventional commit. A changelog looks like this:
See how our conventional commits turned into an easy-to-read change log? It even splits it into versions with dates to track down any new feature and bug fixes.
How can it know the version? This is the next benefit.
Auto Versioning According to SemVer
The Conventional Commit standard has the following structure:
{type}({scope}): {description}
The type is what kind of change was done. Was it a feature? A fix? A chore?
The scope is what component/library/app/service is impacted by this change.
The description is… well… the description.
That’s the gist of it. I’m not going to bore you with the full specifications, but I suggest reading it. It’s short and makes sense.
So, how does this help us with versioning?
SemVer is a way of versioning our software. The version is comprised of 3 digits: X.Y.Z
.
X is called Major
, Y is called Minor
and Z is called Patch
.
When you do a breaking change, you raise a major version. That means the interface of this scope changed.
When you add a new feature (without breaking existing ones), you raise a minor version.
When you fix some bug without a feature and without breaking anything, you raise a patch version.
See where we are getting at?
Now a machine can read our commits and decide what version our scope should receive.
And you don’t have to do it yourself. Because Conventional Commits is a standard, there are many tools that give you auto changelog, auto versioning, and usually both.
One such tool is release-please
, which also has a handy github action
.
Encouraging Better Communication in the Team
Besides commit messages being more readable, defining the structure gives us something else. It gives the commit message context. Even if the description is meh
, the fact the type of the commit and its scope are well known – the reader can at least anticipate what’s coming.
Take a look at this example:
There is an attempt at standardisation here. Still, a human looking at it cannot understand what type of change was done (is this a fix or a feature?) nor to what component. A change log cannot be inferred from this too.
If we change this a bit, it will look like this:
fix(button): verified 48 done and fixed 3 (#172/vivid-103)
feat(button): verified 2 and added demo example (#172/vivid-103)
chore(dialog): fixed 1/5 (font) (#172/vivid-103)
This isn’t much better, because the description non-descriptive.
Then again, if one has to write the commit with fix(button): {description}
it is less likely this person would write such description. It is more likely a person would describe the fix
or feat
done on the button
.
As usual, having some standards helps people do things better. It’s somewhat like the broken window theory.
Try it. See what happens.
Example from Production: VIvid’s Commit Message Standard
We are using Conventional Commits in Vivid.
It helps us generate our changelog and release log and auto-determine the release version.
In addition, our commit messages have been much better ever since.
But our conventional commit enforcement is not on each and every commit. During development, a developer can add as many commits as one likes with any message.
When someone creates a pull request (PR), though, we enforce conventional commit on the pull request’s title:
We’ve added another standard that helps us connect the commit to a JIRA ticket. Every PR title ends with (VIV-XXX) where XXX is the issue number. For example:
fix(disabled): adds a consistent cursor to disabled elements (VIV-999)
Eventually, in our release log, the VIV-XXX
turns into a link to JIRA:
Eventually, when a PR is merged, the PR title is set as the commit. We use squash + merge
so all commit history is removed from the main
branch and we are left with the one Conventional Commit. It looks like this:
If a pull request has more than one change (more than one fix or feature), one can always add them in the merge commit’s comments. We do it like this:
The Conventional Commit tool (in our case, release-please) will consider these comments as if they were part of the message.
This way, we take a relatively lax
approach in regard to commit messages, making sure that the last step would be to actually describe the change.
Summary
Conventional commits give us two straightforward benefits: Auto changelog and auto versioning.
There are many ways to achieve these goals.
One such tool is Beachball. This tool uses a CLI that generates a JSON file instead of reading git history. The big advantage here is that it is much easier to change the history if one made a mistake (changing git history can be quite messy…).
Aside from these two clear and immediate benefits, I claim it also helps encourage people to write better commit messages.
I might be wrong 🙂