GitHuskyCommitlintConventional Commits

Git Commit Rules with Husky and Commitlint

Use Husky and Commitlint to enforce Conventional Commits with a lightweight commit-msg hook, without making every local commit slow or fragile.

·Updated ·8 min read·Counting...
Git Commit Rules with Husky and Commitlint

Husky and Commitlint are not about making commit messages look pretty. They automate one basic collaboration rule: every commit should state what changed and where. That makes code review, changelog generation, release notes, and rollback investigation more reliable.

The highest-value first step is usually a commit-msg hook, not a heavy pre-commit pipeline.

What problem this solves

Commit message rules are worth adding when a team repeatedly sees:

  • Commits named update, fix bug, or changes.
  • One commit mixing unrelated work.
  • Release notes that require manual archaeology.
  • Changelog or semantic-release tooling with unreliable input.
  • Reviewers unable to understand history from Git alone.

The goal is not to replace review discipline. It is to remove the lowest-quality commit messages before they enter shared history.

Commitlint's job

Commitlint checks whether a commit message matches configured rules. It does not write the message for you.

Common Conventional Commit shape:

<type>(scope): <subject>

Examples:

git commit -m "feat(auth): add Google login"
git commit -m "fix(search): handle empty keyword"
git commit -m "docs(readme): update setup steps"

For most projects, extending @commitlint/config-conventional is the practical starting point.

Husky's job

Husky connects scripts to Git hooks. For this workflow, it runs Commitlint when Git is about to create a commit.

Install the tools:

npm install --save-dev husky @commitlint/cli @commitlint/config-conventional
npx husky init

Modern Husky setup commonly uses husky init, which creates the .husky/ directory and updates the package prepare script. Older tutorials that use husky install and husky add may be version-specific.

Minimal Commitlint config

Create commitlint.config.js:

module.exports = {
  extends: ["@commitlint/config-conventional"],
  rules: {
    "type-enum": [
      2,
      "always",
      ["feat", "fix", "docs", "style", "refactor", "test", "chore"],
    ],
    "subject-case": [0],
  },
}

Then update .husky/commit-msg:

npx --no -- commitlint --edit "$1"

Now an invalid message fails before the commit is created.

Why start with commit-msg

Many teams put too much into pre-commit too early:

  • lint
  • tests
  • typecheck
  • format
  • build

That can make local commits slow and brittle. Developers then start bypassing hooks, which defeats the purpose.

A commit-msg check is cheap, fast, and easy to understand. Add heavier checks gradually through lint-staged, local scripts, and CI after the team trusts the workflow.

Branch strategy should stay simple

Commit rules do not require a complex Git Flow setup. Small teams often do well with:

  • main: releasable code
  • feature/*: new work
  • fix/*: bug fixes

Clear commits and small PRs matter more than an elaborate branch naming system. For the basic flow, see A Practical Git Workflow: Add, Commit, Fetch, Pull, and Push.

What to add later

After commit messages are consistent, you can extend the workflow:

  • Generate changelogs.
  • Use semantic-release.
  • Re-check commit messages in CI.
  • Add PR templates.
  • Use lint-staged for staged-file checks.

The order should be: standardize messages, enforce them locally, then connect them to release automation.

Common misunderstandings

Husky is not Commitlint. Husky runs hooks. Commitlint validates messages.

Valid format is not the same as a good commit. feat: update many files may pass the rule and still be too broad.

Old Husky tutorials may not match your installed version. Check current tool docs before copying setup commands.

Subscribe to FreeMac

Weekly picks: free Mac software reviews, trusted source updates, alternatives, and low-friction guides.