There are plenty of articles on writing a proper commit message. But the problem is more profound than the wrong messages. The core problem is that developers think about git as a code delivery tool, not collaboration. With this article, I want to describe the value of meaningful commit messages in general, not the essence of some specific rules.
There are seven more or less standard rules: https://chris.beams.io/posts/git-commit/#seven-rules.
Additionally, many teams have a standard to mention an issue id, but it is used mostly for integration with your issue manager. It can not be a rule itself because the issue describes only an intention and a goal, but not a change itself. For example, every codebase contains bugs, but there is no issue titled “Deploy a bug, I’m bored.”
Imagine you’re working on a feature. On your branch, you implement a refactoring of some generic component. This change does not cover business value. The issue does not describe this change. This is when you need to explain your work adequately the most. Such changes can secretly cause a bug in the existing code area that uses your generic component.
In one of the teams where I worked, we had a rule: “Code in a way, so once you are on vacation, your mates can fix bugs after you.” Thank you guys for this rule. I spread it worldwide. Always remember that you are not the only one who’s working on a project. And even if you are, writing proper commit messages can help you recall the reason for some unobvious changes.
These rules exist not to make your life harder but easier. It takes some time to write the right commit message. But as a result, it can reduce development time while making changes on top of something. Especially in tricky and unobvious parts, that takes the most of the time. But why not just commenting the code? Because comments describe only known cases. A comment exists only in one of the files while the commit joins the hole change. Analyzing a commit, you can see all of the related changes. Additionally, commit messages are visible only on demand, only when you want to explore previous changes.
Version control systems appeared as an answer for distributed collaboration problems, not to create them. And following community proven guidelines, you get an easy-to-understand code base. Because once you get a question, “Why?” you can find an answer in history. Using git, you can see the whole change list of your main branch. But, and it is more important, you can see a history of any line of your code.
Git as a messenger
You can think of a git commit as a message to other collaborators on a project. Your goal is to prevent questions by describing your changes. As an additional result, you will discover that you are making more meaningful changes. And by “you,” I mean your team/company, all of the contributors to the project.
PR as a public speech
Git allows us to send messages to an unknown amount of unknown people. It’s not like you’re talking to your friend or colleague in person. Git commit messages can be read by someone you don’t know. The closest real-life example, in my opinion, is a public speech.
So consider writing a speech while you code. By every single commit, you answer three questions: “how,” “what,” and “why.” You answer “how” with your code. And your commit message answers the rest. Comparing to a speech, you split it into small chapters. These chapters are independent or rely on a previous one. If you speak about three different topics at the same time, no one will understand you.
Why is it important?
Continuing our speech analogy, let’s say you present a solar powerplant. You are talking about used materials. But instead of “heating elements made of recycled aluminum,” everyone can see “bought Aeron chairs” on the screen “because no one reads it anyway.”
The same appears every day in a lot of codebases around the world. But it produces the same problem: it’s much harder to understand the information you get. It’s harder to review, and it’s much harder to investigate a bug produced by some old commit.
You might say that you’re not an open-source project, so you don’t need it. Open-source project contributors try to provide good commit messages because they understand that it’s easier to review and analyze while other contributors are unavailable personally. At the same time, new developers appear in your company; some people leave it. So your situation is not that far from a community of an open-source project.
Better understanding each other can take some time, but it reduces the development time in the long term.
And once again, new people appear on your team. The first step is onboarding. And analyzing the code base takes a lot of time in the beginning. Providing better-structured commits allows reducing this time and onboarding time in general.
Describing the full change list in a commit message, you naturally want to split into smaller pieces. And this is good because you can touch files from different modules while you work on some issue. Splitting commits by changes in different modules will guarantee that each module has only relevant changes. I apologize for such a specific example, but imagine that all changes in something like an HTML button are described with a business logic message. And it happens when developers don’t split changes into separate commits.
The goal is to narrow commits to as much specific as possible ones. Generic components provide their functionality to many places. They serve modules with business logic, but they don’t depend on them — no need to mention anything not related to this universal component updating it. Splitting changes in different modules in separate commits will allow you to write a correct specific message. Messages will help to analyze this component in the future. It is possible that after one of the commits, your CI will fall, but your goal is to deliver a working feature. Making a functional application on every single commit is terrific, but it’s much more essential to have a relevant searchable and ready to analyze history. Additionally, your codebase can contain some other unknown bugs, and the green build itself does not guarantee an absence of bugs. Conventional commits propose adding a commit type, and adding “BREAKING CHANGE” to the commit message can prevent unnecessary rollback to a broken revision.
One of the most common reactions to providing commit message rules is “I don’t want to write multiline messages. I’m a coder, not a writer.” And you shouldn’t. Large commit messages are an exception. Rules only guide you on how to handle them. One-line messages are good. One-line statements limited to 50 characters are the best. The only requirement here is to describe the change entirely. Once you feel you can not tell what and why with one line, you should consider writing a multiline message or breaking down your change into multiple commits.
One of the most crucial questions is how to validate that commit messages are relevant and meaningful. How can we create a git hook to check the content of a message? The answer is you should not. Developers can check messages on a review stage just like they check changes of a code. Is it an additional step? I wouldn’t say so. It is changing a focus. Instead of reviewing a code change, developers can check the adjustment to history. Until merging, your commits are not a part of history. You can do whatever you want: rebase your changes, amend, perform hard and soft resets. You can do everything to have a structured, meaningful change.
So there is no need to automate this process. Manual reviewing the whole adjustment to history will bring you to another level of collaboration. It will reduce investigating time and the amount of in-person discussions. And this is good because of two reasons. First, an author can be on vacation or leave a company by the time you need an explanation. Second, more than one person can ask for the same answer, and it’s not that easy to find time for repetitive discussions, mostly working remotely.