Good Commit Messages
By Laza Upatising on 2020-08-23 Home
Over the past couple of months I had the pleasure of hosting interns. One key skill I saw lacking in the interns was the ability to write good commit messages. This article, along with a couple more in the works, aims to bridge the gap between college acquired software engineering principles and some key skills needed in order to be an effective member in software engineering teams today.
Why Commit Messages
Teams need the ability to capture knowledge and rationale behind code. Strong teams have a robust culture of communication such that information dissemination among team members is a painless and constant process. From informing teammates of why a code is written the way it is, to ramping up new team members, commit messages serve as a key pillar in a team's communication process.
As an example, suppose a new engineer is resolving an issue where payments for a some test credit cards are failing when they should go through. Upon inspection of the payments processing module, the engineer sees the following code snippet. Please note the examples are all made up.
func processPayments(cardNumber string) error {
// ... prior code ...
// Reject test cards.
if strings.HasSuffix(cardNumber, "4400") {
return errors.New("test cards unsupported");
}
// etc...
}
The code comment indicates the code block may have something to do with the issue. However, there is still a lot of room for ambiguity:
- Why was the string suffix "4400" used to check for test cards?
- What about test cards that don't end in "4400"?
- Why are test cards rejected in the first place?
Often, commit messages can serve as a place to capture rationale that may not be suitable in code comments. Imagine you are a new engineer solving this issue: what would you want to find in the commit message alongside this code snippet?
With this setup, we will now explore what constitutes a good commit message and return to the example in the Back To The Example section below.
The Anatomy Of A Good Commit Message
A commit message describes what is being done and why. Of particular importance is to empathize with readers of the code that may not be thoroughly familiar with the codebase. Explain 'why' in a way that is comprehensible to readers that have little knowledge of the codebase. Keep in mind that engineers turn to reading commit messages when a particular bit of code is counterintuitive.
Why This Feature?
A commit message should explain why a certain piece of code is being written in the first place. Often times it can be in response to a feature request, but it can also involve fixing particular issues, working around nasty APIs, etc... Linking to external artifacts such as an issue, or API documentation, can also serve as additional context. Commits or pull requests that are mainly implementing features will focus on answering this 'why'.
Why This Implementation Method?
For particularly tricky implementations, commit messages should also explain why a particular algorithm or implementation method is chosen. For example, a change that partially unrolls a loop should include benchmarks that show that the particular loop is a hot spot, and the performance gains an unrolled loop provides.
How To Write Good Commit Messages
Now we turn to the structure of a commit message, rather than the content. Bear in mind that structure changes on a per-team basis, and it is often best to just follow the structure of other messages in your codebase.
Due to the UI of popular web hosted git repositories (GitHub, GitLab, BitBucket), you should follow the 50/72 rule. Namely, you should adopt the following form for your commit messages:
Short summary of 50 chars or less.
Detailed text which can span multiple paragraphs. Each paragraph
should be line wrapped at 72 chars.
Relevant Links:
- Issue 123: Foobar not bazzing: https://github.com/u/repo/issues/123
- You should use full sentences.
- Your commit messages should not contain egregious grammatical errors.
- You should use the imperative mood when writing commit messages. You are instructing the code to do your will: "Fix Issue 123: Foobar not bazzing.", "Refactor Foo module.", "Implement payments processing.", "Improve search performance.".
When To Not Write Good Commit Messages
In an ideal world, all messages are written in the style and structure detailed here. Inevitably, reality sets in and commit messages do not reach these lofty standards. After a grueling debugging session or a difficult and lengthy implementation, writing structured and descriptive commit messages is often that last thing on an engineer's mind.
However, it is important to at least capture what is being done and why. Often a two sentence description can fulfill these requirements. Including a link can serve as a good way to defer the work - you can always add more to the linked resource if needed.
Implement HTTP basic auth.
API server requires us to authenticate using basic auth.
FR: http://github.com/u/repo/issues/9
One can skip commit messages altogether when working on a prototype that will be discarded. If you are unsure whether the prototype will later evolve to a production codebase, at least describe what is being done.
Back To The Example
Let us turn back to the motivating example from the Why Commit Messages section. As an engineer tasked with fixing broken payments for certain test cards, what would an ideal commit message look like? First let's begin with a terrible message.
Fix test cards
The message describes what's being done, but contains no context. Why are test cards being fixed? What does 'fixed' even mean? Let's move on to a better message.
Avoid processing test cards.
Forwarding test cards to our payment processor causes them to charge
$100.
Now we have context on why test cards are rejected. However, it's still unclear why certain test cards are failing.
Avoid processing test cards.
Forwarding test cards to our payment processor causes them to charge
$100. http://docs.payments.example.com/testcard/dont-send-us-test-cards
indicates that test Visa cards causes issues on their backend.
We use the credit card suffix "4400" to check for test cards. See
http://creditcard.standards.example.org/test_credit_cards.
Fixes: http://github.com/u/repo/issues/321
Not only does the commit message describe why we're avoiding test cards, it also describes why we're using the "4400" suffix for the test card check. Suppose the reported issue is 'Mastercard test cards are failing'. You investigate the standards documentation and discover that "4400" suffix is actually used for all test cards: Visa and Mastercard. You investigate further into the fixed issue (http://github.com/u/repo/issues/321) to discover that it only wanted to avoid Visa card. Given these discoveries, you can safely change the if condition to use a more precise check for Visa test cards, and submit your PR with the confidence of fixing the issue without introducing regressions.
Final Thoughts
Software engineering is an incredibly knowledge intensive process. Commit messages are a well known and powerful tool to capture and transmit knowledge. From personal experience, concise and informative commit messages makes interacting with a codebase much more straightforward.
Remember that when writing commit messages, you are utilizing yet another form of communication to transmit domain knowledge to future teammates. You should try to empathize with these teammates: from the one who may be completely new, to the one being paged at 3AM.
Happy Coding!