Conventional Commits(コンベンショナル・コミット)は、Gitのコミットメッセージを type(scope): description という決まった形式で書くための規約です。メッセージの先頭に feat(機能追加)や fix(バグ修正)といった種類(type)を付けるだけで、変更の意図が一目で伝わり、リリースノートやバージョン番号の生成を機械が自動化できるようになります。この記事では、読み方・基本の書式・type一覧・SemVerとの対応・commitlintなどでの導入手順、そして導入すべきでない場面までを、コミット例とともに解説します。
まとめ:Conventional Commitsの要点
- 正式な読み方は「コンベンショナル・コミット」。「カンバセーショナルコミット」は誤読で、意味は「規約に基づいたコミット」。
- 書式は
type(scope): description。1行目のヘッダーが必須で、任意でボディ・フッターを続ける。 - typeはfeat・fixが中核。仕様(v1.0.0)で意味が定義されるのは feat と fix のみで、docs・style・refactor などは commitlint の慣習セット由来。
- SemVerと直結。feat→MINOR、fix→PATCH、破壊的変更(
!またはBREAKING CHANGE:)→MAJOR。 - 強制はツールで。commitlint+huskyでフォーマット違反を弾き、commitizenで対話式に作成、semantic-releaseでリリースまで自動化できる。
- 小規模・短期プロジェクトでは形式順守のコストが上回ることもある。全プロジェクトで一律に必須ではない。
Conventional Commitsとは(定義と読み方)
Conventional Commitsは、コミットメッセージの構造を統一するための軽量な規約で、公式仕様は conventionalcommits.org で v1.0.0 として公開されています。「Conventional」は「慣習的な・規約に基づいた」という意味で、正しい読み方はコンベンショナル・コミットです。検索では「カンバセーショナルコミット」という表記も見られますが、これは誤読で、同じものを指しています。
規約が解決するのは「コミットメッセージが人によってバラバラで、履歴から変更の意図が読み取れない」という問題です。先頭に変更の種類(type)を機械可読な形で付けることで、コミット履歴がそのままリリースノートやバージョン判定の入力データになります。人間が読みやすくなるだけでなく、後段の自動化が効くのが最大の狙いです。
コミットメッセージの基本構造(書式・形式)
Conventional Commitsのメッセージは、必須のヘッダーと、任意のボディ・フッターで構成されます。全体像は次のとおりです。
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
ヘッダー:type(scope): description の3要素
1行目のヘッダーだけが必須です。type は変更の種類、scope(省略可)は変更した範囲、description は変更内容の要約を書きます。
feat(auth): add login with one-time password
type と description の間はコロンと半角スペースで区切ります。description は現在形・命令形の動詞で始め(「add」「fix」など)、末尾にピリオドは付けないのが慣習です。ヘッダーは50〜72文字程度に収めると git log --oneline やGitHub上で折り返されにくくなります。
ボディとフッター:詳細・Issue番号・破壊的変更
ヘッダーの後に1行空けてボディを書くと、変更の背景や理由といった「なぜ」を補足できます。さらに1行空けたフッターには、関連するIssue番号や破壊的変更を記載します。
fix(api): reject expired session tokens
Sessions older than 24h were still accepted because the
expiry check compared seconds against milliseconds.
Refs: #482
フッターの Refs: #482 や Closes #482 は、GitHubのIssueと変更を紐付けるために使います。Issueの運用方法はGitHub Issueの使い方・書き方もあわせて参考にしてください。
破壊的変更(BREAKING CHANGE)の書き方
後方互換性を壊す変更は、2通りの方法で明示します。仕様が定めるトリガーで、リリースのMAJORバージョンを上げます。
# 方法1: typeの直後に ! を付ける
feat(api)!: drop support for v1 auth header
# 方法2: フッターに BREAKING CHANGE: を書く
feat(api): switch auth header to bearer token
BREAKING CHANGE: the X-Auth-Token header is no longer read.
! と BREAKING CHANGE: はどちらか一方でも、両方併記でも構いません。fix! のように fix と組み合わせても、破壊的変更がある以上は MAJOR 扱いになります。この記法を漏らすと自動化ツールがMAJORアップを検知できないため、破壊的変更のときは必ず入れます。
typeの一覧と使い分け
仕様(v1.0.0)が意味を定義しているtypeは feat と fix の2つだけです。それ以外の docs や chore などは、広く使われている @commitlint/config-conventional(Angularの規約が基)が推奨する慣習的なtypeで、AngularやVue.jsなど主要OSSの多くが採用しています。主なtypeとSemVerへの影響は次のとおりです。
| type | 意味 | SemVerへの影響 |
|---|---|---|
| feat | 新機能の追加 | MINOR |
| fix | バグ修正 | PATCH |
| docs | ドキュメントのみの変更 | なし |
| style | 整形・空白など動作に影響しない変更 | なし |
| refactor | 機能変更を伴わない内部改善 | なし |
| perf | パフォーマンス改善 | PATCH相当 |
| test | テストの追加・修正 | なし |
| build | ビルドシステム・依存関係の変更 | なし |
| ci | CI設定・スクリプトの変更 | なし |
| chore | 上記以外の雑多な変更 | なし |
| revert | 過去のコミットの取り消し | 内容による |
破壊的変更を含むコミットは、typeが何であっても MAJOR を上げます(表の「影響」は破壊的変更が無い場合の目安です)。
feat・fix・docsなど主要typeの具体例
「feat git」「chore git」などの検索で迷いやすいのは、変更をどのtypeに割り当てるかです。判断は「利用者から見て動作が変わるか」で切り分けると安定します。
feat(cart): allow saving items for later
fix(cart): prevent negative quantity on update
docs(readme): add setup steps for Windows
refactor(cart): extract price calculation into a helper
chore(deps): bump eslint to 9.0.0
新機能なら feat、不具合の是正なら fix、READMEやコメントだけなら docs、依存の更新やビルド周りは chore/build を使います。動作に影響しない整形だけの変更は style に分類し、fix と切り分けます。
scopeの付け方と省略の判断
scope は変更の影響範囲を丸括弧で示す任意項目です。feat(auth) のようにモジュール名・機能名を入れると、履歴を範囲で絞り込めます。scope名はチームで一覧化して固定するのがおすすめで、プロジェクト横断で対象が定まらない変更は省略して feat: ... と書けば十分です。無理に付けると表記ゆれの原因になるため、「絞り込みに使うか」を基準に決めます。
SemVer(セマンティックバージョニング)との連携
Conventional Commitsが評価される最大の理由が、SemVer(MAJOR.MINOR.PATCH)との対応関係です。typeを機械が読むことで、次のバージョンを自動で判定できます。
| コミット | 上がる桁 | 例 |
|---|---|---|
| fix | PATCH | 1.2.3 → 1.2.4 |
| feat | MINOR | 1.2.3 → 1.3.0 |
| 破壊的変更(! / BREAKING CHANGE) | MAJOR | 1.2.3 → 2.0.0 |
この対応があるため、リリース時に「今回はMINORか、PATCHか」を人が判断する必要がなくなります。前回のタグ以降のコミットを走査し、feat が1件でもあればMINOR、破壊的変更があればMAJOR、fixだけならPATCHと機械的に決まります。バージョン付けの属人性とヒューマンエラーを同時に減らせるのが実務上の価値です。
導入手順とツール(commitlint・husky・commitizen)
Conventional Commitsは規約なので、守るかどうかは人任せになりがちです。実運用では、フォーマットを機械で強制し、リリースまで自動化するツールを組み合わせます。
commitlint+huskyでフォーマットを強制する
もっとも一般的な構成が、コミットメッセージを検証する commitlint と、Gitフックを管理する husky の組み合わせです。規約違反のメッセージをコミット時点で弾けます。
npm install --save-dev @commitlint/cli @commitlint/config-conventional husky
# commitlint.config.js
module.exports = { extends: ['@commitlint/config-conventional'] };
# husky でコミット時に検証(husky v9 以降)
npx husky init
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg
これで git commit のたびに .husky/commit-msg フックが commitlint を呼び、feat: などの形式に沿わないメッセージはコミットが中断されます。CIでも同じ検証を回しておくと、ローカルでフックを外したコミットも取りこぼしません。CI側の構成はGitHub Actionsでビルド・自動テストを設定する方法が参考になります。
commitizenで対話式にメッセージを作る
フォーマットを覚えきれないメンバーがいる場合は、commitizen が有効です。git cz を実行すると、typeやscopeを選択肢から選ぶ対話形式でメッセージを組み立てられ、規約に沿った文字列を自動生成します。導入初期の学習コストを下げる用途に向いています。
semantic-releaseでリリースを自動化する
コミットが規約に沿っていれば、semantic-release や standard-version でリリースまで自動化できます。前回リリース以降のコミットを解析してSemVerのバージョンを決定し、タグ付け・CHANGELOG生成・パッケージ公開までをパイプラインで実行します。CI/CDへの組み込み方はGitHubとCI/CDツールの統合のメリットやGitHub Actionsの活用例とあわせて設計すると、手動リリースの手間を大きく削減できます。
導入すべきでない場面と失敗パターン
Conventional Commitsは万能ではありません。効果が出るのは「コミット履歴を自動化の入力に使う」プロジェクトで、そうでない場合はコストが上回ります。次のケースでは導入を見送るか、簡略版に留める判断が妥当です。
- 個人開発・短期の使い捨てプロジェクト:リリースノート自動生成やSemVer運用をしないなら、形式を守る手間だけが残る。
- チームがtypeの基準を共有していない:feat/fix/refactorの線引きが人によってブレると、履歴の一貫性が崩れ、かえって信頼できないリリースノートになる。導入時にscope一覧とtypeの判断基準をドキュメント化してから始める。
- ツール検証を強制していない:規約を口頭ルールに留めると必ず逸脱が混ざる。commitlintで機械強制するのが前提で、それ抜きの「気をつける」運用は長続きしない。
逆に、複数人でメンテナンスし、定期リリースやライブラリ公開を行うプロジェクトでは、導入コストを短期間で回収できます。判断軸は「コミット履歴を機械に読ませたいか」の一点です。
よくある質問(FAQ)
Conventional Commitsの読み方は?
「コンベンショナル・コミット」です。Conventionalは「規約に基づいた」という意味で、「カンバセーショナルコミット」は誤読です。どちらで検索しても同じ規約を指します。
typeはfeat・fix以外に何がありますか?自由に増やせますか?
docs・style・refactor・perf・test・build・ci・chore・revert が慣習的な標準です。プロジェクト独自のtypeを追加することも可能ですが、増やしすぎると判断が曖昧になるため、commitlintの設定に登録して最小限に絞るのが安全です。
破壊的変更(BREAKING CHANGE)はどう書きますか?
typeの直後に ! を付ける(例:feat(api)!: ...)か、フッターに BREAKING CHANGE: と続けて説明を書きます。どちらもSemVerのMAJORバージョンを上げるトリガーになります。
コミットメッセージは日本語で書いてもいいですか?
仕様上、description やボディの言語に制限はなく日本語でも問題ありません。ただし type は feat・fix などの英単語で固定し、ツールが解釈できる形を保ちます。チーム内で言語を統一しておくと履歴が読みやすくなります。
小規模なプロジェクトでも使うべきですか?
必須ではありません。リリースノート自動生成やSemVer運用を行わない個人開発・短期プロジェクトでは、形式順守のコストが利点を上回ることがあります。自動化を前提とするチーム開発から導入するのが効果的です。