A year ago I published the first edition of this guide. Since then the tooling has moved forward: AI coding agents learned to write Conventional Commits and set up release automation through agent skills, Simple Release got v3, and both Conventional Changelog and Simple Release got their own documentation websites. So here is the refreshed edition.
Conventional Commits is a specification for adding human and machine readable meaning to commit messages. It provides an easy set of rules for creating an explicit commit history, which makes it easier to write automated tools on top of.
The commit message should be structured as follows:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
Examples:
feat: add user authentication
fix: resolve login redirect issue
feat!: migrate to new API endpoint
feat: add new payment method
BREAKING CHANGE: migrate to new payment gateway
feat(auth): add password reset functionality
fix: prevent racing of requests
Introduce a request id and a reference to latest request. Dismiss
incoming responses other than from latest request.
Closes #123
Conventional commits enable automatic semantic versioning:
Tools can automatically generate changelogs by parsing commit messages, grouping them by type, and extracting relevant information.
Conventional commits create a more readable and searchable project history, making it easier to understand what changes were made and why.
To achieve complete automation of versioning and releases, you'll need several tools working together. Let's set them up step by step.
While not mandatory, Commitlint helps enforce conventional commit standards by linting commit messages.
First, install Commitlint and the Conventional Commits plugin:
npm install --save-dev @commitlint/cli @commitlint/config-conventional
Then you need to set up a configuration.
For a basic project, create .commitlintrc.json
:
{
"extends": ["@commitlint/config-conventional"]
}
Now you can set up commit validation during git commit
, for example using simple-git-hooks, though there are other similar tools available (husky, pre-commit, etc.).
Install simple-git-hooks:
npm install --save-dev simple-git-hooks
Create .simple-git-hooks.json
:
{
"commit-msg": "npx commitlint --edit \"$1\""
}
Now run npx simple-git-hooks
to activate the hooks and you're ready to go!
You can also set up commit linting via GitHub Actions when commits are pushed to the repository.
Create .github/workflows/commit.yml
:
name: Commit Validation
on:
pull_request:
types: [opened, synchronize]
jobs:
commitlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Validate commit messages
run: npx commitlint --from=HEAD~1
In monorepos, it's common practice to use scopes in Conventional Commits that correspond to workspace names in the monorepo. This helps identify which workspace(s) are affected by each commit. For example:
feat(store): add user authentication state
fix(ui): resolve button styling issue
docs(router): update routing configuration guide
You can use specialized packages to automatically get project scopes:
Let's look at an example for pnpm workspaces:
Install the packages:
pnpm add -D @commitlint/cli @commitlint/config-conventional @commitlint/config-pnpm-scopes
Create .commitlintrc.js
:
import scopes from '@commitlint/config-pnpm-scopes'
export default {
extends: ['@commitlint/config-conventional', '@commitlint/config-pnpm-scopes'],
rules: {
'scope-enum': async (ctx) => {
const scopeEnum = await scopes.rules['scope-enum'](ctx)
return [
scopeEnum[0],
scopeEnum[1],
[
...scopeEnum[2],
'deps', // for Dependabot or Renovate - dependency updates
'dev-deps', // for Dependabot or Renovate - dev dependency updates
'release' // for release commits
]
]
}
},
prompt: {
settings: {
enableMultipleScopes: true
}
}
}
Commitizen is an optional but very convenient tool that provides an interactive CLI for creating Conventional Commits.
First, install Commitizen and the Commitlint adapter:
npm install --save-dev commitizen @commitlint/cz-commitlint
Then create .czrc
:
{
"path": "@commitlint/cz-commitlint"
}
Add a script to package.json
:
{
"scripts": {
"commit": "cz"
}
}
Now you can use npm run commit
instead of git commit
for interactive commit creation.
If your team writes commits with an AI coding agent, the conventional-changelog project ships a universal agent skill, conventional-commit-message
, that teaches the agent to produce correct Conventional Commit messages - choosing the type and scope by release impact, flagging breaking changes, and validating against your Commitlint config when it is available.
It works with Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, and other agents that support the skills format. Install it with either package runner:
pnpx skills add conventional-changelog/conventional-changelog --skill conventional-commit-message
npx skills add conventional-changelog/conventional-changelog --skill conventional-commit-message
From then on, asking the agent for a commit message yields a changelog-ready message that follows the same convention Commitlint enforces. See Agent Skills for the full details.
The simple-release-action is a comprehensive GitHub Action that automates version updates, changelog generation, and releases. The project is based on simple-release and conventional-changelog.
The setup described below can also be done for you by an AI coding agent: the setup-simple-release-action skill teaches the agent to detect your project type and toolchain, generate the config and workflow, and finish with a checklist of the repository settings only you can change.
Key Features:
Currently npm, pnpm, and GitHub Actions projects are supported.
Let's look at an example of configuring the action for a project with pnpm workspaces using the @simple-release/pnpm addon:
Create .simple-release.json
config file in repository root:
{
"project": ["@simple-release/pnpm#PnpmWorkspacesProject", {
"mode": "fixed"
}],
"bump": {
"extraScopes": ["deps"]
}
}
extraScopes
is used to trigger version bump by commits like fix(deps): ...
from Dependabot or Renovate.
Create .github/workflows/release.yml
:
name: Release
on:
issue_comment:
types: [created, deleted]
push:
branches:
- main
jobs:
check:
runs-on: ubuntu-latest
name: Context check
outputs:
continue: ${{ steps.check.outputs.continue }}
workflow: ${{ steps.check.outputs.workflow }}
steps:
- name: Checkout the repository
uses: actions/checkout@v7
- name: Context check
id: check
uses: TrigenSoftware/simple-release-action@v2
with:
workflow: check
github-token: ${{ secrets.GITHUB_TOKEN }}
pull-request:
runs-on: ubuntu-latest
name: Pull request
needs: check
if: needs.check.outputs.workflow == 'pull-request'
steps:
- name: Checkout the repository
uses: actions/checkout@v7
- name: Create or update pull request
uses: TrigenSoftware/simple-release-action@v2
with:
workflow: pull-request
github-token: ${{ secrets.GITHUB_TOKEN }}
release:
runs-on: ubuntu-latest
name: Release
needs: check
if: needs.check.outputs.workflow == 'release'
steps:
- name: Checkout the repository
uses: actions/checkout@v7
- name: Install pnpm
uses: pnpm/action-setup@v6
with:
version: 10
- name: Install Node.js
uses: actions/setup-node@v6
with:
node-version: 24
cache: 'pnpm'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: pnpm install
- name: Release
uses: TrigenSoftware/simple-release-action@v2
with:
workflow: release
github-token: ${{ secrets.GITHUB_TOKEN }}
npm-token: ${{ secrets.NPM_TOKEN }}
This workflow consists of three jobs:
Every time you push to the main branch, the action will create or update a pull request with a version bump and updated changelog if necessary.
When the pull request is merged, it will automatically release the project.
If you are not using GitHub, you can write your own custom CI script using the @simple-release/core package.
There is also a recommendation to use squash merge for pull requests to keep the commit history in the main branch clean and well-structured. This ensures that each merge commit follows conventional commit standards and makes the project history more readable.
By implementing Conventional Commits with this toolchain, you'll have a fully automated release pipeline that maintains consistency, improves project history readability, and reduces manual overhead in version management.
For more details, guides and API references, check out the documentation websites: conventional-changelog.js.org and simple-release.js.org.
If you want to see real projects using Conventional Commits and their configurations, here are a few examples: