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

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

概要

  • 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リリース)を通じて人にバージョン番号の決定を任せることができる。

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

でも、なんでそれがタイトルに入る必要があるの?バージョン管理をその変な方法でやりたいなら、コミット本文に魔法をかければいいじゃん。そうすれば、一言に縛られないし。

このタイトルの書き方、あんまり好きじゃないな。「何かをやめろ」って感じ。人気みたいだけど、すごく命令的だし、「私は絶対にこれが正しい」っていう雰囲気がする。だったら「何かに賛成する」とか「何かに反対する」とか、そういう風に書けばいいのに。

なんで直接的に自分の好きな立場をはっきり主張しないの?彼らの意見に賛成する必要はないけど、言葉を和らげるように頼むのは弱いよ。

https://knowyourmeme.com/memes/stop-doing-math このタイトルのジャンルにインスパイアされたミームがあるよ、参考までに。

同じようなことを言いに来たよ。俺も従来のコミットはあんまり好きじゃないけど、みんなが好きなように使えばいいと思うよ!

「有害と見なされる」ほど悪くはないけど、やっぱりちょっと毒っぽいかな。要は、誰かの個人的な好み(AとBの順番を入れ替えたい)を、もっと大きなことのように言おうとしてる感じだね。

意図的に炎上するような内容を書かないと、注目されないからね。

自分たちの世界観に挑戦する発言には興味が湧くよね。多くの人には失礼だけど、注目経済ではそれが報われるんだ。追記: タイトルがあまり挑発的じゃなくなったみたい。良かったね。

機械可読にしたいなら、フッターやトレーラーを使えばいいよ。従来のコミットについては、いいことは言えないな。フォーマットがメッセージの一番読まれる部分を占めちゃってるし、カテゴリやタイプもあんまり情報がない。正直な英語の動詞を文のように件名に埋め込む方がいいと思う。それに、三種類の句読点(:, (), !)よりも、ただの文の方がずっと読みやすいよね。件名に「エリア」を入れるのは我慢できるけど、それもこの慣習より前からあったことだし。俺の仕事では、非技術者向けのウェブアプリを作ってる。そっちのためのチェンジログは問題なく書けるよ(ノルウェー語でね)。コミットメッセージはユーザーには関係ないし、すべてのコミットがエンドユーザー向けのチェンジログに十分なクオリティであるべきだなんて、そんなの無理だよ。フッターやトレーラーを使った方がいいよ。

従来のコミットで「chore」って言葉を使うのが、いつもイライラさせられる。俺は「linux kernel」スタイルのコミット件名が好きなんだけど、ここで言及されててよかった。 [0] https://www.kernel.org/doc/html/v7.0/process/submitting-patc...

リッチの意見も面白いかもね: https://richvdh.org/conventional-commits-considered-harmful....

完全に同意だわ。「chore」って言葉が持つ態度がすごく嫌だ。まるで他のは全部「楽しい」か「無関心」ってマークされるべきみたい。そんな感情的な判断はコミットメッセージにはいらないよね。

代わりの言葉を見つけたよ:「upkeep」。同じアイデアだけど、悪い意味はないね。

確かに用語としては良くないよね。でも、コミットの全体的な影響を事前に知っているふりをするのも問題なんだよ。実際にはわからないのに。普通のコミットを使うと、チーム全員やLLMがそのバカみたいな命名法を考えるために時間やトークンを使わなきゃいけなくなる。

本当に大事なのは、プロジェクトごとに異なる要件があるってことだよ。30年以上ソース管理を使ってきたけど、標準化された方法でコンポーネント(記事ではスコープと呼ばれてる)を説明に含めることが役立つプロジェクトには一度も関わったことがない。影響を受けるファイルがソースツリーのどこにあるかで、どのコンポーネントが影響を受けるかは明らかだし。同様に、「バグ」、「修正」や「機能」も特に価値がない。重要だからチェックインされるわけで。俺が見つけた有用なものは、記事が考慮すらしていない、関連する変更リクエストのリンクやIDだよ。コミットには変更で何が行われたかの情報がすでに含まれてるけど、欠けているのはその理由のコンテキスト。ソロプロジェクトでも、説明の前に角括弧でJIRAのリファレンスを入れてる。もし開発中にランダムに修正することに決めた場合は、短い1行のJIRAを作ってIDを取得し、そこで理由を説明するようにしてる。

