Git commit messages are useless. Yes, I said it. I’m calling a fundamental part of Git useless. Git just doesn’t model modern workflow practices anymore. Okay, it’s not all commit messages. Commit messages on your main branch are good and needed. Commit messages on your random PR branch which will get squash-merged into
main are completely useless. They’re the author’s local save points, and not relevant to anyone other than the author. In fact, for the vast majority of authors they’re not even relevant to the author. We all write them, and they cost the collective engineers of the world countless hours writing ridiculous messages like, “changed something”, “did work”, or the super descriptive, “stuff”.
Git recently added an
--allow-empty-message flag, but really this is backwards. Commit messages should be empty by default. If you want to name your save points, you should have to do so explicitly. Actually, why doesn’t it just constantly autosave your work, like editing text anywhere else (Google Docs, Notion, etc.)?
Requiring commit messages is a vestige of engineering practices from the ancient times of the mid-aughts. They are the human appendix of Git-based development — just unnecessary. With modern trunk-based engineering, pull request branches are squash-merged onto
main — and all those silly commit messages are thrown out in the process.
This isn’t grade school, you don’t have to show your work
As you become an efficient engineer, the path you took to get to the final state of a pull request becomes far less important — and is academically interesting at best. You shouldn’t have to show your work like you did in school. Land the feature or bug and move on to the next one. The code speaks for itself (alongside some judiciously placed comments).
Having granular annotations of all your work is unnecessary, the same way you don’t need to write a description for every change you make in a Google Doc, or write a comment for every single function in your codebase.
main tells the real story. The journey along the way is inconsequential. The ultimate source of truth is the code — everything else is a slowly decaying version of the truth. Code comments are useful to explain something clever or novel, but not the self-explanatory parts. What actually matters is what gets built — not the original spec or accompanying annotation.
High noise, low signal
Through rote action, we’re teaching our brains to ignore the flaws of a tool — in this case leading us to write commit messages without thinking, and we end up with something like this:
When we lose faith in our tools to deliver meaningful signals, bad practices emerge. We can see similar bad user behavior in regard to test failures. When our tests are flakey, we start to write off the signal a failing CI run provides. The noise drowns out the signal to the point where we consider everything noise.
This loss of trust makes it unclear which rules to follow and which to circumvent — ultimately leading to sloppiness and possibly even breakages.
No one cares what you had for breakfast
As developers, we care so much about our tools and processes. So we should always look for unnecessary processes and eliminate them. At Trunk, we’ve configured our Git setup to not enforce the arbitrary commit message required rule. This saves us a lot of time, and we highly encourage everyone do the same:
With a siloed, universal single source of truth, our pull request commit histories become useless. Nobody cares what we wrote in our commit messages, whether they’re good or not. Today, additional context is shared through email, Slack, or docs. As we evolve, we should identify more ways to improve our workflows and minimize mental overhead.
Squash-Merge or Bust
Git commit messages on your main branch should be autogenerated by GitHub based on your pull request title and description, and your pull request should be merged into a single commit on your main branch. This means that you, the author of changes, never directly need to write a commit message. To do that, you’ll want to set a few things:
1. Set the GitHub commit message for PRs merged into your main branch
Change the squash-merge commit message. The default message uses the commit title and description if the pull request contains only one commit, or the pull request title and list of commits if the pull request contains two or more commits. As we covered, no one cares about your local commits to achieve this pull request, so why would they want it polluting Git history for all time? It’s a bad default.
While you’re at it, once those PRs are merged, there’s zero reason to keep the branches with your unimportant save points around:
2. Stop bothering with local commit messages
Unfortunately Git doesn’t support setting
--allow-empty-commit by default in your global git config yet, but you can alias a different commit command to do it:
git config --global alias.nccommit 'commit -a --allow-empty-message -'
Note: this requires a recent version of Git which supports the
3. Make sure to disallow anyone from accidentally committing to your main branch
This ensures that the only commits to your main branch come from PRs, and if you’ve set the PR squash-merge commit message as mentioned above, you’ll have great commit messages on your main branch.
Where we’d like to see Git and GitHub go next
We’d really like Git to have first-class knowledge of the differences between a transient PR branch and long-lived branches like
main. That opens up the possibility to implement things like autosave functionality, or conditionally requiring commit messages based on the branch. Right now, the closest thing we have is Branch Protection rules in GitHub, which Git has no awareness of. A more iterative step would at least provide the ability to set the Git config for
git commit to always allow empty messages.
In GitHub, it should be difficult to do anything other than squash-merge PRs into a
main branch — using the PR Title and Description as the default commit message. There are a scary amount of companies we talk to that merge but don’t squash-merge just because they don’t know any better. The extreme repo-bloat that accumulates is nuts. Everyone’s “local save points” pollute the history of your main branch forever.
It also breaks a really important property everyone should be striving for: every commit on
main should work. Local commits on your PR branch don’t need to work — in fact they’ll often be broken. Additionally, GitHub should either stop showing local commits on a PR branch, or make it harder to find. It’s just noise that no one cares about.