CHANGELOG とは何か
CHANGELOG(チェンジログ)とは、ソフトウェアのバージョン間でどのような変更が加えられたかを時系列に記録したドキュメントだ。 ユーザー・開発者・運用担当者のいずれにとっても「このバージョンで何が変わったか」を把握する最も信頼性の高い情報源となる。
CHANGELOG を整備しない開発チームが陥る典型的な問題は次の3つだ。 まず、バージョン間の差分が git log のコミットメッセージからしか追えなくなり、読者にとって意味を解釈する負荷が高くなる。 次に、セキュリティパッチがどのバージョンで適用されたかが不明になり、影響範囲の特定が困難になる。 最後に、非互換変更(ブレイキングチェンジ)が予告なく入ることで、利用者が予期しないシステム障害を経験する。
これらの問題を回避するために、リリース時に CHANGELOG を定義・更新することはチームの義務と言える。 本記事では業界標準である Keep a Changelog 形式を基準として解説する。
バージョン体系 — SemVer の定義
CHANGELOG のバージョン番号には Semantic Versioning(SemVer) を使用する。
SemVer は MAJOR.MINOR.PATCH の3フィールドで構成される。
| フィールド | 変更タイミング | 例 |
|---|---|---|
MAJOR |
後方互換性のない API 変更(ブレイキングチェンジ)が入ったとき | 1.0.0 → 2.0.0 |
MINOR |
後方互換性を保ちながら機能追加したとき | 1.3.0 → 1.4.0 |
PATCH |
後方互換性を保ちながらバグ修正したとき | 1.4.0 → 1.4.1 |
💡 プレリリース識別子
アルファ版・ベータ版など本番前の段階は 1.4.0-alpha.1 や 1.4.0-rc.2 のように - に続けて識別子を付ける。CHANGELOG ではプレリリースのエントリを [Unreleased] セクションに蓄積し、正式リリース時にバージョン番号に昇格させる運用が一般的だ。
リリース日付には ISO 8601 形式(YYYY-MM-DD)を使う。
これにより国際チームでもタイムゾーンによる混乱が生じない。
バージョンヘッダーは ## [1.4.0] - 2026-06-27 のように記述する。
CHANGELOG の構造と6変更カテゴリ
Keep a Changelog 形式では、各バージョンブロックの中に6つのセクションを定義する。 すべてのセクションが必須というわけではなく、変更がある種別のセクションだけを記載する。
| セクション | 記載内容 | 優先度 |
|---|---|---|
### Added |
新機能の追加 | 高 |
### Changed |
既存機能の仕様・動作変更 | 高 |
### Deprecated |
将来のバージョンで削除予定の機能の告知 | 中 |
### Removed |
以前 Deprecated とした機能の削除(ブレイキングチェンジ) | 高 |
### Fixed |
バグ修正 | 高 |
### Security |
脆弱性対応・依存ライブラリのセキュリティアップデート | 最高 |
⚠️ Security セクションは最優先で記載する
CVE(共通脆弱性識別子)が関連する場合は必ず番号も記載する。例:- JWT の有効期限バグを修正(CVE-2026-12345)。セキュリティパッチを含むリリースはパッチバージョンであっても速やかに CHANGELOG へ反映し、ユーザーにアップデートを促す必要がある。
CHANGELOG 構造フロー図
CHANGELOG の構成と Python 自動検証フローを以下の図にまとめた。
[Unreleased] セクションに変更を蓄積し、リリース時にバージョンヘッダーへ昇格させる流れが基本となる。
図1:CHANGELOG の構成とバージョンブロック・Python 検証フロー
Keep a Changelog 形式の全体像
実際の CHANGELOG の書き方を示す。ファイルはリポジトリルートに CHANGELOG.md として配置する。
# Changelog
すべての注目すべき変更はこのファイルに記録されます。
このフォーマットは [Keep a Changelog](https://keepachangelog.com/ja/1.1.0/) に基づいており、
このプロジェクトは [Semantic Versioning](https://semver.org/lang/ja/) に準拠しています。
## [Unreleased]
### Added
- ユーザーダッシュボードにエクスポート機能を追加(CSV / Excel)
---
## [1.4.0] - 2026-06-27
### Added
- ユーザーダッシュボードにエクスポート機能を追加
### Changed
- 検索 API のレスポンス形式を JSON:API 準拠に変更
### Deprecated
- `/v1/search` エンドポイントは v2.0 で削除予定
### Fixed
- Safari でログインが失敗するバグを修正 (#412)
### Security
- JWT の有効期限を 24h → 1h に短縮
---
## [1.3.2] - 2026-05-15
### Fixed
- ページネーションが最終ページで無限ループする問題を修正 (#398)
### Security
- log4j を 2.17.2 へ更新(CVE-2021-44228 対応)
---
## [1.3.1] - 2026-04-03
### Fixed
- タイムゾーンオフセットが考慮されない日付表示のバグを修正
✅ 書き方の3原則
①エントリは「ユーザー視点」で書く(技術的実装の説明ではなく、何が変わったかを伝える)。②PR 番号やイシュー番号をリンク形式で付けると追跡が容易になる。③ブレイキングチェンジは BREAKING CHANGE: プレフィックスをエントリ先頭に追加すると検索しやすい。
Python によるフォーマット検証スクリプト
CHANGELOG の記述ルールを人間のレビューだけに頼ると、フォーマット違反や不正なセクション名が紛れ込む。 次のスクリプトを CI に組み込むことで、Keep a Changelog 形式への準拠を自動検証できる。
"""
CHANGELOG フォーマット検証スクリプト
Keep a Changelog 形式への準拠を自動チェックする。
"""
import re
import sys
from pathlib import Path
from dataclasses import dataclass, field
VALID_SECTIONS = {"Added", "Changed", "Deprecated", "Removed", "Fixed", "Security"}
VERSION_PATTERN = re.compile(r"^## \[(\d+\.\d+\.\d+[^\]]*)\] - (\d{4}-\d{2}-\d{2})$")
UNRELEASED_PATTERN = re.compile(r"^## \[Unreleased\]$")
SECTION_PATTERN = re.compile(r"^### (.+)$")
@dataclass
class ValidationResult:
errors: list[str] = field(default_factory=list)
warnings: list[str] = field(default_factory=list)
@property
def is_valid(self) -> bool:
return len(self.errors) == 0
def report(self) -> None:
if self.errors:
print("❌ エラー:")
for e in self.errors:
print(f" - {e}")
if self.warnings:
print("⚠️ 警告:")
for w in self.warnings:
print(f" - {w}")
if self.is_valid:
print("✅ CHANGELOG の検証に成功しました")
def validate_changelog(path: str = "CHANGELOG.md") -> ValidationResult:
result = ValidationResult()
content = Path(path).read_text(encoding="utf-8")
lines = content.splitlines()
versions_found: list[str] = []
current_sections: list[str] = []
in_version_block = False
for i, line in enumerate(lines, start=1):
if line.startswith("## "):
if in_version_block and not current_sections:
result.warnings.append(
f"バージョンブロックにセクションがありません(行 {i} 付近)"
)
current_sections = []
in_version_block = False
if UNRELEASED_PATTERN.match(line):
in_version_block = True
continue
m = VERSION_PATTERN.match(line)
if not m:
result.errors.append(
f"行 {i}: バージョンヘッダーの形式が不正です: `{line}`\n"
f" 期待形式: `## [X.Y.Z] - YYYY-MM-DD`"
)
continue
version = m.group(1)
if version in versions_found:
result.errors.append(f"バージョン {version} が重複しています")
versions_found.append(version)
in_version_block = True
elif line.startswith("### ") and in_version_block:
m = SECTION_PATTERN.match(line)
if m:
section = m.group(1)
if section not in VALID_SECTIONS:
result.errors.append(
f"行 {i}: 不正なセクション名 `{section}`\n"
f" 有効: {', '.join(sorted(VALID_SECTIONS))}"
)
current_sections.append(section)
if not versions_found:
result.errors.append("バージョンエントリが見つかりません")
return result
if __name__ == "__main__":
path = sys.argv[1] if len(sys.argv) > 1 else "CHANGELOG.md"
if not Path(path).exists():
print(f"❌ ファイルが見つかりません: {path}")
sys.exit(1)
result = validate_changelog(path)
result.report()
sys.exit(0 if result.is_valid else 1)
スクリプトは次の項目を検証する。
| 検証項目 | チェック内容 | エラー種別 |
|---|---|---|
| バージョンヘッダー形式 | ## [X.Y.Z] - YYYY-MM-DD に一致するか |
Error |
| バージョン重複 | 同じバージョン番号が複数存在しないか | Error |
| セクション名 | 6種類の定義済みセクション名以外が使われていないか | Error |
| 空バージョンブロック | バージョンブロック内にセクションが1つもない | Warning |
| バージョン不在 | リリースバージョンが1件も存在しない | Error |
CI/CD への組み込み
GitHub Actions を使って release/** ブランチへのプッシュ時に自動検証を実行する例を示す。
name: CHANGELOG Format Check
on:
push:
branches:
- "release/**"
pull_request:
paths:
- "CHANGELOG.md"
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Run CHANGELOG validator
run: python tools/validate_changelog.py CHANGELOG.md
💡 Pre-commit フックとの組み合わせ
.pre-commit-config.yaml に検証スクリプトを登録しておくと、コミット前にローカルでも自動チェックが走る。CI に到達する前に問題を発見できるため、フィードバックループが短くなる。
PR テンプレートで更新を強制する
CHANGELOG の更新漏れを防ぐ最もシンプルな方法は、Pull Request テンプレートにチェックボックスとして組み込むことだ。 開発者が PR を作成するたびに CHANGELOG 更新を意識させることができる。
## 変更内容
## チェックリスト
- [ ] CHANGELOG.md の `[Unreleased]` セクションに変更内容を追記した
- [ ] 破壊的変更がある場合は `BREAKING CHANGE:` を先頭に付けた
- [ ] テストを追加・更新した
- [ ] API の変更がある場合、ドキュメントを更新した
まとめ
CHANGELOG はリリース時に必ず定義・更新すべきドキュメントだ。以下の4点を押さえれば Keep a Changelog 準拠の運用が回り始める。
- バージョン体系:SemVer(MAJOR.MINOR.PATCH)と ISO 8601 日付を使う
- 6変更カテゴリ:Added / Changed / Deprecated / Removed / Fixed / Security。Security は最優先で記載する
- Unreleased 運用:開発中の変更は
[Unreleased]に蓄積し、リリース時にバージョンブロックへ昇格させる - 自動検証:Python スクリプトを CI に組み込み、フォーマット違反を自動検出する
✅ CHANGELOG を起点にリリースノートを自動生成する
CHANGELOG の各バージョンブロックを Python で解析することで、ユーザー向けリリースノートを自動生成できる。次の記事「リリースノートで定義すべきこと」では、その具体的な実装と --audience フィルタを解説する。