On the Importance of Commit Messages
$ git log
commit f9d601108fd2a19ff7b5dfc62a790083b9af500c
Author: CF Frost <cf.frost@example.com>
Date: Wed Jan 17 14:08:36 2018 -0500
fixed that bug
commit 253b7e2f445e65222ef81f33cf4070660084a3dc
Author: CF Frost <cf.frost@example.com>
Date: Wed Jan 17 14:07:05 2018 -0500
tweaks to button styles
commit 7e12e77eae5e4ca80e7b3fb55db5c6d954d2cbe4
Author: CF Frost <cf.frost@example.com>
Date: Wed Jan 17 14:06:16 2018 -0500
added a new action
We’ve all seen Git histories like this. To some it’s no big deal, but on my team this will get your pull request (PR) declined regardless of how your code looks. In fact we won’t merge your PR unless your commit messages conform to a very specific standard: Conventional Commits. We chose this standard for its readability and its ability to automate our semantic versioning & changelog.
Conventional commits are structured as <type>(<scope>):<subject>
. (I’ll let
you read the above document for further details on the convention.) Adhering to
this structure with detailed subjects and message bodies when appropriate allows
us to create a truly meaningful history. When browsing this history we can
really see the progression of development, and when reviewing PRs we have a
brief summary of changes right in front of us. However, the real value of a
meaningful commit message may not come for months down the road.
A Case Study
The following is based on actual events.
Let’s say we have a software engineer named Dorcas. Dorcas’s product owner, Throckmorton, came to her to ask, “Why doesn’t this user see the same links that I do on the homepage?”. Dorcas didn’t know, so she told him that she would find out and get back to him. Dorcas only took a few minutes to find the code that was causing the issue Throckmorton had called out. She looked through the commit history on BitBucket and saw this commit:
commit 3b85fbfebac285745e15da57e89d591dac21a3fe
Author: Myrtle Carter <myrtle.carter@example.com>
Date: Fri Aug 4 15:58:12 2017 -0400
feat(links): filtered out links for non-premium users
Only premium users should see links to premium features.
JIRA-1337
Myrtle left Dorcas a gold mine of information in this commit message. Now she can see that the reason for the filtering has to do with premium features. She even included a message body with a bit of reasoning behind the change. There is something else really important here that we haven’t discussed yet: a JIRA task ID. With this, Dorcas was able to look up the JIRA task and learn even more details about the change and why it was done. As it turns out, the given user was not a premium user. She is able to write up a quick instant message with an explanation and a link to the JIRA task and send it off to Throckmorton.
Thanks to Myrtle’s diligence in her commit messages, a great deal of time searching is saved for Dorcas and anyone she would have had to otherwise engage to find the answer Throckmorton needed.
Squashing commits
Another key that helped Dorcas find the answer so quickly, was her team’s
practice of squashing commits before merging PRs and keeping PRs small (limited
to a single feature). This keeps “work in progress” commits out of the history,
so that each commit is more meaningful and the history as a whole is easier to
read and understand. Most hosted git solutions have a built-in option for
squashing commits when merging PRs, but it can also be done manually through a
soft reset and commit or an interactive
rebase. For the following example, we’ll
walk through a soft reset and commit assuming that the user is making a pull
request from feature/user-alert
to main
.
- First, make sure we are up-to-date by fetch the latest from our origin repo.
$ git fetch origin
- Then we will do a soft reset to
main
which will make our git history identical to that ofmain
, and stage our changes to be committed.$ git reset --soft origin/main
- Now we are ready to commit.
$ git commit
- After committing we’ll need to force push since we are rewriting history.
$ git push origin feature/user-alert -f
It is worth noting that there are rare occasions when it makes sense to have multiple commits in a single PR, as long as each of those commits represents an individual unit of work. Like the pirate code, this is more of a guideline than an actual rule.
Tooling
There are some great open source couple tools for JavaScript projects that can
set us up for success with our commit messages.
commitlint
provides a CLI for
validating commit messages. We’ll need to install this CLI and our config before
continuing.
$ npm install --save-dev @commitlint/cli @commitlint/config-conventional
To add our config we’ll create a commitlint.config.js
in the root of our
project:
module.exports = {
extends: ['@commitlint/config-conventional']
};
Using githook-scripts
we can
easily add this into our workflow with the following package.json
configuration:
{
...
"scripts": {
"githook:commit-msg": "commitlint --edit $GIT_PARAMS"
}
...
}
A contributor could try to get past that without a valid commit message by using
the --no-verify
flag when they commit, so we’ll add a test for the git
history, which we can use in our PR build process to validate all commits in the
branch that aren’t in main yet. This is recommended to be run in your test
script like so:
{
...
"scripts": {
"test:spec": "jest",
"test:git-history": "commitlint --from main --to HEAD",
"test": "npm run test:spec && npm run test:git-history",
"githook:pre-commit": "npm test"
}
...
}
Now you are all set and ready to rock with your detailed & meaningful commit messages. Though it may take some time to get used to for a team that hasn’t enforced commit message standards before, it’s sure to save you some headaches down the line. While this may not be for every team, it certainly has been beneficial for mine. Many other teams within American Express have differing approaches with their histories and still deliver quality software to production.
Important Notice: Opinions expressed here are the author’s alone. While we're proud of our engineers and employee bloggers, they are not your engineers, and you should independently verify and rely on your own judgment, not ours. All article content is made available AS IS without any warranties. Third parties and any of their content linked or mentioned in this article are not affiliated with, sponsored by or endorsed by American Express, unless otherwise explicitly noted. All trademarks and other intellectual property used or displayed remain their respective owners'. This article is © 2018 American Express Company. All Rights Reserved.