Commitlint Standards: Enforce Commit Messages
Welcome to TopperBlog! 👋
I'm a tech content creator passionate about helping developers level up their careers and master cutting-edge technologies.
🎯 What I Write About:
• AI/ML Engineering & LLMs
• Web3 & Blockchain Development
• System Design & Architecture
• Interview Preparation (FAANG)
• Freelancing & Remote Work
• Modern Tech Stacks (Next.js, React, Rust, TypeScript)
• Performance Optimization & Best Practices
💼 Mission: Sharing practical, actionable insights that accelerate your tech career and maximize your earning potential.
📚 15+ In-Depth Guides covering everything from earning $10k/month as a freelancer to cracking FAANG interviews.
🌐 Let's connect and grow together in this amazing tech journey!
#TechBlogger #SoftwareEngineering #CareerGrowth #WebDevelopment #AIEngineering
Commitlint Standards: Enforce Commit Messages with Git Hooks
Table of Contents
- What is Commitlint?
- Why Enforce Commit Message Standards?
- Setup and Installation
- Configuration Examples
- Commit Message Format
- Comparison Table
- Frequently Asked Questions
What is Commitlint?
Commitlint is a powerful tool that checks if your commit messages meet the conventional commit format. It works seamlessly with Git hooks to enforce commit message standards across your development team, ensuring consistency, readability, and automated changelog generation.
The tool integrates with Husky to create Git hooks that validate commit messages before they're accepted into your repository. This prevents poorly formatted commits from entering your codebase and maintains a clean, professional Git history.
Key Benefits:
- Automated validation of commit messages
- Consistent format across team members
- Better changelog generation from commit history
- Improved code review process
- Semantic versioning support
Why Enforce Commit Message Standards?
Enforcing commit message standards isn't just about being pedantic—it provides tangible benefits:
- Automated Changelog Generation: Tools can parse structured commits to generate release notes automatically
- Better Navigation: Quickly filter commits by type (features, fixes, breaking changes)
- Clearer History: Understand what changed and why without reading code
- Semantic Versioning: Automatically determine version bumps based on commit types
- Team Collaboration: Consistent format reduces cognitive load when reviewing history
- CI/CD Integration: Trigger specific workflows based on commit types
Setup and Installation
Prerequisites
- Node.js (v14 or higher)
- npm or yarn
- Git repository initialized
Step-by-Step Installation
Step 1: Install Dependencies
npm install --save-dev @commitlint/{cli,config-conventional} husky
Step 2: Initialize Husky
npx husky-init && npm install
Step 3: Create Commitlint Configuration
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
Step 4: Add Commit Message Hook
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit ${1}'
Step 5: Test Your Setup
git commit -m "invalid commit message" # Should fail
git commit -m "feat: add new feature" # Should succeed
Configuration Examples
Example 1: Basic Configuration
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'feat', // New feature
'fix', // Bug fix
'docs', // Documentation
'style', // Formatting
'refactor', // Code restructuring
'test', // Adding tests
'chore', // Maintenance
],
],
'subject-case': [2, 'never', ['upper-case']],
'subject-max-length': [2, 'always', 100],
},
};
Example 2: Custom Rules Configuration
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', [
'feat', 'fix', 'docs', 'style', 'refactor',
'perf', 'test', 'build', 'ci', 'chore', 'revert'
]],
'scope-enum': [2, 'always', [
'api', 'ui', 'database', 'auth', 'core'
]],
'scope-empty': [2, 'never'],
'subject-empty': [2, 'never'],
'subject-full-stop': [2, 'never', '.'],
'header-max-length': [2, 'always', 72],
'body-leading-blank': [2, 'always'],
'footer-leading-blank': [2, 'always'],
},
};
Example 3: Monorepo Configuration
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional', '@commitlint/config-lerna-scopes'],
rules: {
'scope-enum': async (ctx) => {
const packages = [
'frontend',
'backend',
'shared',
'mobile',
'docs'
];
return [2, 'always', packages];
},
'type-enum': [2, 'always', [
'feat', 'fix', 'docs', 'style', 'refactor',
'test', 'chore', 'deps', 'release'
]],
},
};
Example 4: Advanced with Custom Plugins
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
plugins: ['commitlint-plugin-jira-rules'],
rules: {
'jira-task-id-max-length': [2, 'always', 9],
'jira-task-id-min-length': [2, 'always', 3],
'jira-task-id-separator': [2, 'always', '-'],
'type-enum': [2, 'always', [
'feat', 'fix', 'docs', 'style', 'refactor',
'perf', 'test', 'build', 'ci', 'chore'
]],
'references-empty': [2, 'never'],
'header-max-length': [2, 'always', 100],
},
parserPreset: {
parserOpts: {
issuePrefixes: ['JIRA-', 'PROJ-'],
},
},
};
Example 5: Package.json Integration
{
"name": "my-project",
"version": "1.0.0",
"scripts": {
"commit": "git-cz",
"commitlint": "commitlint --edit",
"prepare": "husky install"
},
"devDependencies": {
"@commitlint/cli": "^17.6.0",
"@commitlint/config-conventional": "^17.6.0",
"commitizen": "^4.3.0",
"cz-conventional-changelog": "^3.3.0",
"husky": "^8.0.3"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
},
"commitlint": {
"extends": ["@commitlint/config-conventional"],
"rules": {
"body-max-line-length": [1, "always", 100],
"footer-max-line-length": [1, "always", 100]
}
}
}
Commit Message Format
Conventional Commit Structure
<type>(<scope>): <subject>
<body>
<footer>
Type Values
- feat: A new feature for the user
- fix: A bug fix for the user
- docs: Documentation changes
- style: Code style changes (formatting, semicolons, etc.)
- refactor: Code changes that neither fix bugs nor add features
- perf: Performance improvements
- test: Adding or updating tests
- build: Changes to build system or dependencies
- ci: Changes to CI configuration files
- chore: Other changes that don't modify src or test files
- revert: Reverts a previous commit
Examples of Good Commit Messages
feat(auth): add OAuth2 authentication support
fix(api): resolve null pointer exception in user service
docs(readme): update installation instructions
style(components): format code according to prettier rules
refactor(database): optimize query performance
perf(images): implement lazy loading for gallery
test(auth): add unit tests for login functionality
build(deps): upgrade react to version 18.2.0
ci(github): add automated deployment workflow
chore(release): bump version to 2.0.0
Breaking Changes
feat(api)!: redesign REST API endpoints
BREAKING CHANGE: API endpoints now use /v2/ prefix
All clients must update their base URL configuration
Comparison Table
| Feature | Commitlint | Git Commit Template | Manual Review | Commitizen |
| Automated Enforcement | ✅ Yes | ❌ No | ❌ No | ✅ Yes |
| Pre-commit Validation | ✅ Yes | ❌ No | ❌ No | ⚠️ Optional |
| Custom Rules | ✅ Extensive | ⚠️ Limited | ✅ Yes | ⚠️ Limited |
| Team Consistency | ✅ High | ⚠️ Medium | ❌ Low | ✅ High |
| Learning Curve | ⚠️ Medium | ✅ Low | ✅ Low | ⚠️ Medium |
| CI/CD Integration | ✅ Easy | ❌ Difficult | ❌ Difficult | ✅ Easy |
| Changelog Generation | ✅ Automatic | ❌ Manual | ❌ Manual | ✅ Automatic |
| Setup Complexity | ⚠️ Medium | ✅ Simple | ✅ None | ⚠️ Medium |
| IDE Support | ✅ Good | ✅ Good | ✅ Native | ✅ Good |
| Cost | ✅ Free | ✅ Free | ✅ Free | ✅ Free |
| Maintenance | ⚠️ Low | ✅ None | ⚠️ High | ⚠️ Low |
| Error Messages | ✅ Detailed | ❌ None | ⚠️ Varies | ✅ Interactive |
Frequently Asked Questions
Q1: Can I bypass commitlint for emergency fixes?
A: Yes, you can use the --no-verify flag to skip Git hooks:
git commit -m "emergency fix" --no-verify
However, this should be used sparingly and only in genuine emergencies. Consider creating a proper commit message afterward with git commit --amend.
Q2: How do I handle commits from external contributors?
A: You have several options:
- CI/CD Validation: Add commitlint to your CI pipeline to check all commits
- Documentation: Provide clear contribution guidelines
- Squash Merging: Squash external PRs and write proper commit messages
- Bot Integration: Use GitHub bots to validate PR commit messages
Q3: What if my team disagrees on commit message format?
A: Commitlint is highly configurable. Hold a team meeting to:
- Discuss pain points with current practices
- Review conventional commit benefits
- Customize rules to fit your workflow
- Start with lenient rules and gradually tighten them
- Document decisions in your contributing guide
Q4: Can commitlint work with existing projects?
A: Absolutely! Commitlint only validates new commits. To implement:
- Install and configure commitlint
- Announce the change to your team
- Provide training and documentation
- Consider a grace period with warnings instead of errors
- Optionally rewrite history (use with caution)
Q5: How do I integrate commitlint with CI/CD?
A: Add this to your CI configuration:
GitHub Actions:
- name: Validate commits
run: npx commitlint --from=HEAD~1 --to=HEAD --verbose
GitLab CI:
commitlint:
script:
- npx commitlint --from=$CI_COMMIT_BEFORE_SHA --to=$CI_COMMIT_SHA
Q6: What's the difference between commitlint and commitizen?
A: They serve complementary purposes:
- Commitlint: Validates commit messages (enforcer)
- Commitizen: Helps write commit messages (assistant)
Use both together for the best experience: Commitizen guides users in writing proper commits, while commitlint ensures compliance.
Q7: Can I use commitlint with other Git hooks?
A: Yes! Husky allows multiple hooks. Example .husky/commit-msg:
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no -- commitlint --edit ${1}
# Add other validations here
Q8: How do I handle long commit messages?
A: Use the body section for detailed explanations:
git commit -m "feat(api): add user authentication" -m "
- Implement JWT token generation
- Add password hashing with bcrypt
- Create middleware for route protection
- Add refresh token mechanism
"
Or use an editor:
git commit # Opens default editor
Conclusion
Implementing commitlint with Git hooks transforms your development workflow by enforcing consistent, meaningful commit messages. While there's an initial setup investment, the long-term benefits—automated changelogs, better collaboration, and clearer project history—far outweigh the costs.
Start with the basic configuration, customize rules to fit your team's needs, and gradually adopt best practices. Your future self (and teammates) will thank you for maintaining a clean, professional Git history.
Next Steps:
- Install commitlint in your project today
- Customize rules for your team's workflow
- Document your commit message standards
- Train team members on the new process
- Monitor and refine your configuration over time
Happy committing! 🚀