Last updated: March 21, 2026

Choose conventional-changelog for projects already using Conventional Commits (enforced via commitlint), with automatic semantic version bumping and multi-repository support. Choose Semantic Release for fully automated changelog generation tied to release pipelines and Git tags. Choose Cliff for Rust/system software projects with custom templates and Git metadata extraction. Choose Claude + automation for legacy codebases needing intelligent commit interpretation and manual handoff workflows. All handle semantic versioning correctly but differ in CI/CD integration depth and handling of irregular commit histories.

Key Takeaways

The Changelog Problem at Scale

Manual changelogs accumulate technical debt. Developers forget to update them, merge conflicts arise, and changelog contents diverge from actual code changes. Automated solutions parse git history, detect breaking changes from commit messages, group related changes, and generate structured documentation.

The challenge intensifies with monorepos where different packages release independently, or when commit histories are irregular (old projects without Conventional Commits). AI tools solve this by either enforcing strict commit conventions or interpreting loose, inconsistent commit messages intelligently.

Conventional Changelog: The Gold Standard

Conventional Changelog (used by Angular, React, and thousands of projects) generates changelogs from Conventional Commits—a structured commit message format specifying type (feat, fix, docs) and scope.

Installation and Setup

# Install globally or per-project
npm install --save-dev conventional-changelog-cli

# Generate initial changelog
npx conventional-changelog -p angular -i CHANGELOG.md -s

# Configure in package.json for automation
npm install --save-dev commitlint @commitlint/config-conventional

Conventional Commit Format

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

# Example:
feat(auth): implement OAuth2 token refresh

Previously, tokens expired without automatic refresh. Users were forced to
re-authenticate when tokens became invalid.

This change implements automatic token refresh using a background timer
that triggers 30 seconds before expiration.

Closes #456
BREAKING CHANGE: The login endpoint now returns access_token instead of token

Types follow a standard: feat (features), fix (bug fixes), docs (documentation), style (formatting), refactor (code reorganization), perf (performance), test (test additions), chore (dependency updates).

Automatic Semantic Versioning

Conventional Changelog ties commit types to semantic versioning automatically:

# Configure automatic versioning with standard-version
npm install --save-dev standard-version

# In package.json:
{
  "scripts": {
    "release": "standard-version",
    "release:minor": "standard-version --release-as minor",
    "release:major": "standard-version --release-as major"
  }
}

# Running this generates changelog and bumps version
npm run release

CI/CD Integration Example

# .github/workflows/release.yml
name: Release

on:
  push:
    branches:
      - main

jobs:
  release:
    runs-on: ubuntu-latest
    if: "!contains(github.event.head_commit.message, 'skip ci')"
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0  # Fetch full history for changelog

      - uses: actions/setup-node@v3
        with:
          node-version: '18'

      - run: npm ci

      - name: Generate changelog and bump version
        run: |
          git config user.email "release@example.com"
          git config user.name "Release Bot"
          npx standard-version

      - name: Push changes
        run: git push --follow-tags origin main

      - name: Create GitHub Release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ github.ref }}
          body_path: CHANGELOG.md
          draft: false
          prerelease: false

Monorepo Support with Lerna

For monorepos with independent package versioning:

npm install --save-dev lerna conventional-changelog

# Configure lerna.json
{
  "packages": ["packages/*"],
  "version": "independent",
  "command": {
    "publish": {
      "conventionalCommits": true,
      "changelogPreset": "angular"
    }
  }
}

# Publish with automatic changelogs per package
lerna publish

Each package gets its own changelog tracking its specific changes.

Customizing Output Format

# Use different preset (angular, atom, ember, eslint, express, jquery, jshint, angular-essential)
npx conventional-changelog -p atom -i CHANGELOG.md -s

# Create custom preset
npm install --save-dev conventional-changelog-custom-preset

Custom preset example:

