Git Good: Automating Commit Message Standards with Husky and Commitlint

Added on December 14, 2025


The most difficult part of agreeing to follow best practices and standards is getting into situations where you just want to get things done and standards are no longer a concern.

I'm sure the best of us have been in situations where we needed to solve a problem instantly in a magical way; that's when we removed the "Ideal developer" mask and went full ninja mode.

“Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live” ― John Woods

Another example is while working in a team and everyone just wants the task completed (as if they're not the one to modify it the next day). A better approach is to implement certain rules in our project to ensure that we do not simply ignore the standards.

This is a guide on how to setup Husky and Commitlint to enforce the commit message practices. A continuation of my last blog Git Good: Best Practices for Branch Naming and Commit Messages.


Husky Setup

Husky is a modern way of implementing Git Hooks. Or like the readme says:

Modern native Git hooks made easy. Husky improves your commits and more 🐶 woof!

Husky is extremely lightweight and fast! Linting commit messages (enforcing message format) is merely one of its many uses. Husky can be used to "Automatically lint your commit messages, code, and run tests upon committing or pushing".

Install and Initialize Husky using following commands, for other package managers check here.

shell
npm install --save-dev husky
npx husky init

The husky init command will create a file with a default script:

.husky/pre-commit
npm test

You can safely remove it because we will overwrite it later.


Install and Configure Commitlint

Commitlint is a tool that exists just to ensure that you write your commit messages properly. Install @commitlint/cli and @commitlint/config-conventional as dev dependencies.

shell
npm install -D @commitlint/cli @commitlint/config-conventional  

We use @commitlint/config-conventional as our base configuration because we developed our best practices with Conventional Commits as the foundation.

Configuration

I created a configuration that applied all of the commit message standards outlined in the last blog post. You can add, update, or remove any ruleset; the @commitlint/config-conventional is enough for a normal use case.

commitlint.config.mjs
const config = {
  extends: ["@commitlint/config-conventional"],
  rules: {
    // 1. Format: <type>([optional scope]): <description> - enforced by most rules below.
    // Enforce that the type is not empty.
    'type-empty': [2, 'never'],
    
    // Enforce specific commit types. Add/remove types based on the project.
    'type-enum': [
      2, 
      'always', 
      ['feat', 'fix', 'build', 'chore', 'ci', 'style', 'refactor']
    ],
    
    // Subject/Description Rules:
    // 2. Short and Summarized: Try to fit the subject line inside 50 characters.
    'header-max-length': [2, 'always', 50],
    
    // 3. Capitalize the description: Start subject line with a capital letter.
    // Imperative Mood. Mostly useful for generating changelogs.
    'subject-case': [2, 'always', 'sentence-case'],
    
    // 4. Avoid trailing period.
    'subject-full-stop': [2, 'never', '.'],
    
    // Body Rules:
    // 5. Body is added by leaving a blank line after the subject line.
    'body-leading-blank': [2, 'always'],
    
    // 6. Wrap the body at 72 characters.
    'body-max-line-length': [2, 'always', 72],
 
    // Footer Rules:    
    // Ensure a blank line precedes the footer.
    'footer-leading-blank': [2, 'always'],
    
    // Rule for Optional Scope:
    // Enforce that if a scope is used, it is in lower-case.
    'scope-case': [2, 'always', 'lower-case'],
  } 
};
 
export default config;

Place this config file in your project root.

It is important that you extend Commitlint with any of your preferred configuration libraries (as seen in line 2 above) in order to perform linting. As previously mentioned, other options are entirely optional. Refer this for advanced configuration.

Add Hook

Add the following content to the commit-msg hook file in the Husky folder.

.husky/commit-msg
npx --no -- commitlint --edit $1  # $1 points to the commit message file that git created.

That's it. Now the Commitlint will lint your commit messages based on the added config.

Test Commitlint

To test your Commitlint configuration, make a git commit.

shell
git add --all
git commit -m "chore: Add linting on commit message"

If the commit format does not follow the configured rules, an error will be thrown, preventing the commit action.
For example:

shell
 git commit -m "foo: this will fail"
   input: foo: this will fail
   subject must be sentence-case [subject-case]
   type must be one of [feat, fix, build, chore, ci, style, refactor] [type-enum]
 
   found 2 problems, 0 warnings
   Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
 
husky - commit-msg script failed (code 1)  

All that being said, most of the simple projects we work on do not require such strict standards. Still, I keep this configuration in all new projects to develop this into a habit.

It is worth noting that Commitlint offers some other interesting features, such as the Prompt option.

Escape Hatch

Because we are forcing ourselves to follow these standards, there may be times when you just want to get around them. These are the methods to disable the hook, hence the Commitlint.

shell
HUSKY=0 git ...  # Temporarily for single command
 
# For `multple` commands  
export HUSKY=0 # Disables all Git hooks
git ...
git ...
unset HUSKY # Re-enables hooks

For GUI users (same can be used to globally disable Husky):

~/.config/husky/init.sh
export HUSKY=0 # Husky won't install and won't run hooks on your machine

Husky's guide on Skipping Git Hooks.

Conclusion

This guide is a follow-up to my last blog, and as I indicated then, enforcing these standards may not be strictly necessary in this era of coding. This strategy is for those who desire to maintain the best quality in whatever they do.

Lint-staged is another tooling library that I would recommend since we used git hooks for this configuration. Lint-staged, when used with Husky, performs final touch-ups before committing, such as linting, formatting, and so on... It's really useful for maintaining code quality when working in a team.

Good day!

References

Git Good: Automating Commit Message Standards with Husky and Commitlint