Writing 10x Git Commit Messages
There’s been talk about 10x developers, but have you ever seen 10x git commit messages?
Crafting a good commit message today can yield a 10x return down the line. I’ve seen this firsthand in codebases lasting 10+ years where decisions were made and forgotten long ago.
Having a commit message provide the right information at the right time saves frustration, speeds up work, and improves code quality.
Here you’ll find the tips I believe can help craft 10x commit messages.
If you’re already sold on why this is important, feel free to skip ahead.
Why bother?
It’s Monday morning, and you set out to fix a bug.
You narrowed it down to an older section of the codebase.
It’s unfamiliar to you, so you fire up git blame
and see what the commit has to say:
Fix odd bug
https://trello.com/c/zNXUtvnX
Great. Not much of an explanation. What about the diff? That shows a refactoring, a whole bunch of files that were changed for what seems like reformatting purposes, and the code change you’re interested in.
You also just realize: the author left the company a month ago, and your team uses Jira for issue tracking, not Trello. So of course that link didn’t work, and the context behind the bug is still elusive.
This is a very contrived example. A much more realistic one can be found in A Branch in Time (a story about revision histories).
But I have been in a similar position quite a few times. It can be frustrating having to guess at why things were done a certain way. It can even be error prone if you make the wrong assumptions. You may end up going down rabbit holes that don’t lead anywhere. You just don’t know how big of a yak shave it will be.
A commit message becomes 10x when it gives you the brief insight into what the author was thinking at the time. When you can quickly understand all the context, assumptions, and constraints behind that portion of the code.
Why use commit messages?
A commit message is a lightweight form of documentation that doesn’t get outdated as it is bundled directly with the change it describes. It’s in plain text, with no dependencies, so it’s future-proof and doesn’t rely on an external service.
When taken advantage of, a commit provides a snapshot in time capturing important context. It tracks details that might easily be forgotten in 6 months time, or if someone leaves the team.
When viewed in aggregate over a timeline, each commit contributes to refining the mental model of what’s happening in the codebase. It can make reviewing a Pull Request much faster, and speed up getting familiar with an unknown codebase.
When creating something from scratch, your mental model gets built naturally each step of the way. On the other hand, contributing to an existing codebase requires a lot of effort because you have to re-create that mental context in bigger chunks. You do this based on tribal knowledge from teammates, the code itself, reverse engineering, and reading any available documentation.
With well-written commit messages, you speed up that process. It removes a lot of the guesswork and reverse engineering to understand what’s going on because the important context was already captured.
This does require taking more time with your commits, but it has the potential to 10x the return by quickly unblocking teammates and even yourself in the future.
Crafting 10x Git Commit Messages
When you’re working on a change and committing your code, you have all the context fresh in your head.
That’s the best time to take a few extra minutes to craft your 10x commit message. It’s like insurance: you don’t know if you’ll need it, but when you do, you’re glad you had it.
What I’ve found that works well is to:
- Write well-formed commit messages
- Make your commits atomic
- Write your commit message like a blog post
- Tell a story with your commit history
Write well-formed commit messages
First, ensure your git commit messages are well formed. The first line shouldn’t go over 50 characters. The body should start on the second line and not go over 72 characters.
Follow the agreed-upon convention for writing commit messages within your team, like Conventional Commits, Arlo’s Commit Notation, or something else. Consistency is key.
See A note about git commit messages and How to Write a Git Commit Message for more insight.
Make your commits atomic
Git commits should be atomic, just as evergreen notes should be atomic.
Each commit should focus on one concept and capture it fully. Think Single Responsibility Principle from SOLID.
A single commit that includes both the tests that expose the bug and the corresponding fix is better than two separate commits: one for the fix, the other for the tests.
This way, a single commit shows all the changes together. It’s easier to understand the whole picture without having to jump around to other commits.
Write your commit message like a blog post
Treat your commit message as a short blog post. The audience is a teammate or your future self.
Focus on “why” you made the change, the context, the constraints, any reasoning. Forget the “what”, that’s already in the commit’s diff. Write enough to make the need for the change clear.
It doesn’t take much longer to do this, and oh is it so helpful. I’ve had people thank me for explanations I’ve left in commit messages years ago. They mentioned the context had helped to understand why a particular approach was taken.
Use your judgment though. There’s no need to be overly verbose on small/obvious changes. Err on the side of caution if it doesn’t come naturally to you.
Include links to other resources in your messages.
Internal links such as references to other commits are very handy. Github automatically turns commit SHAs into links you can navigate to. This is useful when your commit builds upon previous work (e.g. fixing a bug introduced in a previous commit).
External links to blog posts or specs that you referenced while making the change provides additional insights. Linking to wiki pages or issue tracker tickets help understanding where the change fits into the big picture.
Beware though! External references may disappear and issue trackers can change. In the first 10 years at Talkdesk we went from Github Issues to Trello to Asana to Jira. Plain text is future-proof.
Duplicating just enough information in the context of your commit-as-a-blog-post is helpful. Like keeping your tests DAMP (Description And Meaningful Phrases) rather than dry DRY (Don’t Repeat Yourself).
Here’s an example taken from this blog’s repo, showing a fix commit that points to the the one where the problem was introduced:
There’s another example in the post: My Favourite Git Commit.
Tell a story with your commit history
The final step to unblock the 10x return on investment is: treat your local branch’s history as malleable and shape your narrative with it. You’ll end up with an easy to digest history that can get anyone up to speed quickly.
Keeping all the intermediate commits creates a lot of noise. There’s no need to show how the sausage was made, e.g.:
Add test for person listing bug
Refactor person related queries
Add fix for person listing bug
Address PR feedback
On the other extreme, grouping all changes within a single commit can mix various concepts together and obscure important details. This is why I avoid doing a squash and merge of a feature branch into a single commit in the mainline, e.g.:
Fix bug in person listing ordering
This hides the opportunistic refactoring the author made that didn’t directly impact the bug fix itself (make the change easy, then make the easy change).
The goal is to show a clean story with your commit history, with just enough detail, e.g.:
Refactor person related queries
Fix bug in person listing ordering
Where:
- The refactor step is separate, it’s not directly related to the bug fix;
- The bug fix itself includes the implementation and corresponding tests;
- Any feedback from a code review from either the refactoring or the bug fix is squashed into the corresponding commit.
For instructions on how to manipulate the history safely, see: Write Better Commits, Build Better Projects.
Takeaways
A well-crafted git commit message is lightweight documentation that doesn’t go stale. With a little investment, you’ll 10x your return for your teammates and even your future self.
You already have the context on top of mind, take advantage of it. When you next write a commit message, or before asking for a code review, remember to:
- Write a well-formed commit message;
- Make each commit atomic, with one concept, one single responsibility;
- Write the message as a blog post, focusing on why the change is needed;
- And tell a story with your commit history, where each snapshot in time contributes to an easy-to-follow narrative.
You’ll be thanked :)
Published on by Raoul Felix, putsdebug.com (c) 2024