// conventional-changelog-custom-preset.js
module.exports = {
  parserOpts: {
    noteKeywords: ['BREAKING CHANGE', 'SECURITY'],
    referenceActions: [
      'close', 'closes', 'closed',
      'fix', 'fixes', 'fixed',
      'resolve', 'resolves', 'resolved'
    ]
  },
  writerOpts: {
    transform(commit) {
      if (commit.type === 'feat') {
        commit.type = '✨ Features';
      } else if (commit.type === 'fix') {
        commit.type = '🐛 Bug Fixes';
      }
      return commit;
    },
    commitGroupsSort: (a, b) => {
      const groupOrder = ['✨ Features', '🐛 Bug Fixes', 'Documentation'];
      return groupOrder.indexOf(a.title) - groupOrder.indexOf(b.title);
    }
  }
};

Semantic Release: Fully Automated Release Pipeline

Semantic Release goes beyond changelog generation—it fully automates releases, including version bumping, changelog creation, GitHub releases, and NPM publishing.

Installation and Configuration

npm install --save-dev semantic-release @semantic-release/github @semantic-release/npm @semantic-release/changelog @semantic-release/git

# Configure .releaserc.json
{
  "branches": ["main", {"name": "beta", "prerelease": true}],
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    [
      "@semantic-release/changelog",
      {
        "changelogFile": "CHANGELOG.md"
      }
    ],
    "@semantic-release/npm",
    [
      "@semantic-release/git",
      {
        "assets": ["package.json", "package-lock.json", "CHANGELOG.md"],
        "message": "chore(release): ${nextRelease.version} [skip ci]"
      }
    ],
    "@semantic-release/github"
  ]
}

Workflow Setup

# .github/workflows/release.yml
name: Release

on:
  push:
    branches: [main, beta]

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - uses: actions/setup-node@v3
        with:
          node-version: '18'

      - run: npm ci

      - run: npm test

      - name: Publish to npm and create GitHub release
        run: npx semantic-release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

On every commit to main, Semantic Release analyzes commits, determines version bump, generates changelog entries, publishes to npm, and creates a GitHub release—all automatically.

Handling Pre-releases and Hotfixes

{
  "branches": [
    "main",
    {"name": "next", "prerelease": true},
    {"name": "beta", "prerelease": true},
    {"name": "alpha", "prerelease": true},
    {"name": "hotfix/**", "range": "1.x", "prerelease": false}
  ]
}

Commits to alpha branch generate 1.0.0-alpha.1, beta generates 1.0.0-beta.1, while hotfix branches release patch versions for current stable release.

Cliff: System Software and Rust Projects

Cliff (Changelog Generator) excels with system software, Rust projects, and complex custom formats. It parses Git history, groups commits intelligently, and supports custom templates.

Installation and Setup

# Install via package managers
brew install git-cliff
cargo install git-cliff

# Initialize configuration
git cliff --init

# Generate changelog
git cliff -o CHANGELOG.md

Configuration for Rust Projects

# cliff.toml - Rust project configuration
[changelog]
header = """
# Changelog

All notable changes to this project will be documented in this file.\n
"""
body = """
{% if version %}\
## [{{ version }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
## [Unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | upper }}
{% for commit in commits %}
- {% if commit.breaking %}[**BREAKING**] {% endif %}{{ commit.message | upper_first }} ([`{{ commit.id | truncate(length=7, end="") }}`]({{ config.commit_parsers[0].link_base }}/commit/{{ commit.id }}))\
{% endfor %}
{% endfor %}\n
"""
trim = true

[git]
conventional_commits = true
filter_unconventional = true
split_commits = false
commit_parsers = [
  {message = "^feat", group = "Features"},
  {message = "^fix", group = "Bug Fixes"},
  {message = "^doc", group = "Documentation"},
  {message = "^perf", group = "Performance"},
  {message = "^refactor", group = "Refactoring"},
  {message = "^test", group = "Testing"},
  {message = "^chore", skip = true},
  {message = "^style", skip = true}
]
protect_breaking_commits = false
filter_commits = false
topo_order = false
limit_commits = 1000
skip_tags = ""
ignore_tags = ""
date_order = false
sort_commits = "oldest"

