Skip to main content
Skip to main content

Luke Howsam

Software Engineer

Conventional commits, a better way to commit

image of code that says "git commit -am 'feat(frontend): add warning text to product page'"
Published
Share

Introduction

Conventional commits define a standard format that commit messages should adhere to. When using conventional commits, the reader (i.e. someone using git log) can easily glean valuable context & info about a given commit such as whether the commit implements a new fixture, fixes a bug, refactors existing code, etc.

While I like conventional commits, I don't use it typically on every project. I've found it's quite common to just follow the convention of using the Jira ticket number as a template for a commit message such as:

BOARD-ID-3988 - add tests for BE product handlers 

In the case when I or the project however doesn't follow the above convention, I think conventional commits are a great way to go about making commit messages.

Standardizing commit messages

Conventional commits are simply a spec for adding human and machine-readable meaning to commit messages. The concept behind it is to provide a rich commit history that can be read by both humans and automated tools (think release tools such as GitHub releases, changelogs, etc). Conventional commits have the following format:

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

In practice this would look something like this:

feat(server): add endpoints for the new event system

Commit types

The <type> field provides the context for the commit. What intent did this change make? Did the commit introduce a new feature? improve unit testing? fix a bug? etc. The <type> field can be quite flexible and your project can define what values you want to use. I typically use the following on a daily basis:

  • feat - This commit implements a new feature

  • chore - This commit includes a maintenance task that is necessary for managing the repository but doesn't introduce a major change. A good example of when I use this is when I've made a typo or am cleaning up some leftover work from a separate PR.

  • ci - This commit makes changes to continuous integration scripts or configuration files

  • fix - This commit fixes a bug

  • test - This commit enhances, adds to, revises, or does something else to a suite of automated tests in the repository

Scopes

The scope field is optional and is used to tag a given module or package in the repository. The Scope field is flexible, like the <type> field can change based on the project you're working on. If you have several packages inside a monorepo, you could preface the Scope field with the area you're working on i.e.

feat(common): add common utilities

Description

The <description> field is a short summary of the intent or content included in the commit. The description field can be used as a title to introduce the change and then you can write a further description on your Git provider to provide more details.

When writing the description, it should be written in the imperative or future-tense mood, such as 'change' instead of 'changed' or 'fix' instead of 'fixed'. Instead of writing created function MyFunction I will write create function MyFunction.

Footers

Footers are optional & I don't typically use them too much but they can be a great way to provide additional metadata for a commit. I typically use them for linking related issues, issues that will be closed by this PR, etc. Example:

  • Closes #3232
  • Resolves #1111

Enforce conventional commits adoption

Adopting & following the conventional commits spec is one thing, but enforcing and making sure that everyone is following these standards is another thing. Fortunately, there are a few tools that you can use to ensure that commit messages follow the standards you set out. I typically utilize tools that make use of commit hooks to ensure these rules get enforced before a push occurs.

  • Commit lint - commitlint is a nodejs-based tool that will validate commit messages to ensure they use the conventional commit convention. This tool can be combined with a pre-commit tool such as Husky to ensure that a commit does not succeed if it doesn't meet the team's rules for properly formed commit messages.