The art of writing meaningful Git commit messages

Git commit messages can seem deceptively easy to write on a cursory glance. These small blurbs of text describe the change that is being committed to your repository. This sounds easy to accomplish, yet there’s a skill involved in writing something that communicates meaning and intent to other contributors. A difficult to understand commit message might rely on implicit information not available to the reader or it can overwhelm with too much detail. The act of writing a good commit message is a nuanced and artful discipline that requires practice.

Commit messages generally contain a subject line that is optionally followed by a blank line and an explanatory body of text. Common conventions and rules include keeping the subject line to 50 characters to enhance readability; wrapping lines of the body at 72 characters; and pushing any Git trailers to the end of the message. You can explore more about committing a feature to Git in the thoughtbot guides

Conventional Commits

Conventional Commits standardizes the format of Git commit messages and enforces some additional rules. These rules, according to the specification, add human and machine readable meaning to commit messages. The messages are written to conform to the following format:

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

Deconstructing this a bit, a commit type is prefixed to the front of the subject line in order to categorize the type of work being done. An optional scope can be included to contextualize and constrain the changes to a particular area of code. This scope can represent a subject, technical concern, or business domain. These elements are then followed by a description. The specification doesn’t enforce much about the description beyond that it should be a short summary of the changes. I’ll cover later how I believe this description should follow the same best practices that would make any commit message readable and meaningful. The specification wraps up with a freeform body of text and some guidelines on including tokens in the footer (Git trailers).

The immediate and overt benefit to this format is the enforced standardization facilitates making commit message machine parsable. This, in turn, enables the use of tooling to automate tasks such as: linting; changelog generation; version bumping; or triggering other automated processes in your CI/CD pipeline. This objective is all well and good, but you could accomplish the same thing with Git Trailers. Why would you want to write a Conventional Commit?

What’s the Point?

At the surface level, the conventional commit format is a simpler and more approachable strategy for adding metadata to a commit message. It’s faster and easier to add a coded type and scope to the front of the subject line than it is to add a Git trailer.

Still, I don’t believe it’s enough to do something simply because it’s easy. Whats more is that the practice of prepending metadata to the start of your commit message can be polarizing. Prepending the type (and optional scope) can be seem arbitrary and perfunctory; these components don’t seem to add much to the human element which should be the focus and purpose of a commit message. In the absence of any tooling utilizing the format, the format can be seem incidental and superfluous at best, or noisy at worst. What’s more, the added information can infuse the message with a mechanical or robotic feeling. What benefits does this format afford to humans?

What Is the Human Benefit?

As previously stated, the primary purpose of a commit message is to communicate with future contributors and describe what change is committed to the code repository. In this context, the prefixes facilitate a form of cognitive funneling when scanning a list of commit messages. The format lays out information in an increasing level of detail so that as you read through the subject line, you are presented the broadest level of details first and the most narrow and specific last.

In other words, the prefixed types and scopes serve as an escape hatch. Scanning through the list, quicker judgement calls can be made to evaluate whether a commit is relevant to any given need or inquiry. This is because certain subject lines can be skipped after reading the prefix. If you were, for example, searching for changes related to feature work, greater attention could be directed towards messages tagged with feat while you could ignore irrelevant tags such as build or ci. On the other hand, when looking for a bug fix, messages including the fix type can be singled-out. Consistent use of prefix types across an entire team can enable filtering the Git history on type or scope.

Additionally, we can evaluate the benefits of Conventional Commits from a development oriented lens. Writing messages in the Conventional Commit format forces me to think about what sort of change I’m making and how that change relates to the the scopes and domains of the application. Using a standardized format reduces the cognitive cost and friction involved with consistently presenting that information. Knowing that work must be tagged with a type encourages me to construct commits that are smaller, controlled and more atomic.

The constraints prevent writing a concise commit message to relay that a commit fixes a bug, implements a feature, modifies the CI configuration, updates docs, adds tests, fixes style, and refactors code. That’s too many changes to include in a single commit and each change maps to its own type tag. This can signal that work can be diffused or decomposed into more granular commits. So when I get carried away making too many changes without committing any of them, the format serves as a final reminder to commit more digestible units of work.

A Commit Needs To Describe What It Accomplishes

The specification remains rather agnostic towards the contents of the description, offering only that it is “a short summary of the code changes”. I believe this is a critical component that should follow the same best practices and guidelines that would apply to any unadorned subject-line of a commit message.

Some best practices for shaping a commit include that it should:

  • be small, atomic and focused
  • specify the type of work being committed
  • represent a logical and cohesive changeset
  • be reviewable and digestible
  • be self-contained and discrete

The subject-line of the commit message supports these objectives. It should be short; clear; concise; descriptive; meaningful; and communicate the context surrounding a change. Whenever possible, it’s best to avoid framing the message in terms of what you did as well as how you did it. Restating the original issue or problem only establishes the baseline state as it exists before the commit is applied. A literal description like this provides an anchor point to contextualize the work, but won’t necessarily reveal why the change was necessary. It’s an insufficient amount of information to serve as a summary.

Instead, the subject-line should orient towards describing what the change accomplishes. Have it describe the effect that applying the commit will have to the application. What is the resulting context and how is the issue addressed or solved? When the subject line isn’t long enough to fully describe and communicate the context, a long form description can be provided in the body of the commit message.

Below are some additional guidelines I like to keep in mind when writing a subject line:

  • the description should be succinct, but not terse
  • provide enough information without being verbose
  • write in imperative mood, present tense, active voice
  • don’t assume the reader will have access to any implicit information
  • avoid stating the obvious
  • avoid disposable/superfluous commit titles
  • avoid vague or ambiguous titles
  • avoid amorphous (shapeless) titles
  • avoid misleading descriptions
  • avoid disinformation

In summary, the standardized structure enforced by Conventional Commit specification presents benefits for both machine and human. There’s more to it than initially meets the eye. Finally, whether or not you adopt the Conventional Commit, it’s always a good practice to check that the subject line of a commit message clearly communicates what the change accomplishes.

References and Resources