Custom Template with Multiple Sections

body = """
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group }}
{% for commit in commits %}
{%- if commit.scope %}
- **{{ commit.scope }}**: {{ commit.message }} ([{{ commit.id | truncate(length=7) }}]({{ commit_url }}{{ commit.id }}))
{%- else %}
- {{ commit.message }} ([{{ commit.id | truncate(length=7) }}]({{ commit_url }}{{ commit.id }}))
{%- endif %}
{%- if commit.breaking %}

**BREAKING CHANGE:** {{ commit.breaking_description }}
{%- endif %}
{% endfor %}
{% endfor %}
"""

CI/CD Integration with Git Cliff

#!/bin/bash
# scripts/release.sh

set -e

# Get current version and next version
CURRENT_VERSION=$(grep version Cargo.toml | head -1 | grep -oP '\d+\.\d+\.\d+')
NEXT_VERSION=$(cargo semver bump patch)

# Generate changelog
git cliff --tag "$NEXT_VERSION" -o CHANGELOG.md

# Commit and tag
git add CHANGELOG.md
git commit -m "chore: release $NEXT_VERSION"
git tag "$NEXT_VERSION"

# Push
git push origin main --tags
# .github/workflows/release.yml for Rust
name: Release

on:
  workflow_dispatch:
    inputs:
      version:
        description: 'Version to release (e.g., 0.2.0)'
        required: true

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - uses: orhun/git-cliff-action@v3
        with:
          config: cliff.toml
          args: --verbose
        env:
          OUTPUT: CHANGELOG.md

      - name: Update version in Cargo.toml
        run: |
          sed -i "s/^version = .*/version = \"${{ github.event.inputs.version }}\"/" Cargo.toml
          git add CHANGELOG.md Cargo.toml
          git config user.email "release@bot.local"
          git config user.name "Release Bot"
          git commit -m "chore(release): ${{ github.event.inputs.version }}"
          git tag "${{ github.event.inputs.version }}"
          git push origin main --tags

Claude: Legacy Codebases and Manual Interpretation

For projects without Conventional Commits, Claude interprets commit messages and suggests structured changelog entries.

Workflow Example

Request to Claude: “Analyze git log from the last 30 commits and generate a changelog following this format:

## [Unreleased]

### Added
- New features here

### Changed
- Updated functionality

### Fixed
- Bug fixes

### Security
- Security patches
```"

Claude parses commits like:
- "Fixed login bug where users could bypass email verification"
- "Added support for OAuth2 providers"
- "Updated dependencies to latest versions"
- "Refactored database connection pooling"

And structures them into proper sections with clarity improvements:

```markdown
## [Unreleased]

### Added
- OAuth2 provider support for Google, GitHub, and Microsoft accounts
- Two-factor authentication (2FA) via authenticator apps

### Fixed
- Login bypass vulnerability where email verification could be skipped
- Database connection pool exhaustion under high concurrency

### Changed
- Database connection pooling strategy optimized for reduced latency
- Deprecated API endpoints removed (scheduled for version 2.0)

### Security
- Updated cryptographic libraries to fix padding oracle vulnerability in AES-GCM

Automated CLI Wrapper

#!/bin/bash
# scripts/generate-changelog-claude.sh

SINCE_TAG=${1:-"v0.0.0"}
UNTIL_TAG=${2:-"HEAD"}

# Extract commits
COMMITS=$(git log $SINCE_TAG..$UNTIL_TAG --pretty=format:"%h %s")

# Create prompt for Claude
PROMPT="Analyze these commits and generate a CHANGELOG.md entry:

$COMMITS

Output in this format:
## [Unreleased]

### Added
### Changed
### Fixed
### Security