別のソースを使うメリットって、画像とか何か他のものを含められるからなの?コミット本文にコンテキストを含められない?

リリースノートを自動生成するなら便利だよね。そうすれば、新機能が最初に、次にバグ修正がまとめられるから、技術に詳しくない人でも読みやすくなる。

最近働いたところでは、PRのタイトルにJIRAのチケット番号を入れるのが必須だったな。

JIRAを使っていたときはこんな感じだった。GHのイシューでは、コミットからPRの議論に戻るのはいつでもできる(そこにはリンクされたイシューや他の指摘があるはず)。もちろん、GHのイシューに切り替えたとき、JIRAはほとんど放棄して、数年後にはインスタンスがオフになって削除された。今となっては、あのJIRAのタグは完全に無駄になった。個人的には、イシュー追跡ツールとgitリポジトリの密接な結合が必要だと思う。実際に求めているのはポータビリティだけど、それは密接な結合なしでは得られないと思う。理想的にはオープンな標準フォーマットがあるべきだけど、現状ではgithubがフォーマットを定義する800ポンドのゴリラで、gitlabや他のクローンがgithubのプロジェクトメタデータ(少なくともPR)を取り込める限り、近づける。だけど、5年後に使っていないかもしれないアトラシアン製品への固定された不変のポインタは良い方針じゃないと思う。むしろ、gitコミットは完全に独立して存在すべきで、変更の「理由」に関する情報はgitコミットやソースのコメントに組み込まれるべきだと思う(ただ、みんながgitコミットで過度に簡潔になってしまって、問題を要約して情報を失うから、PRの議論のやり取りは一人の声だけじゃなくて、変更の理由をまとめるのに役立つ)。

同様に「バグ」や「修正」、「機能」って、あんまり役に立たないよね。もし問題追跡ツールを使ってないなら、gitにこういうタグを埋め込むことで、基本的なメトリクスは得られるけど。

僕が役に立つと思ったのは、この記事が全然触れてないけど、関連する変更リクエストのリンクやIDだね。コミットには何が変更されたかの情報は全部入ってるけど、足りないのは「なぜ」なのかっていうコンテキスト。『なぜ』がgitのコミットメッセージに入れるべき一番のポイントなんだ。『なぜ』をキャッチするのがそのメッセージの目的で、外部の(最終的には存在しないかもしれない)リソースへのリンクを貼るのは良い代替にはならないよ。

チケット番号の扱いについてはFAQに載ってるよ(下にスクロールしてね)[編集] https://scopedcommits.com/の一番下にあるよ。

fixesやfeatureのラベルは、外部や内部で公開する場合にセマンティックバージョニングやチェンジログを生成するのに役立つよ。JIRAのチケットも役立つし、コミットが存在する理由にコンテキストを与えることが大事なんだ。大きなモノレポでは「コンポーネント」ラベルが一番役に立つと思う。

その通り。コミットメッセージは未来の開発者のためのもので、チェンジログを生成するためじゃない。未来の開発者がコミットメッセージを読むのは、そのコミットがなぜ存在するのか理解できない時なんだ。何が変わったかじゃなくて、特定の行の目的が知りたいから。「blame」を実行してコミットを見たら、元の開発者はもう会社にいないかもしれないし、古いJIRAも存在しないかもしれない。唯一の手がかりはコミットメッセージだけなんだ。 https://dev.to/splix/the-why-behind-the-code-2bb1

プログラマーとして、ちょっとしたこと(タブかスペースかとか)で最適な設定についていつも細かいことを言い合ってる気がする。普通のコミットメッセージの構造に関して、従来のコミットが神から与えられた最善の方法だとは言わないけど、定義された構造があるのは重要だと思う。コミットメッセージに関する期待を設定するのはすごく効果的だし、従来のコミットはそれに対して十分だと思う。著者がスコープの方がタイプより重要だって大騒ぎしてるけど、僕は同意するかもしれないけど、「fix(compiler)」と「compiler fix」の違いは、そこまでこだわるほどのものじゃないと思う。技術業界には最適じゃなくても標準になったものがたくさんあるし。例えば、もしゼロから始めるなら、どんなまともな人もJSONはコメントをサポートすべきだって主張すると思う(ダグラス・クロフォードのコメントを含めない理由は全然理解できないけど)。でも、JSONは多くの文脈で前のものより良かったから標準になったんだ。従来のコミットとは少し異なる別のフォーマットがあるかもしれないけど、それが本当に良いとは思えないし、全く別の競合する方法でコメントを構造化する必要はないと思う。

