世界を動かす技術を、日本語で。

従来のコミットは間違ったことに焦点を当てることを促す

概要

  • Conventional Commits は多くのプロジェクトで使われているが、本質的な問題点が多い
  • 変更の スコープ よりもタイプ(fix, feat等)を優先し、実用性に欠ける
  • Changelog自動生成 などのメリットも実際には十分に機能しない
  • 成功している多くのプロジェクトは スコープ重視 のコミットメッセージを採用
  • より良いコミットメッセージの構造とその理由を提案

Conventional Commitsの問題点

  • Conventional Commits は、コミットメッセージにセマンティックな意味を持たせることを目的
  • フォーマット例: <type>[optional scope]: <description>
  • type (例: fix, feat, chore等)が先頭に来て、 scope は任意
  • scope (どの部分を変更したか)が最重要だが、typeが優先されている現状
  • scopeが任意であるため、 「主語のない文」 のようなメッセージが生まれやすい
  • typeの情報は 冗長 であり、説明文から容易に判断できる場合がほとんど
  • typeの選択が難しく、 複数のタイプに該当する変更 では逆に混乱を招く

スコープ重視の重要性

  • コントリビューター :自分の興味範囲や作業中の部分に関連する変更を探す際、scopeが最重要
  • デバッガー :バグ発生時、どの領域が変更されたか(scope)がバグ特定の鍵
  • インシデント対応者 :障害発生時、scopeから原因箇所を特定しやすい
  • いずれも type情報はほぼ役に立たず、scopeが本質的に重要

Conventional Commitsの約束と現実

  • Changelog自動生成
    • コミットログとChangelogの 利用者層・目的が異なる
    • Changelog:ユーザー向け、機能・ビジネス観点の変化を知りたい
    • Commit log:開発者向け、コードベースの変遷を追いたい
    • 複雑な機能追加は複数コミットに分かれるため、Changelogには不向き
    • revert コミットの扱いも不十分
  • セマンティックバージョン自動判定
    • revertや誤ったtype指定で 誤ったバージョンアップ が発生
    • 実際のソフトウェア開発の流れに合わない
  • 変更内容の伝達
    • ChangelogとCommit logの目的・対象が異なるため、両立できていない
  • 自動ビルド・公開トリガー
    • タイプ指定に依存した自動化は セキュリティリスク や誤動作の温床
    • 実際はgit diff等でscopeを見て処理する方が安全
  • 貢献のしやすさ
    • フォーマットは整うが、 実際の貢献のしやすさには寄与しない

実運用上の課題

  • プロジェクトごとに type定義が必要 だが、多くはcommitlintのデフォルトを流用
  • 企業プロジェクトでは チケット番号 をscopeに入れるケースが多く、scopeの意義が薄れる

スコープ重視型コミットメッセージの推奨

  • Linux, FreeBSD, Git, Go, NixOS など著名プロジェクトはscope重視のフォーマットを採用
  • 例:
    • Linux: サブシステム: 説明 (i2c: virtio: mark device ready before registering the adapter)
    • Go: パッケージ: 説明 (net/http/cookiejar: add godoc links)
    • NixOS: パッケージ名: 説明 (xwayland: 24.1.11 -> 24.1.12)
  • scopeはプロジェクトごとに 自然に決まる (サブシステム名、パッケージパス、マイクロサービス名等)

結論と提案

  • Conventional Commitsの利点は幻想的 であり、実際の価値は低い
  • しかし普及しているため、AIもこのフォーマットをデフォルトで使いがち
  • より良いコミットメッセージ構造 としてscope重視型を推奨
  • Changelog生成とコミットログ管理は 分離すべき
  • 今後は scopedcommits.com などを通じて、より良いプラクティスの普及を目指す

Hackerたちの意見

俺の主な不満は、従来のコミットがコミットタイトルにイシュー番号を含めてないことなんだよね。オプションとしても言及されてないし。俺にとって、コミットメッセージの中でこれが一番大事な情報だと思う。過去15年間、古いコミットを見てイシューの説明を確認して、変更の全体像を把握することがどれだけあったか分からないよ。これが標準的な習慣だと思ってたけど、「従来のコミット」について学ぶまでそう思ってた。あの盛り上がりは全然理解できなかった。

