Wiring commitizen into an existing repo Jump to heading

A guided prompt solves a specific problem: people who know the convention exists but not what it requires. It is not a substitute for validation, because anyone can bypass the prompt with git commit -m, and it is actively harmful if it becomes the only way to commit — the first time someone is mid-rebase with a conflict and the prompt will not run, they will reach for a workaround that disables everything. This recipe adds it as an option alongside a hook that remains the real gate, within commit message hooks and templates.

When to use this approach Jump to heading

  • Your project uses a structured convention such as Conventional Commits.
  • New contributors get the format wrong in ways review has to correct.
  • The repository already has a Node toolchain, so the dependency costs nothing new.
  • Your validation hook rejects often enough to be annoying, which is the signal a prompt helps.
  • If your convention is loose, a prompt will feel like an interrogation; use a template instead.

Step 1 — Install it without changing how commits are made Jump to heading

npm install --save-dev commitizen cz-conventional-changelog

# Register the adapter in package.json rather than in a global config
npm pkg set config.commitizen.path='cz-conventional-changelog'
npm pkg set scripts.commit='cz'
# Verification: the guided path works and the ordinary path is untouched
npm run commit -- --help >/dev/null && echo "prompt available"
git commit --allow-empty -m "chore: ordinary path still works" && git reset --hard HEAD~1

What changed: npm run commit opens the prompt; git commit behaves exactly as before. Nothing is forced, which is what keeps the escape hatches unused.

Two paths to the same validated commitThe guided prompt composes a message that satisfies the convention by construction. The ordinary editor path is unchanged and relies on the validation hook. Both end at the same gate, so neither is a way around the rule.npm run commitguided questionsformat by constructiongood for newcomersgit commiteditor and templatemuscle memoryworks mid-rebaseboth paths are validated by the same commit-msg hook — the prompt is a convenience, not the gate

Step 2 — Keep the validation hook as the authority Jump to heading

The prompt cannot be relied on, so the hook stays. Point both at the same configuration file, or they will disagree the day someone edits one of them.

// commitlint.config.js — one source of truth for the shape of a message
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'subject-max-length': [2, 'always', 72],
    'scope-enum': [2, 'always', ['api', 'web', 'infra', 'deps', 'release']],
  },
};
# .husky/commit-msg
npx --no -- commitlint --edit "$1"
# Verification: the same rule rejects the same message from either path
echo "feat(unknown): thing" | npx commitlint || echo "scope rejected, as configured"

The scope list is the setting that pays for itself. Free-text scopes drift into thirty variants within a year, and a changelog grouped by scope becomes useless — the failure mode described in how to enforce conventional commits with commitlint.

Step 3 — Make the prompt fast enough to prefer Jump to heading

A prompt that asks eight questions is slower than typing the message, and people notice. Trim it to the fields your convention actually requires.

// .czrc equivalent, expressed in package.json config
// Skip questions whose answers are almost always empty.
{
  "config": {
    "commitizen": {
      "path": "cz-conventional-changelog",
      "skipQuestions": ["body", "footer"],
      "maxHeaderWidth": 72,
      "defaultType": "feat"
    }
  }
}
# Verification: time both paths honestly before defending either
time npm run commit -- --dry-run 2>/dev/null || echo "measure with a real commit"
Seconds to compose a conforming messageA full eight-question prompt takes longer than an experienced author typing the message directly. Trimming to three questions makes the guided path competitive, which is the condition for anyone choosing it voluntarily.seconds from git add to committedtyping it (experienced)14 sthree-question prompt18 seight-question prompt47 sthe prompt has to be roughly as fast as typing, or it is used once and abandoned

Step 4 — Handle the cases where the prompt cannot run Jump to heading

Rebases, merges, amends and commit --fixup all produce messages without an interactive session. The hook must recognise them, and the team needs to know the prompt is optional in those moments.

# These all bypass the prompt by design and must still pass validation
git commit --amend --no-edit
git commit --fixup HEAD~2
git rebase --continue
# Verification: the hook exempts generated messages
printf 'fixup! feat(api): add refund endpoint\n' > /tmp/m
npx --no -- commitlint --edit /tmp/m && echo "fixup accepted"

Commitlint understands fixup! and squash! prefixes; a hand-rolled hook usually does not until someone hits it during review. The autosquash workflow itself is covered in using fixup commits and autosquash during review.

Step 5 — Document the choice, not the tool Jump to heading

Write down which path is recommended for whom, so the prompt reads as help rather than as policy.

# CONTRIBUTING.md, in three lines that do more than a paragraph:
#   New here?     npm run commit   — asks the questions, gets the format right.
#   Comfortable?  git commit       — the hook checks the result either way.
#   Mid-rebase?   git commit       — the prompt is not available, and that is fine.
Which path to recommend to whomA newcomer benefits from the questions. An experienced author is faster in the editor. During a rebase or an amend there is no interactive session at all, so the editor path is the only one available.Who is committing, and in what situation?new to the conventionGuided promptformat by constructionknows it wellEditor and templatefaster in practicemid-rebase or amendEditor onlyno session to prompt inall three end at the same hook, which is why offering a choice costs nothing

Validation checklist Jump to heading

Frequently Asked Questions Jump to heading

Should we force everyone through the prompt? Jump to heading

No, and attempting it is how teams end up with a repository-wide --no-verify habit. Force the rule, offer the tool: the hook guarantees the outcome, and the prompt exists to make the outcome easy for whoever wants it.

Does the prompt work for contributors from forks? Jump to heading

It does if they install the dependencies, which many drive-by contributors will not. That is another reason the pipeline check matters more than the local tooling — see linting commit messages for forked pull requests.

Is a Node dependency acceptable in a non-JavaScript repository? Jump to heading

Sometimes, but weigh it honestly: it brings a toolchain, a lockfile and an upgrade cadence to a repository that may not otherwise need one. For polyglot projects, the pre-commit framework offers equivalent hooks without the runtime.