定義された構造が質を構成するわけじゃない。コミットメッセージは緩やかに構造化されていても、変化の本質を伝えるのに非常に洞察に満ちていて良いこともある。一方で、非常に構造化されているけど混乱を招くような、または情報がないコミットメッセージも作れる。一般的に、著者に同意する傾向があるけど、従来のコミットは質の低いコミットメッセージの問題を根本的に解決するわけではないと思う。

従来のコミットは、少なくともコミットメッセージに何か考えを入れるように人を強制できるから、良いアイデアだと思う。実際、「小さな修正」という件名のコミットを何度もレビューしなきゃいけなかったけど、それは実際には小さな修正じゃなかったんだよね。

コミットメッセージって誰が読むの?

僕はこの件に関して特に利害関係はないけど、OPの反論は薄っぺらく感じるな。- スコープは重要なのは確かだけど、それってコミットの内容から導き出せるんじゃない?diffの重要なサニティチェックは、影響を受けたパスを見ることだよ。(「テスト」diffは本番の認証コードを変更しちゃダメ。)でも、—onelineから見るなら、確かにfeat(auth):の方がfeat:よりいいと思う。 - 誤ったオーディエンス: それには同意しないな。featコミットは実際にプロダクトに影響を与える変更を説明すべきだよ。まずはノーオプのリファクタ変更でいいスタックを整えてから、小さな新機能の変更をその上に載せるべきだね。これがdiffコメントに含めるべき一番役立つことだと思う。技術的なことはコメントに入れておくべきで、失われないようにするべき。「なぜアルゴリズムXを選んだのか」はコメントかDECISIONS.mdに入れるべきだよ。こういうのは、急成長している会社のコミット履歴でやるのは面倒なことだけど、OSSプロジェクトではコミットメッセージにコンテキストを詰め込むのがもっと重要だと思う。

余談だけど、JSONはすごく一般的だけど、EDNが一番いいデータフォーマットだと思う。

標準化には賛成だけど、この議論を使って非最適な現状を維持しようとすることもできるよね。XMLは十分良くて、標準だし。SOAPも十分良くて、標準だし。などなど。主張は、従来のコミットが十分良くて標準化されているから、別の構造を持つことは本当に価値がないってことなんだ。でも「価値がある」は主観的だよね。毎日コミットしてPRを読んでるなら、従来のコミットフォーマットがちょっとした摩擦を生むことがあって、その摩擦が積み重なることもあると思う。従来のコミットを自然の法則として見る以外の選択肢があることで、それを好むチームにとって選択肢が増えるんだ。(ほとんどのチームはチェンジログを生成してないしね。)

JSONのスコープ付きコメントを紹介するよ。従来のJSONコメントよりずっと優れてる。これを使ってる人たちはライバルだね。

ダグラス・クロフォード・クロックフォード。

プログラマーによっては2スペースが好きな人もいれば、4スペースが好きな人もいる。じゃあ、妥協して2と4の間の数字を選ぼうよ。

個人的には、仕事のプロジェクトの一つでこれが好きなんだ。使ってるのはそれだけ。AndroidとiOSのアプリが入ったリポジトリなんだけど、従来のコミットが好きなのは、リリースの時に2つのタグの間を見て、「このリリースの新機能」セクションに何を入れるか選べるから。普通の「バグ修正といろいろな改善」っていうだけじゃなくて、実際にやったことを載せるようにしてる。リリースブランチに何が入ったか、何をチェリーピックする必要があるかを明確にするのにも役立つしね。「What's new」セクションの生成は自動化してないよ。タグの間のコミットを全部見てる。あと、バグ-ios: feat-androidみたいにすることが多いから、バグ-ios: メモリ問題修正みたいな一般的なものにはならない。

最高のコミットメッセージは、1週間後の未来の自分にとって意味のあるものだね。それ以上は考える時間が少なくて済むから。

この考え方には完全に同意だよ。実は、ちょっと前に自分の基準も作ったんだ。https://commits-with-character.org/ これはGit Bookのガイドラインに軽く追加したもので、スコープにももっと重点を置いてる。小さなプロジェクトではすごくうまくいってるよ。