CHANGELOG とリリースノートの違い

CHANGELOG とリリースノートはどちらも「何が変わったか」を記録するドキュメントだが、目的と読者が根本的に異なる。 CHANGELOG は技術者が詳細な変更差分を追跡するための内部ドキュメントであり、6つの変更カテゴリをすべて記録する。 一方、リリースノートは製品の変更をステークホルダーや利用者に伝えるためのコミュニケーション文書だ。

観点CHANGELOGリリースノート
主な読者開発者・技術者エンドユーザー・運用担当者・管理職
形式Markdown(機械処理可能)Markdown / HTML / PDF など読者に合わせた形式
記載範囲すべての変更(技術的詳細含む)利用者に関係する変更のみ(内部リファクタは除外)
トーン事実記述・簡潔価値訴求・分かりやすい言葉で
更新タイミング変更のたびに随時更新リリース直前に確定・公開

読者によって何が変わるか

リリースノートの最大の特徴は読者フィルタリングだ。 同じリリースでも、エンドユーザーと開発者では関心の異なる変更がある。 フィルタを適切に設定することで、各読者に必要な情報だけを届けられる。

👤 エンドユーザー向け

CHANGELOG の AddedChanged セクションから、機能改善とUI変更を中心に抜粋。技術的詳細は除き、「何ができるようになったか」を平易な言葉で伝える。

含める変更カテゴリ: Added / Changed(ユーザーに影響する変更のみ)

除外する変更カテゴリ: Fixed(内部バグ)/ Security(技術詳細)/ Deprecated(開発者向け)/ Removed

🔧 開発者・運用担当者向け

CHANGELOG の全セクションを含む。ブレイキングチェンジ・非推奨 API の告知・セキュリティパッチの内容を強調する。CVE 番号・マイグレーションガイドへのリンクも含む。

含める変更カテゴリ: Added / Changed / Deprecated / Removed / Fixed / Security(全セクション)

追加情報: ブレイキングチェンジハイライト / マイグレーション手順

リリースノートの構成要素

リリースノートに含める構成要素とその定義を以下に示す。

