Semantic Commits for the Impatient

2023 11 02 head

The conventional commits specification is a lightweight convention on top of commit messages. The additional information classifies the type of work performed in the commit.

A developer infers the importance of the task by looking at the type of the commit. The message communicates the nature of changes to teammates, the public, and other stakeholders.

Contributors of project repositories know that a well-designed Git commit message is the best way to communicate the context of a change to other developers. And in fact, they will help themselves in the future. A diff will tell you what has changed, but only the confirmation message can say why.

It provides an easy set of rules for creating an explicit commit history which makes it easier to write automated tools on top of.

The development team can use scripts to filter and aggregate commit information based on the tags in the commit message.

This convention dovetails with SemVer, by describing the features, fixes, and breaking changes made in commit messages.

Structure

The commit message should be structured as follows:

<type>[optional scope]: <description>           (1)

[optional body]

[optional footer(s)]
1 Use the body to explain what and why. Do not explain how. That is the code.

The commit contains the following structural elements to communicate intent to the consumers of your library:

fix

A commit of the type fix patches a bug in your codebase. This correlates with PATCH in Semantic Versioning. It is used to identify production changes related to backward-compatible bug fixes.

feature

A commit of the type feat introduces a new feature to the codebase. It is used to identify production changes related to new backward-compatible abilities or functionality. This correlates with MINOR in Semantic Versioning.

BREAKING CHANGE

A commit that has a footer BREAKING CHANGE:, or appends a ! after the type/scope, introduces a breaking API change. This correlates with MAJOR in Semantic Versioning. A BREAKING CHANGE can be part of commits of any type.

Other types are allowed, for example:

build

The build type is used to identify development changes related to the build system. They involve scripts, configurations or tools, and package dependencies.

ci

The ci type is used to identify development changes related to the continuous integration and deployment system. They involve scripts, configurations, or tools.

doc

The doc type is used to identify documentation changes related to the project. Whether intended externally for the end users (in the case of a library) or internally for the developers.

refactor

The refactor type is used to identify development changes related to modifying the codebase, which neither adds a feature nor fixes a bug. Examples are removing redundant code, simplifying the code, or renaming variables.

test

The test type is used to identify development changes related to tests. chore: Activities not adding new features or fixing a fault. This type is used when you do not know which other tag you could use.

footers other than BREAKING CHANGE: <description> may be provided and follow a convention similar to git trailer format.

How to Create a Semantic Commit with Git

The command line for a complex git commit message is simple:

git commit -m "fix(core): remove deprecated and defunct wtf* apis"                              (1) (2)
           -m "These apis have been deprecated in v8, so they should stick around till v10."
           -m "PR Close #33949"
1 We use multiple -m to concatenate paragraphs instead of simple lines. The header and body are supposed to be separated by a blank line. That is distinctly true due to the paragraphs.
2 Summary in present tense.

Your IDE probably provides a comfortable user interface to create a commit message as a set of paragraphs.

Use your preferred IDE to write semantic commits with multiple blocks.

Thoughts

Please start with a small list and extend it if needed. I suggest: feature, refactor, fix, build, chore [1].

Ask yourself if identifying the subsystem where the changes were performed makes sense for your application. Often changes are not local to a specific component.

Some teams put a ticker number instead of the subsystem name. It makes sense if your platform transforms the ticket number into a link to the ticket content.

I use the tags feature, refactor, build, and chore. I do not put component or ticket information in the parenthesis after the tag.

I put the ticket number in the summary of the commit message.

I seldom use the footer for breaking change information. I handle this information through semantic versioning in tags.


1. I never needed the doc tag. Feel free to use it if you find it useful for your product.