Writing better commits

I think we can all agree to have made commits where we just want to push the feature out without writing a detailed commit message

relevant xkcd: as project drags on, my git commit messages get less and less informative
relevant xkcd: source

While we can fix this by doing an interactive rebase, it's not what we will be focusing on. Instead I want to talk about why and how we can make our commits more useful in general.

But first, let's discuss a little background

Communicating Intent

The primary goal of a software developer should be to communicate their intent to future developers - Louise Crow

And as such there are various ways of achieving that

  • readable and maintainable code1

  • code comments

  • Technical / Design documentations

  • Tests

    Yes, tests can be considered documentation too. They are some of the best ways to document edge cases and to explain behaviours for how any user of an application/library would go about using it.

All of these things are about communicating intent. To explain to any future developers (that may also be you) how and why you wrote something the way it is.

Where does Git fit in?

Developing features is hard, and that is when you know what you are working with. More often than not this will not be the case.

Say, none of the above listed things helped you, maybe because of a lack of proper documentation or because they are simply outdated. So, when you find yourself in times of trouble, Mother Mary may not come to you2. Instead, you will have to go and ask someone else on the team about it. Now it may be the case that,

  • The person who worked on the feature left the team.
  • They are still with the team but can no longer remember why they wrote it that way.

Well, that's just bad luck, or is it?

That's where having a good reference history helps us. Git is a living, ever-changing, searchable record that tells the story of how and why the project came to be what it is today.

Your commits, if you do them well, offer unique benefits over other ways of explaining your code:

  • They never change
  • They live alongside your code
  • They're searchable with commands like git grep or git log
  • They allow you to document your changes at the time that you made them

Every line of code is always documented - Mislav Marohniฤ‡

This allows you to debug things quicker because your project will have a searchable history. You'll have access to why this particular code change was added which will prevent you from re-introducing bugs.

Git on the train

Here are some principles to guide you on your journey abord the Git train ๐Ÿš‚

1. Make atomic commits

Atomic messages are all about making your commits about a single isolated change.

Large commits are difficult to read, especially when they contain changes that are unrelated. It might be difficult to reason about what constitutes as unrelated when you are making the commit, so there's a simple rule of thumb. Avoid using and in your commit messages.

Similar to how you split a feature into multiple smaller tasks, split your big commit into small atomic ones that achieve one task, make sense in isolation, and when grouped together tell a larger story.

Sometimes it's not possible to make commits along the way, especially when you are still figuring out how to implement the feature. And that's all right, you do can do all the changes in one go and still make atomic commits. We know that we can stage some files but did you know that you can also stage only portions of a file?

When you have some lines in your file that you want to commit but some that you don't - use git add --patch. It will ask you which bits of a file you want to add. It calls these bits hunks. If that's too much for you (like it's for me), a lot of modern code editors like VS code make this process a lot easier and that too out of the box without the need of any extensions.

stage selected ranges in vs code

So you will just be making a lot of atomic commits after you are done ๐Ÿ˜‹

2. Write good commit messages

That's a pretty vague statement, so here's a template that might be a good place to start. However, keep in mind that each commit is different.

commit.sample
short one line title

longer description of *what* the change does (if the title isn't enough)

An explanation of *why* the change is needed

Perhaps a discussion of context and alternatives that were considered.

You at the time of writing this commit have the most knowledge about this piece of code. Only you know the alternative approaches that you have may thought of but ultimately ruled out. Keeping them in provides much needed context to future developers in understanding why the code is the way it is.

Along that line, it helps to look at the commit from the perspective of another developer. What questions might they ask looking at your code? What might not be immediately obvious to them?

PSA: If you are linking to tickets that's great, but don't rely on it for context. You never know when your ticketing hosting might change. So always put all of the details in the commit message.

Further Reading


  1. Static types are also a great way to communicate intent that makes it clear and unambiguous what the code does.โ†ฉ
  2. sorry about that ๐Ÿ˜…, see Let it Be by The Beatlesโ†ฉ