なんでコミットタイトルにイシュー番号が必要なの?俺はそれが超ウザいと思うし、残念ながらGitHubはマージキューを使うと強制されるんだよね。説明欄にあればいいと思うけど。

個人的には、すでに文字数が限られてる変更ログをざっと見るときに、メインのコミットメッセージに「XYZ-999999」とかは気にしないな。トレーラーとしてタグ付けするのはいいけど、どのJiraイシューから来たかよりも、コミットが何をしたかを見たい。

それは標準じゃなくて、慣習だよ。チーム内でコミットメッセージにチケットIDを含める標準を設定することもできる。

そう、それが実際の鍵だよ。実用的なタイトルと変更に関する議論への安定したリンクが必要なんだ。従来のコミットは見た目はいいけど、実際の有用性は疑問だよ。コードが自分で語るからね。実際に役立つ情報は、よく選ばれたタイトルと変更のコンテキストなんだ。

面白いね、俺たちはずっと間違ったことをしてたみたいだ。俺たちは fix(ABC-123): ここにメッセージ ってやってて、リンクもバッチリだし、自動リリースノートにもすごくきれいに表示されるんだよね。

個人的には、問題をgitトレーラーとして含めるのが好きだな:fooの問題を修正する Issue: ABC-123。Gitにはこれらのトレーラーを解析してフォーマットするためのビルトインがたくさんあるから、インラインで見るためのカスタムgit logエイリアスを簡単に作れるよ。

俺の意見では、問題キーを件名の最初に持ってくる必要があるって主張する人にとってだけの問題だと思う(これもまた、可読性には良くないと思うけど)。従来のコミットのゴミの後にどこかにそれを置いておけばいいじゃん?問題キーは、アルファベットと数字のプレフィックスに基づいて取り出せるべきだし、こういう「標準」に対して特別にスペースを設ける必要はないと思う。個人的には(従来のコミットなしで)、そのコミットがその問題に関係しているなら、最後にカッコで入れることが多いよ。でも、もしその問題を修正するような強い関係があるなら、メッセージにFixesトレーラーも入れる。

従来のコミットが本当に役立つのは、継続的デリバリーのためなんだ。メインへのマージは自動的にセマンティックバージョニングでタグ付けされて出荷できる。タグ付けやバージョニングに関する考えは、開発者がコミットメッセージを書くときにすでにされてるからね。Linuxカーネルみたいな大規模プロジェクトには意味がないのは分かってるけど、99%のプロジェクトにとっては、従来のコミットとセマンティックバージョニングの組み合わせがリリースプロセスを大幅に改善して、自動化を簡単にしてくれる。

俺はOSSプロジェクトでセマンティックバージョニングのバンプを自動化するためにこれをやってるけど、すごくいいよ!仕事でも「タグ」を強制してる(gitタグじゃなくて、PRタイトルの文字列ね)。誰がその変更に関心があるかに基づいて、各チームのために変更ログを生成してる。

継続的デリバリーの状況でも、git tagsに頼るのが好きなんだ。git describeは、継続的デリバリーのバージョニングに十分なことが多いし、v1.2.3-4-gabcdefみたいにコミットを正確に説明できるから、gitも満足するし、期待を設定するにはセマンティックバージョニングに近い。特に新しいgit tagsが人の判断でしか挿入されない場合(「これは破壊的変更だから新しいメジャーをタグ付けしなきゃ」みたいな)。git describeのフォーマットバージョン番号に関する唯一の本当の議論は、セマンティックバージョニングの期待によりよく従うために最初のダッシュをプラスにすべきかどうかで、それは必要だと思ったら簡単に正規表現で変更できる。git describeはCDの自動化には簡単だけど、コミット履歴の魔法のキーワードから推測するのではなく、git tagの選択(やGitHubリリース)を通じて人にバージョン番号の決定を任せることができる。

この記事では、なぜこれがうまく機能しないのかを説明してる。

Hacker Newsで議論の続きを見る