要素内容必須/任意
バージョン番号とリリース日v1.4.0 — 2026-06-27 形式のヘッダー必須
ハイライト(概要)このリリースで最も重要な変更を2〜3文で要約する必須
新機能(What's New)ユーザーが利用できるようになった機能の説明必須(あれば)
改善事項既存機能の使い勝手・パフォーマンス改善推奨
バグ修正ユーザーに影響していた問題の解決(技術詳細は省略)推奨
セキュリティ修正脆弱性対応。CVE 番号を含める必須(あれば)
非推奨・削除廃止予定 / 削除された機能と移行先の案内必須(あれば)
アップグレード手順ブレイキングチェンジを含む場合の移行手順必須(ブレイキングチェンジ時)
既知の問題把握している未解決の問題と回避策任意

⚠️ 「内部リファクタ」はユーザー向けリリースノートに記載しない

コードの整理・テストの追加・CI 環境の変更など、利用者に影響しない変更はユーザー向けリリースノートから除外する。ただし開発者向けのリリースノートには含めることがある。フィルタリングの基準を Python スクリプトで自動化することが望ましい。

自動生成フロー図

CHANGELOG.md を単一の真実のソースとして、読者別リリースノートを Python で自動生成するフローを示す。 --audience フラグで出力内容を切り替える設計だ。

CHANGELOG→リリースノート変換フロー図

図1:CHANGELOG を起点としたリリースノート自動生成フローと読者別フィルタリング

リリースノートの記述例

ユーザー向けリリースノートと開発者向けリリースノートの記述例を並べて示す。

Markdown — ユーザー向けリリースノート(v1.4.0)
# v1.4.0 リリースノート(2026-06-27)

## 🎉 ハイライト

このリリースではダッシュボードへのデータエクスポート機能と
検索パフォーマンスの大幅な改善を提供します。

## ✨ 新機能

- **データエクスポート機能**:ダッシュボードのデータを CSV / Excel 形式でダウンロードできるようになりました。

## 📈 改善

- 検索結果の表示が最大 3 倍速くなりました。
- Safari でのログイン画面の表示崩れを修正しました。

## 🔒 セキュリティ

- セッションの有効期限を短縮しました(セキュリティ強化)。
Markdown — 開発者向けリリースノート(v1.4.0)
# v1.4.0 Developer Release Notes(2026-06-27)

## ⚠️ ブレイキングチェンジ

- 検索 API のレスポンスが JSON:API 準拠に変更。`data.results` → `data.items`

## Added
- ダッシュボードに CSV / Excel エクスポートエンドポイント追加(`GET /api/v1/export`)

## Changed
- 検索 API レスポンス形式を JSON:API 準拠に変更(後方互換性なし)

## Deprecated
- `/v1/search` エンドポイントは v2.0.0 で削除予定。`/v2/search` へ移行してください。

## Fixed
- Safari ブラウザでのセッションクッキー処理のバグを修正(#412)

## Security
- JWT 有効期限を 24h → 1h に短縮(セキュリティポリシー変更)
- 影響なし(後方互換あり)

## マイグレーション手順

```diff
- const res = await fetch('/api/v1/search');
- const items = res.data.results;
+ const res = await fetch('/api/v2/search');
+ const items = res.data.items;
```

Python 自動生成スクリプト

CHANGELOG.md から読者別リリースノートを自動生成するスクリプトだ。 --version で対象バージョンを、--audience で読者種別を指定する。

Python — generate_release_notes.py
"""
CHANGELOG.md からリリースノートを自動生成するスクリプト。
--audience dev  : 全セクション出力(開発者向け)
--audience user : Added / Changed のみ出力(エンドユーザー向け)
"""

import argparse
import re
import sys
from pathlib import Path
from dataclasses import dataclass, field

# ユーザー向けに含めるセクション
USER_SECTIONS = {"Added", "Changed"}
ALL_SECTIONS = {"Added", "Changed", "Deprecated", "Removed", "Fixed", "Security"}

VERSION_PATTERN = re.compile(r"^## \[(\d+\.\d+\.\d+[^\]]*)\] - (\d{4}-\d{2}-\d{2})$")
SECTION_PATTERN = re.compile(r"^### (.+)$")

SECTION_EMOJI = {
    "Added": "✨",
    "Changed": "🔄",
    "Deprecated": "⚠️",
    "Removed": "🗑️",
    "Fixed": "🐛",
    "Security": "🔒",
}


@dataclass
class VersionBlock:
    version: str
    date: str
    sections: dict = field(default_factory=dict)  # section_name -> list[str]


def parse_changelog(path: str) -> list[VersionBlock]:
    """CHANGELOG.md をパースしてバージョンブロックのリストを返す"""
    blocks: list[VersionBlock] = []
    current_block: VersionBlock | None = None
    current_section: str | None = None

    for line in Path(path).read_text(encoding="utf-8").splitlines():
        m = VERSION_PATTERN.match(line)
        if m:
            if current_block:
                blocks.append(current_block)
            current_block = VersionBlock(version=m.group(1), date=m.group(2))
            current_section = None
            continue

        if line.startswith("## [Unreleased]"):
            current_block = None
            current_section = None
            continue

        if current_block is None:
            continue

        ms = SECTION_PATTERN.match(line)
        if ms:
            current_section = ms.group(1)
            if current_section not in current_block.sections:
                current_block.sections[current_section] = []
            continue

        if current_section and line.startswith("- "):
            current_block.sections[current_section].append(line)

    if current_block:
        blocks.append(current_block)

    return blocks


def generate_notes(block: VersionBlock, audience: str) -> str:
    """バージョンブロックからリリースノート文字列を生成する"""
    target_sections = USER_SECTIONS if audience == "user" else ALL_SECTIONS
    label = "ユーザー向け" if audience == "user" else "開発者向け"

    lines = [
        f"# v{block.version} リリースノート ({label})",
        f"**リリース日:** {block.date}",
        "",
    ]

    for section in ["Added", "Changed", "Deprecated", "Removed", "Fixed", "Security"]:
        if section not in target_sections:
            continue
        entries = block.sections.get(section, [])
        if not entries:
            continue
        emoji = SECTION_EMOJI.get(section, "")
        lines.append(f"## {emoji} {section}")
        lines.extend(entries)
        lines.append("")

    if not any(
        block.sections.get(s) for s in target_sections
    ):
        lines.append("_このバージョンには該当する変更がありません。_")

    return "\n".join(lines)


def main() -> int:
    parser = argparse.ArgumentParser(
        description="CHANGELOG.md からリリースノートを生成する"
    )
    parser.add_argument("changelog", nargs="?", default="CHANGELOG.md")
    parser.add_argument("--version", default=None, help="対象バージョン(省略時は最新)")
    parser.add_argument(
        "--audience",
        choices=["dev", "user"],
        default="dev",
        help="読者種別(dev: 開発者向け / user: エンドユーザー向け)",
    )
    parser.add_argument("--output", default=None, help="出力ファイルパス(省略時は標準出力)")
    args = parser.parse_args()

    if not Path(args.changelog).exists():
        print(f"❌ ファイルが見つかりません: {args.changelog}")
        return 1

    blocks = parse_changelog(args.changelog)
    if not blocks:
        print("❌ CHANGELOG にバージョンブロックが見つかりません")
        return 1

    target = blocks[0]
    if args.version:
        matches = [b for b in blocks if b.version == args.version]
        if not matches:
            print(f"❌ バージョン {args.version} が見つかりません")
            return 1
        target = matches[0]

    notes = generate_notes(target, args.audience)

    if args.output:
        Path(args.output).write_text(notes, encoding="utf-8")
        print(f"✅ リリースノートを生成しました: {args.output}")
    else:
        print(notes)

    return 0


if __name__ == "__main__":
    sys.exit(main())

実行例を示す。

Shell — 実行例
# 最新バージョンの開発者向けリリースノートを生成
python generate_release_notes.py CHANGELOG.md --audience dev

# v1.4.0 のユーザー向けリリースノートをファイル出力
python generate_release_notes.py CHANGELOG.md \
  --version 1.4.0 \
  --audience user \
  --output release-notes-1.4.0-user.md

CI/CD への組み込みと公開先

リリースブランチ作成時に GitHub Actions でリリースノートを自動生成し、 GitHub Release・Confluence・Slack へ自動配信する設計例を示す。

YAML — .github/workflows/release-notes.yml
name: Generate Release Notes

on:
  push:
    tags:
      - "v*"

jobs:
  release-notes:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Generate dev release notes
        run: |
          python tools/generate_release_notes.py CHANGELOG.md \
            --audience dev \
            --output dist/release-notes-dev.md

      - name: Generate user release notes
        run: |
          python tools/generate_release_notes.py CHANGELOG.md \
            --audience user \
            --output dist/release-notes-user.md

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          body_path: dist/release-notes-dev.md
          files: dist/release-notes-*.md
公開先推奨読者形式
GitHub Releases開発者Markdown(dev 向け)
Confluence / Notion運用担当者・管理職Markdown or HTML(dev 向け)
製品サイト / ヘルプページエンドユーザーHTML(user 向け)
Slack #release チャンネル社内全体ハイライトのみ要約

まとめ

リリースノートは CHANGELOG の技術的記録を「読者ごとに最適化されたコミュニケーション文書」に変換したものだ。 以下の3点を押さえて運用を設計する。

  • 単一ソース:CHANGELOG.md を唯一の真実のソースとして保守し、リリースノートは派生物として自動生成する
  • 読者フィルタ:ユーザー向け(Added/Changed のみ)と開発者向け(全セクション)でフィルタリングを分ける
  • CI 自動生成:タグプッシュをトリガーに GitHub Actions でリリースノートを自動生成・公開する

次のステップ

リリースノートの品質を担保するには、その前段としてリリース判定チェックリストで「リリースノート作成」が完了していることを確認する仕組みが必要だ。次の記事「リリース判定チェックリストで定義すべきこと」では、Python 検証スクリプトによる GO/NO-GO 判定の自動化を解説する。