Andrew Feeney

Software developer in Sydney, Australia.

Commit Message Driven Development

The problem

These days, Git is a ubiquitous tool for the software development workflow. But if you're like me, remembering to commit after each logical change is a continual struggle. There you are in a state of "flow" for a couple of hours when suddenly it's time to finish up and commit the changes so you can ship the day's work. You look with horror at your list of un-staged changes. There are two dozen files there. Various major logic changes, all intermingled. Do you go through and try to remember each logical stage of the code's evolution and cherry pick the various hunks that should be in each commit? I have certainly tried to do this. It's the main reason I end up using a GUI based git client like Fork or SourceTree. Sometimes, this can work okay. The changes were discrete enough that they can still be nicely grouped after the fact. Other times though, and increasingly so the longer you've worked without a commit, this simply can't be done. Information is lost when files are overwritten before their initial changes were committed. When adding a new file, you can't cherry pick hunks to commit. You have to just add the whole file. Or often you'll get to the end and find a change in a file that should have been grouped with the changes a few commits back in your end of day commit frenzy. This means the changes aren't really grouped into logically separate commits and everything is just a bit messy.

Most commonly you'll probably just end up resorting to something like this: 

git commit -am "Bugfixes and other improvements"

Or perhaps it's just:

git commit -am "WIP"

I must give a shoutout to David Hemphill here who has actually turned this into a disestablishmentarian philosophy. I can understand where David is coming from. His (albeit tongue-in-cheek) approach does force us to ask ourselves the questions "What are commit messages even for?" and "Do we need them?".

Those questions present a good conversation for another article, but suffice it to say that at this point I still believe that the discipline of writing helpful commit messages which provide high level summaries of the changes made, and/or why those changes were made offers value, provided it can be done with minimal added cognitive load.

My Suggested Solution

After listening to Daniel Colbourne and Caleb Porzio's recent episode of the No Plans To Merge podcast where they discuss this with David, I was reminded to finish this blog post that I've had rattling around in my head for a couple of years now about a technique I've been using for a while that I think provides a different approach to the same problem. For want of a better name I call it "Commit Message Driven Development", because you know, we need more DDs.

Basically the idea can be summed up in these three steps:

  1. Write the commit message
  2. Write the code
  3. Commit the code with the pre-written commit message (edit if necessary)

This idea occurred me to a couple of years ago, and it really seemed like someone must have already thought of this. I googled around to see if I could find anyone else who was doing it. Sure enough, I am not the first to think of this, which is hardly surprising since it ain't exactly a complex idea! There are probably other good articles, but I will shout out to Arialdo Martini who wrote one article which I found at the time.


But Why?

Fair question! Here are my reasons:

1) Decide what you want to do before you start doing it

There's a lot of value in deciding upfront what 'done' looks like in the chunk of work you're about to do. It helps you get things shipped because you know what things will look like when the job is done, therefore you know the point at which you can give yourself permission to stop work.

2) Articulate your intentions

Expressing what you plan to do your own words for a hypothetical commit message is a great way to have to think through the planned change conceptually before you waste time going down a road that doesn't make sense.

3) Focus your mind

The process of declaring your intention is an excellent way to hold yourself accountable to focus on the task at hand.

4) Remember what you are trying to do now

If you're like me you run the constant risk of going down a tangent when working on code. When you want to refocus and remember what you are trying to achieve you can just type 'aim' and check your upfront commit message and away you go.

5) Remember what you were doing in a past session

If you get interrupted at any point you know you can jump back in to your codebase at any time, hours, days or years from now and quickly know what you were in the middle of doing by looking at your commit message.

6) Write the message when you're at your best

At the start of the coding session your mind is more clear. You have more energy, and are more motivated to do things to a higher standard. At the end of the session you are more likely to be tired, de-motivated, bored, and just generally brain fried. It's unfair to expect this version of yourself to have to carry all the burden of writing useful and correct commit messages.

7) Ties in with TDD

Personally I find TDD, or Test Driven Development to be a philosophy which improves my development process a great deal. Philosophically I think this approach is has a lot in common with TDD, and works in tandem with TDD nicely.

How?

There are lots of ways you could do this. For my purposes I use two simple bash scripts I've written called 'aim' and 'fire'. I've included them below for you to use and modify as you wish. If you have some suggestions for improvements I'd love to hear about it.

aim

#!/usr/bin/env bash

vim ./COMMIT_MSG.aim.txt

fire

#!/usr/bin/env bash

vim ./COMMIT_MSG.aim.txt
git commit -F ./COMMIT_MSG.aim.txt
rm ./COMMIT_MSG.aim.txt

'aim' and 'fire' were the best I could come up with. If anyone can think of similarly catchy terms without the gun reference I'd be stoked.

Here's a link to the gist.

A few things to remember:

1) See your pre-written commit message or edit it at any time by typing 'aim' which opens it in vim.

2) Stage and commit files normally at any time if you find things that need to be committed mid-process

3) When you finally stage and commit your changes the pre-written commit message will be opened in vim before you commit in case you need to tweak it or add more info.