Shortly after I joined the team at Aloompa, one of our contractors pushed some code with the commit message:
"Changes". Full stop. Great David Bowie song. Terrible commit message.
It’s easy to look at that example and objectively call it out for its lack of detail and context. However, without a framework for what a good commit looks like, there is nothing to point to as a better alternative.
The philosopher Plato wrote of a distinction between physical “Forms” and their non-physical essence. A physical “Form” can only be judged as inferior or superior in relation to the ideal essence of that form. In the same way, in order for there to be a good commit message, we must be able to imagine the ideal commit message.
But before we look at the commit messages themselves, let’s talk about how we name branches.
When it comes to finding context for a commit, branch names are the first line of defense. Here are some things a branch name should do in order for it to build the context we need.
Everything you do as you are building and maintaining a product can fundamentally be broken down into two separate classes. You are either fixing something that’s broken or you are building something new. This should be pretty easy to distinguish. Are you creating a new repo? That’s a feature. Are you adding a login button? That’s another feature. Are you fixing the login button because you hard-coded your password in the UI? That is a hotfix.
It’s important to distinguish features and fixes from each other because they often need to follow different paths to get merged into production. An urgent bug fix should get merged straight into staging so that it can be deployed more quickly. A feature should go through a release branch on its eventual path to staging and production. The urgency implied in a
hotfix branch should be a tipoff that it needs to be reviewed and merged more quickly.
At Aloompa, we prefix all of our feature branches with
feature/* and all of our bug fixes with
hotfix/*. The names aren’t important, but having a convention around it is.
In as few words as possible, your branch name should say what it is doing. For a new feature, I would typically just go with the name of the feature I am creating. If I am adding a login button to our CMS, I would go with something like
feature/cms-login/*. Just by looking at the branch name, a developer should be able to form an idea of what it does.
Bug fixes should describe what is being fixed. Their names tend to get a little longer than the feature names, but should still aim for brevity and clarity. If your login form always throws an error when someone has a Gmail address, your branch name might be
hotfix/cms-login-for-gmail-users/*. You don’t need to include any verbiage about “fixing” anything because you already have a
hotfix in the branch name prefix.
For consistency, the description in the branch name should always be lowercase.
We use Jira to manage our tasks. Something that is helpful for us is to literally include the Jira card ID inside the branch name. Jira has a pretty nice Github integration that can do some card automation if it sees a branch with the card ID. It also makes it easier to reference the requirements to see if the feature or bug fix are being addressed correctly by the branch implementation.
If there isn’t a card for the branch, step back and ask why you are doing features or bug fixes that aren’t being tracked in your system.
The task management identifier comes at the end of the branch name, so for a feature branch, it would look something like:
Per the Linux philosophy, software should do one thing and do it well. This applies not only to code, but to our commits as well. If you are using words like “and” to describe the full context of a change, that should give you pause.
In truth, this is an area that I struggle with. I have a tendency to see problems and want to fix them right away. Please join me in resisting the urge. Our teammates will be grateful when they review our code and it is clear what is being reviewed.
Git has built-in functionality to include commit messages inline with your commit. This can be achieved by running:
git commit -m <Message>
The problem is that this forces you into the mindset of needing to fit your commit message into a short one-liner. A commit message isn’t a tweet. It is the epic story of your code.
If you don’t include the
-m <Message> in the command, you will be forced to write a commit message with a full screen prompt. That prompt should be the canvas for your commit message masterpiece.
In Github, and I assume most other repository platforms, the first line of a commit message is displayed large at the top of the screen. If it is more than 50 characters, it includes an ellipsis and continues beneath. To make it more legible, just break the first line before the first 50 characters.
This may seem strange at first, but it makes reading through Git history much easier. Consider the following example of an incorrect commit:
"The login form was throwing errors when an email address was from Gmail. This removes the hard-coded error."
When you are reading through your Git history, the above message falls flat. It tells the story, but the voice it is written in is hard to read in a commit history.
Write your commits like you are telling somebody what to do in a blunt, almost rude way. For example:
"Fix error that is thrown for Gmail addresses at login"
Yeah, it sounds a little cold at first, but don’t worry. Git can take it.
A commit message should always provide clarity on the following:
The side effects should be a bullet list that can be easily scanned.
Add a full link to the Jira card. This makes it much easier to jump over and review requirements.
feature/cms-login/DEV-102 branch example, we might write a message like this:
"Add button to login to the CMS https://task-manager.com/path-to-card Users should be able to click a button to login to the CMS with their username and password. When they click the login button, we open a form using the AWS Cognito userpool to allow them to log in or create a new account. * Login with Cognito and Amplify * Store auth token in local storage"
Please note that if you end up having too many bulletpoints about potential side effects, it is a red flag that you may be doing too much in a single commit.
When I write about improving commit messages, I am writing to myself as much as anyone else. This is an area in which I want to improve. These guidelines function as a reference for myself and for my team.
This is an iterative process and while I don’t think this outline is exactly the non-physical essence of Plato’s ideal commit message, it is a definite place to start. Surely even Plato would be okay with that.
Tyson is the Chief Technology Officer at Aloompa.
He has a passion for Functional Programming, GraphQL, the Serverless architecture and React.
When he's not writing code or working with his team, Tyson enjoys playing guitar, growing vegetables and spending time with his family.
Tyson primarily works remotely to help support the needs of his oldest son who has level 3 autism.