Juan Cruz Martinez I'm an entrepreneur, developer, author, speaker, YouTuber, and doer of things.

Commitlint: Write more organized code

4 min read 1372

Commitlint Logo Over a Dark Blue Background

As developers, it’s common for us to jump into existing projects — sometimes with large code bases — to fix a bug or work on a new feature. We often start by navigating the source code to understand how it was built and how components within the application interact with each other.

But even when we find the exact place where a bug occurs, it may not be clear what the right solution to the problem is or how it got there in the first place.

Thankfully, there’s a tool called Git that can investigate who and why a particular code was changed or added when used correctly. But, if your experience is anything like mine, it’s possible that when accessing the Git history of your project, you find something like this:

Git History

Not very helpful, right? It is impossible to identify which commit is relevant to us, as all descriptions are the same or not, well, descriptive.

Can this be fixed? Let’s discuss how commitlint comes to save the day.

What is commitlint?

Commitlint is the ESLint for your commit messages. It performs validations on any text against a predefined commit format. Users can configure these formats to their needs or adopt pre-built-in conventions, such as conventional commits.

Because the tool can be piped to the output of other processes, it easily integrates with your development workflow by validating the messages right before committing changes, pushing, or using any other Git hook.

Before learning how to set it up, let’s see it in action:

Commitlint in Action

Installing commitlint

Commitlint is easy to set up for either npm or Yarn projects. Let’s start by installing the tool as a dev dependency.

Because we will be using the default configuration, we need to install two different commitlint modules, the CLI tool, and the actual configuration. From your terminal, run:

npm install --save-dev @commitlint/{cli,config-conventional}

Or, using Yarn:

yarn add @commitlint/{cli,config-conventional} --dev

Lastly, you need to create a commitlint.config.js file with your configuration options. To do that, you can execute the following in your terminal:

echo "module.exports = { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js

Setting up Git hooks with commitlint

For commit message validations to run automatically on every Git commit command, we will use Husky, a tool that enables us to set up Git hooks quickly.

It’s pretty straightforward, so let’s jump into the commands:

With npm

Install Husky

npm install husky --save-dev

Activate hooks

npx husky install

Add commit-msg hook

npx husky add .husky/commit-msg 'npx --no-install commitlint --edit $1'

With Yarn

Install Husky

yarn add husky --dev

Activate hooks

yarn husky install

Add commit-msg hook

yarn husky add .husky/commit-msg 'yarn commitlint --edit $1'

With everything now set up, we can try to commit with an invalid text format and see what happens:

~ git commit -m "commit"

⧗ input: commit

✖ subject may not be empty [subject-empty]

✖ type may not be empty [type-empty]

✖ found 2 problems, 0 warnings

ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint

It is clear that commitlint rejected the message “commit.” We also know the reasons why, so let’s fix our message and see the results:

~ git commit -m "chore: add commitlint on commit-msg"

[master (root-commit) e0f064f] chore: add commitlint on commit-msg

5 files changed, 3412 insertions(+)

create mode 100644 .gitignore

create mode 100755 .husky/commit-msg

create mode 100644 commitlint.config.js

create mode 100644 package-lock.json

create mode 100644 package.json

When the message satisfies the criteria, the commit command continues its workflow and stores the changes.

It’s all done. Commitlint is now validating all of your commit messages and helping you enhance your commit history. Now let’s discuss the default convention and how we can write quality commit messages.

Writing commit messages

Let’s leave the technical realm for a moment to focus on writing — more precisely, how to write good commit messages that are self-explanatory and pass the commitlint default validations.

A good typical commit message will have the following structure:

<type>(<scope?>): <subject!>

Let me explain each part.


The type is mandatory and determines the intent of the change. Here are possible values:

  • build: changes affecting build systems or external dependencies
  • ci: updating configuration files for continuous integration and deployment services
  • chore: updating grunt tasks etc.; no production code change
  • docs: documentation-only changes
  • feat: a new feature
  • fix: a bug fix
  • perf: a code change that improves performance
  • refactor: a code change that neither fixes a bug nor adds a feature
  • style: changes that do not affect the meaning of the code (white-space, formatting, missing semicolons, etc.)
  • test: adding missing tests or correcting existing tests


A scope is an optional value that provides additional contextual information about the change. For example, when the module’s name, npm package, or particular routine was affected.

The scope, when present, must be contained within parenthesis.


The subject is the headline of the commit. It should summarize in one sentence the nature of change.

More great articles from LogRocket:

For the subject, consider the following rules:

  • use the imperative, present tense: “change,” not “changed” nor “changes”
  • do not capitalize the first letter
  • no dot (.) at the end


The body is an optional space to provide additional information about the change, its motivation, and what was done. As it is the case of the subject, the body is written in the present tense.


Lastly, the footer is an optional placeholder for referential information, e.g., alert for breaking changes or refer US numbers or references.

Breaking changes should start with the word “BREAKING CHANGE:” with space or two newlines.

Examples of good commit messages

First, let’s look at some examples I’ve created:

Example 1:

feat(payment): add a new endpoint to calculate taxes

This allows the payment module to calculate taxes for an order based on the order information. Currently only US sales tax and European VAT are supported

Refs #45621

Example 2:

build(docs-infra): regenerate docs after deployment pipeline completes

Automates the process of building the documentation portal after changes are merged into develop, release and master branches.overloads.
PR Close #43614

Here are some other excellent examples from GitHub:

Example 1:

fix(bazel): construct a manifest file even when warnings are emitted

Previously if _any_ diagnostics were emitted, regardless of their category, the manifest would not be generated. This means that if a target emits only warnings and no errors, it would still fail to build because it does not generate all the required output files (specifically the `.es5.MF` file). Now the manifest file is generated as long as there are no error diagnostics in the result. This makes `ng_module()` support compiler warnings as a user would expect.

Added a test that uses extended template diagnostics to trigger the invalid banana in box diagnostic. This generates a warning and uses Skylib's `build_test()` to verify that it builds successfully. Unfortunately, there is no easy way to verify that the warning diagnostic is emitted at all. `expected_diagnostics` should be able to do that, but it doesn't seem to have any effect on `ng_module()` and may not be integrated. Instead, testing that a target with warnings builds correctly is the best we can easily do here without a deeper investigation.

PR Close #43582

Example 2:

docs: reviewed tag added (#43472)

PR Close #43472

Example 3:

test(router): refactor tests to not use deprecated loadChildren (#43578)

Many of the tests in the router code use the deprecated loadChildren as a string. This
has been deprecated for years and can easily be changed to just a function that
returns the module.

PR Close #43578


Developers hate spending time on trivial tasks such as formatting text. That’s why they built amazing automation tools, such as ESLint, Prettier, and now commitlint — to make their lives easier. More importantly, they built these tools because they know the value of having nicely formatted and standardized code and messages.

Would you invest time in this automation and process for the value it brings to you, your project, and your organization? I certainly do!

ESLint and Prettier are already part of our lives. Let’s welcome commitlint to the developer’s productivity tools family.

Thanks for reading!

Get set up with LogRocket's modern error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID
  2. Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not server-side
  3. $ npm i --save logrocket 

    // Code:

    import LogRocket from 'logrocket';
    Add to your HTML:

    <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
    <script>window.LogRocket && window.LogRocket.init('app/id');</script>
  4. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • NgRx middleware
    • Vuex plugin
Get started now
Juan Cruz Martinez I'm an entrepreneur, developer, author, speaker, YouTuber, and doer of things.

Leave a Reply