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.
npm install --save-dev husky
npx husky initThe husky init command will create a file with a default script:
npm testYou 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.
npm install -D @commitlint/cli @commitlint/config-conventional We use
@commitlint/config-conventionalas 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.
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
extendCommitlint 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.
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.
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:
❯ 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.
HUSKY=0 git ... # Temporarily for single command
# For `multple` commands
export HUSKY=0 # Disables all Git hooks
git ...
git ...
unset HUSKY # Re-enables hooksFor GUI users (same can be used to globally disable Husky):
export HUSKY=0 # Husky won't install and won't run hooks on your machineHusky'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!