I’ve seen lots of advice about git commits and messages over the years—some good, some not so good. This post will go through what works for me.
Note: I’m no git workflow expert, these are just my personal opinions developed over ~10 years working at startups.
Commit Best Practices:
- Commit often
- Try and make your commits self-contained pieces of work
- Try and keep your commits as small as reasonably possible
Commit Message Best Practices:
- Keep your messages short
- Make your messages as descriptive as possible while still being short
- Be specific
Let’s go through each point.
Git Commit Best Practices
This section is for the contents of the commits themselves—the actual code changes.
Commit Often
If you only remember one thing from this post, make it this: commit often. This has saved me countless times. I frequently commit very small changes while working—at every incremental step. I may not push the commit immediately, in fact I frequently squash commits when opening code for review, but I commit all the time. This type of history is typical:
git log
# git log doesn't normally look exactly like this, I have a custom log
6904a37 fix nil bug in summary return payload (4 hours ago) <justin>
4e47ef2 adds mapSummaryToSector method (4 hours ago) <justin>
b972350 cleanup and lint /summaries (4 hours ago) <justin>
76a4f00 summaries endpoint functional, needs cleanup (4 hours ago) <justin>
By committing often, I always have a known working state to compare with, or to roll back to if something breaks (which it will). This also allows me to rapidly try things that might not work out; I can always just roll back to the version from 20 minutes earlier.
Keep your commits as small and self-contained as possible
This is a practice I’ve developed after many, many rebases. The smaller and more isolated each individual commit is, the easier it is to reason about the history of a branch and determine exactly what should be happening. This tip is very tightly-related to the point above about committing often; the more often you commit the smaller each commit will be.
You might see something unrelated in a file you’re working on and think “oh I should really fix that while I’m in here”, which is a great instinct, but I’d strongly recommend putting it in its own, separate commit with an associated message. You don’t want to revert a commit for adjust sidebar logo sizing
and find out that it also changed behavior in your navbar.
Commit Message Best Practices
Now that we’ve gone through what goes into commits, let’s talk about the messages.
Tldr: be succinct.
Keep your messages short
If your commit message is too long to fit on one line, you should shorten it1. Chances are your messages will be viewed in someone’s terminal, or in the history on a PR in github, and no one is going to read more than the first line in either of those places2. In fact, github’s UI hides longer messages behind a button click—so you know no one is going to read them:
There’s a lot of advice out there about messages having a short header line, and then an optional body with more detailed information, and maybe even a bulleted list of notes. I’m here to tell you no one will ever read all that. If you need that much detail in a single commit, you should either:
- Break that commit into multiple smaller commits
- Open a PR and write a detailed description
Make your messages as descriptive as possible
This might seem at odds with keeping your messages short. How can you be descriptive while also keeping it short? What if your description just has to be longer? This is why it’s important to keep your commits as small as reasonably possible, so they can be summarized succinctly. Now, “be descriptive” is a bit vague, what does “descriptive” even mean? In the context of commit messages, what I really mean is: be specific.
Be Specific
Consider the following commit message:
6904a37 adds test (4 hours ago) <justin>
Is it short? Yes. Is it descriptive? In a way, yes. Is it helpful? Not at all. The above message is useless. What test? Where? It needs to be more specific:
6904a37 adds test for rate limit backoff behavior (4 hours ago) <justin>
Ok, that’s better, and might be good enough. There likely aren’t too many places you need to worry about rate limits in your code, so this is pretty specific. However, I still think there’s room for improvement:
6904a37 test summary_client rate limit backoff behavior (4 hours ago) <justin>
Now we’re talking. I know exactly what that commit is doing.
What tense or voice should your message use?
Internet people have lots of opinions on this.
Which one of these messages is better?
fix test in summary_client
# or
fixes test in summary_client
# or
fixed test in summary_client
Answer: All three are fine. The only thing that matters is whether the message is clear, and all of these are.
I can hear the objections: “but what about consistency with auto-generated commits?” To that objection I say: who cares? Consistency within code is very important—and hopefully enforced by an automated tool—but your git history is for future-you who has to fix something you thought was clever today. All that matters to future-you is that your message is descriptive so you can figure out what happened.
Notes
- There’s no hard rule on the length of “one line”. Github seems to wrap at around 70 characters, so aim for less than that. ↩︎
- Some codebases have a mandatory, automation-triggering tag in commit messages, which takes up some space. You should still try and keep to one line, but you can always add an additional
-m "[automation_tag]"
on a second line. ↩︎