Only include sections with content."

# Call Claude API (requires CLAUDE_API_KEY)
curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $CLAUDE_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d "{
    \"model\": \"claude-opus-4-6\",
    \"max_tokens\": 2000,
    \"messages\": [{
      \"role\": \"user\",
      \"content\": \"$PROMPT\"
    }]
  }" | jq -r '.content[0].text'

Manual Handoff Workflow

For critical releases, combine Claude’s interpretation with manual review:

#!/bin/bash
# scripts/prepare-release.sh

echo "=== Analyzing commits since last tag ==="
LAST_TAG=$(git describe --tags --abbrev=0)
COMMITS=$(git log $LAST_TAG..HEAD --oneline)

echo "$COMMITS"
echo ""
echo "=== Generated changelog entry ==="

# Have Claude suggest, save to temp file
./scripts/generate-changelog-claude.sh $LAST_TAG > CHANGELOG.md.draft

echo "Draft changelog saved to CHANGELOG.md.draft"
echo "Review and edit manually, then:"
echo "  mv CHANGELOG.md.draft CHANGELOG.md"
echo "  git add CHANGELOG.md"
echo "  git commit -m 'docs: update changelog'"

Comparison Matrix

Feature Conventional Semantic Cliff Claude
Automatic versioning Yes Yes No No
Changelog generation Yes Yes Yes Yes
Breaking change detection Yes Yes Partial Excellent
Monorepo support Yes (Lerna) Yes Limited No
CI/CD integration Excellent Excellent Good Moderate
Conventional Commits required Yes Yes Yes No
Custom templates Moderate Moderate Excellent N/A
Handles irregular commits No No No Yes
License MIT MIT GPL-2.0 Proprietary
Learning curve Low Low Moderate Low

Implementation Strategy

  1. New projects: Use Semantic Release with Conventional Commits enforced via commitlint.
  2. Rust/system software: Use Cliff with custom templates.
  3. Legacy projects: Use Claude for one-time migration, then enforce Conventional Commits going forward.
  4. Monorepos: Use Lerna + Conventional Changelog with independent versioning.

Enforce commit conventions early:

# Install commitlint
npm install --save-dev commitlint @commitlint/config-conventional husky

# Set up pre-commit hook
npx husky install
npx husky add .husky/commit-msg 'npx commitlint --edit "$1"'

This prevents non-conventional commits from being pushed, ensuring changelogs remain reliable.

Validation and Testing

Always test changelog generation before release:

# Dry run semantic release
SEMANTIC_RELEASE_DRY_RUN=true npx semantic-release

# Preview Cliff output
git cliff --latest

# Validate generated changelog format
grep "^##" CHANGELOG.md  # Should see version headers

Frequently Asked Questions

Who is this article written for?

This article is written for developers, technical professionals, and power users who want practical guidance. Whether you are evaluating options or implementing a solution, the information here focuses on real-world applicability rather than theoretical overviews.

How current is the information in this article?

We update articles regularly to reflect the latest changes. However, tools and platforms evolve quickly. Always verify specific feature availability and pricing directly on the official website before making purchasing decisions.

Are there free alternatives available?

Free alternatives exist for most tool categories, though they typically come with limitations on features, usage volume, or support. Open-source options can fill some gaps if you are willing to handle setup and maintenance yourself. Evaluate whether the time savings from a paid tool justify the cost for your situation.

Can I trust these tools with sensitive data?

Review each tool’s privacy policy, data handling practices, and security certifications before using it with sensitive data. Look for SOC 2 compliance, encryption in transit and at rest, and clear data retention policies. Enterprise tiers often include stronger privacy guarantees.

What is the learning curve like?

Most tools discussed here can be used productively within a few hours. Mastering advanced features takes 1-2 weeks of regular use. Focus on the 20% of features that cover 80% of your needs first, then explore advanced capabilities as specific needs arise.

Built by theluckystrike — More at zovo.one