/ git

How I Write a Commit Message

Introduction

This is a topic that has been covered at length in other blog posts (most notably Chris Beams' blog post), but it's important, so it bears repeating. When you're working on a large software project, it makes everyone's life (including yours) much easier if you write consistent, descriptive, and easily readable commit messages.

Software engineering is tough. You spend all day (or multiple days) getting your feature/bug fix mapped out, writing the code, debugging said code, writing tests to verify that nothing is broken in the future, and then, after all of that, you have to craft your commit(s). It's very tempting to let your discipline lapse and just write something half-baked without thinking too much about it, just so you can get that task into the "Done" state.

Here's the kicker, though: someday, you're likely going to have to go back and review that commit message. If, for no other reason, you may want to go through your git repository to see what you've accomplished since your last performance review. Going through a git commit log like this (Yeah, this is a real snippet from a commit log that I wrote. The worst part is that those commit messages are all there is. There's no additional description in any of them):

85dd75c refactor interfaces so they are all in the same format
2af1c6d add code for branching model
d3b2606 Merge branch 'glsl'
3a6bed2 add necessary graphics libraries
d028509 fix issue #17261
2367d80 more work toward full implementation
2067b23 add cpp files
a111bff makefile generation
14a715e create repo

Is much more work than going through a commit log like this:

61c70bc Add a configuration to ripsaw.
8af4151 Merge pull request #7 from jwir3/jwir3/#3-cut-list
9d05854 Add a CutList data structure.
74a21ae Merge pull request #5 from jwir3/main-ui
4be2728 Add a basic user interface for binary program.
de4061d Refactor measurements functionality into its own rust file.
15cc8e1 Merge pull request #1 from jwir3/basics
a6c6010 Add the ability to create nominal and actual lumber sizes.
6a1e1fd Initial commit

I can tell exactly what was changed in each of the commit messages from the latter example.

Moreover, this is your work. You've put in the hours necessary to make this code work. The git commit message is your advertisement of the work you've accomplished. It's the thing most of your colleagues are going to see that has your name attached, since, as we're working in code from day-to-day, we typically see the code, not the person who wrote it. Take pride in your work - this is your opportunity to "sell" it to your colleagues!

The Rules

There are three basic rules I follow (with an additional, optional one at the very beginning):

  1. (Optional) Choose a gitmoji to represent your commit
  2. Add a simple subject in the imperative mood, starting with a capital letter, no more than 80 characters in length, and separated from the body by a blank line.
  3. Write a body message describing what was accomplished in the commit, as well as why it was necessary, wrapped at 80 characters in length, separated from the metadata with a blank line.
  4. Add all metadata (e.g. ticket numbers, CI commands, etc...) at the end of the commit message.

Choose a gitmoji to represent your commit

I've found that reading commit logs (especially one-line logs) can be made much simpler with an emoji that describes the category of change that the commit falls under. I personally use gitmoji, an informal standard for which types of commits use which emoji, but you can really use anything you want, as long as you're consistent.

By adding something like :bug: before your commit, it's pretty clear every time you read it that this commit fixed a bug. I find it helps with parsing, but you have to be willing to add some helper packages so that git log works on the command line.

Add a simple subject

In the manpage for git-commit, the following argument is made:

DISCUSSIONThough not required, it's a good idea to begin the commit message with a single short (less than 50 character) line summarizing the change, followed by a blank line and then a more thorough description. The text up to the first blank line in a commit message is treated as the commit title, and that title is used throughout Git. For example, git-format-patch(1) turns a commit into email, and it uses the title on the Subject line and the rest of the commit in the body.

I've personally found that 50 characters is a little short, although, I can see the point if you're reviewing patches via email. Github seems to truncate the subject line of a commit at 72 characters, so that's worth keeping in mind, too. I set my vim editor (which is what I use to write commit messages) to show a vertical line and automatically add a newline at 80 characters. It's somewhat of an arbitrary limit, but I don't think that 50 or 72 characters is quite enough, personally, and 80 character line limits seem to be a standard.

I think the idea behind the 50 character limit is that when in a terminal with an 80 character line length limit, the sha of the commit and it's trailing space require 10 characters on average, but they can require up to 41 characters (40 for the sha itself, plus 1 for the space), thus you really only have 80 - 41 = 39 characters of actual message length. I think, though, that most terminals aren't limited to 80 characters anymore, so this is somewhat moot. Instead, it's a discipline thing: you should be disciplined enough as an engineer to be able to summarize your changes in 50 characters or less.

That said, here's my argument against limiting yourself to 50 characters: first off, we live in a society that is becoming more and more conditioned to brief communications (think news headlines and tweets). In most cases, the ideal spot for a news headline is 60-100 characters [^2]. 80 characters is a good middle ground in that range for people to be able to digest.

A commit subject should be in the imperative mood. What this essentially means is that it should complete the sentence "When this commit is applied, it will ___________________. Think about how easy to read this is when it's properly written in this manner. It also helps you, as an engineer crafting the commit, to know if you've overstepped the bounds of a commit changing a single related and cohesive thing, rather than changing a bunch of things that are unrelated.

One other detail about the subject that might be controversial: I actually add a period to the end of my commit messages. The reason I do this is because, in my opinion, the subject line of the commit should be a complete sentence. I find messages easier to read and parse when proper punctuation is used, but this is just me.

Write a body message

It's important to add descriptive data as to what, specifically, was changed in your commit (beyond the limit of your subject line). I've found, though, that more important is the justification for this change. In six months, you might come back to this commit, realize that you changed a file or a class, but you might not remember why that change was necessary. The body also gives you room to express yourself using lists, bullets, links, or, in some cases (depending on your organization's policies) full markdown.

It is worth noting that not every commit requires a subject line and a body. Sometimes, if the subject line is absolutely clear, it's worth just leaving the body blank. I also do this if it's a merge commit, but I will sometimes add the metadata section to indicate who reviewed my merge:

commit a2f525f01bf657b706fd2f39bbf704aa7b9c4a69
Merge: d9f74c1fd 4530a7243
Author: Scott Johnson <jaywir3@gmail.com>
Date: Mon May 13 16:22:45 2019 -0500

    Merge pull request #5616 from jwir3/bump-versions-package-may19

    [r=VerteDinde, coreh]

Add all metadata at the end of the commit message

We all have things that we need to add to a commit - ticket numbers, references to other commits, who reviewed a given commit, etc... This metadata should be placed at the end of the commit message. The rationale for this is that this tends to be things that are either required by a continuous integration system (e.g. it should be machine-readable, rather than human-readable), or is ancillary information not directly related to the commit in question (i.e. if your commit is readable enough, why bother going to the original ticket for more information? You should be able to find the answer to your question within the git log itself).

Thus, I tend to organize my data in a commit in a linear fashion, with the information I'm most likely to want coming first, in this order:

  1. What category this commit fits into
  2. What this commit changes
  3. A thorough explanation of what was changed and why it was necessary
  4. Any ancillary metadata that helps me locate more information if this wasn't enough

A Config to Help You

I find the following commit template configuration helpful as a reminder of this. Just put the file into ~/.gitmessage and it will show up for you every time you enter the editor after running git commit:

# When applied, this commit will:

# This change is necessary because:

# The following is metadata for this commit:

How can I get my organization to do this?

There's a small part of this that I've glossed over that makes this all worthwhile. It's only somewhat beneficial if you're the only one in your organization doing this. How can you get engineers from across the organization writing commits like this?

Part of the answer to this questions is: You can't. It's not possible to control other people's behavior completely, so you should temper your expectations. People will do things that they believe have value, so if they don't think that writing commit messages in this way has value, they won't do it. There are ways to encourage them to see value in it, though.

I've found that around 85% of organizations I've been a part of don't have any documentation on how to write commit messages. If, however, your organization is part of the 15% that does have a standardized way of writing commit messages, by all means, follow those rules. Otherwise, what I do is to keep in mind a couple of things:

1. The other person isn't you. They have different expectations from their higher-ups (and themselves), so it's not a great idea to try to force upon them a standard that you simply create out of thin air.

2. Some small things (like the difference between an 80-character subject line and a 50-character subject line) are ideological battles that aren't worth fighting over. If that's the sticking point between you and a colleague, just let them do it however they want, as long as the benefit is still present.

Now, on to the techniques for encouraging this behavior. I find that core review and micro-rewards are a great way to encourage the writing of great commit messages. Most organizations have some form of code review process. This is a great place to enforce commit message practices. One thing to be careful about, though, is to not become too dictatorial. If I see a commit message that could be improved during a code review, I will often consider re-writing the commit message for the person in question, and posting that as a suggested change in the code review. Additionally, not everyone knows how to use git rebase (see also Rebasing Toward Independence), so it's worth it to add a comment detailing how they change their commit message in the code review, as well.

Keep in mind that you can always suggest the change be made, but unless the commit message is really nondescriptive, I typically wouldn't hold up a code review solely on that front. Over time, it will become clear in the log which engineers take the time to write a good commit message and which ones don't. Once other engineers are onboard and have to work with the commit messages another person is creating, peer pressure will eventually win out.

The other option that I use is micro-rewards. This is specific to an individual company, but some companies utilize a platform like bonus.ly to enable employees to reward each other over the course of a month. If I see a commit message that really stands out in a good way, I'll go out of my way to reward that individual publicly so that everyone knows I appreciate that.

The downside to this, of course, is that your company needs to subscribe to the service in order for you to do this. You can, however, bring good commits up during retrospective meetings, during stand-ups, or even just in Slack on a good day, complimenting someone publicly about their commit messages. Stay away from chastising people who aren't doing what you want publicly, as that doesn't tend to work well in getting people to change their behavior, and usually makes you look bad.

In all, you can't force someone to do what you want them to do, but you can encourage the path you think is the correct one.