3 ポイント 投稿者 GN⁺ 2024-02-02 | 1件のコメント | WhatsAppで共有
  • GOV.UK の作業中に生まれた Dan Carley の「“Convert template to US-ASCII to fix error”」コミットは、小さなコード変更でも 詳細なコミットメッセージ によってコードベースの長期的なドキュメントになり得ることを示している
  • 良いメッセージは何を変えたかよりも、なぜ変えたのかを残し、この事例では bundle exec rake 実行時に発生した invalid byte sequence in US-ASCII エラーと再現条件を具体的に記録している
  • エラー文字列と調査過程が一緒に残っているため、後から同じ問題に遭遇した人が git log --grep や GitHub のコミット検索で 過去の解決記録 を見つけられる
  • メッセージには find -execfile --mimeiconv のような Unix ツールの利用過程も含まれており、レビュー担当者や後の読者が問題解決の流れを追える
  • すべてのコミットがこの程度の長さである必要はないが、文脈・調査過程・感情まで残したメッセージは、チームの 共有されたコードベース理解 と信頼を築く助けになる

小さな変更を長期ドキュメントにしたコミット

  • Git のコミットメッセージは、うまく書けばコードベースの寿命を通じて残る 強力なドキュメント化ツール になり得る
  • 例として挙げられているコミットは、Government Digital Service で GOV.UK に取り組んでいた時期の変更である
  • 作者は Dan Carley で、タイトルは「Convert template to US-ASCII to fix error」である
  • GDS の公開開発スタイルのおかげで、このような組織内部の事例も外部に共有できる

長さより重要な文脈

  • このコミットはコード変更量に比べてメッセージが長いが、核心的な価値は長さそのものではなく 有用な文脈 にある
  • 同じ変更でも別の場所なら change whitespacefix bug のように短く残されていたかもしれない
  • Dan は周囲の同僚や後の読者が問題の原因と解決過程を理解できるよう、詳細な記録を残している

何が変わったかより、なぜ変わったか

  • 良いコミットメッセージは、何が変わったかだけでなく なぜ変わったか を説明する
  • このコミットは、feature branch で /etc/nginx/router_routes.conf の内容と突き合わせるテストを追加した後、実行方法によって結果が異なっていたことを記録している
    • bundle exec rake spec または bundle exec rspec modules/router/spec で実行すると正常に動作する
    • bundle exec rake で実行すると各 should ブロックが失敗する
    • エラーは ArgumentError: invalid byte sequence in US-ASCII である
  • こうした説明がなければ、どのツールでどのようなパースエラーが起きたのか推測しなければならなかった可能性が高い
  • 元の作業文脈は、人が忘れたり、チームを移ったり、組織を離れたりすることで簡単に失われうる

検索可能なエラー記録

  • コミットメッセージの冒頭には、変更のきっかけになった エラーメッセージ がそのまま入っている
  • 同じエラーに遭遇した人は、次の方法でコードベース内の過去の記録を探せる
  • 検索結果を見ると複数の人がこのエラーを検索しており、誰が最初に問題に遭遇し、どのような対応を取ったのかも確認できた

問題解決の過程を物語として残す

  • メッセージは、問題がどう見えたか、どの順番で調査したか、最終的にどう修正したかを追えるように構成されている
  • 例として、.with_content(//) matcher を取り除くとエラーが消えたこと、spec ファイルにはおかしな文字がなかったこと、同じインタプリタで Puppet を require すると再現できたことなどが含まれている
  • コミットメッセージは特定のファイル・関数・コード1行ではなく 変更そのもの を文書化するため、コードベースがたどってきた道のりを記録するのに適した場所である

チームの知識を広げる副次効果

  • Dan は調査の各段階で実行したコマンドを記録しており、これはチーム内で知識を軽やかに広める方法にもなりうる
  • 読者はコミットメッセージだけでも Unix ツールの使い方を学べる
    • find-exec 引数を渡すと、見つかった各ファイルに対してコマンドを実行できる
    • コマンドの末尾に \+ を付けると、ファイルごとに1回ずつ実行する代わりに、複数のファイル名を1つの file コマンドに渡せる
    • file --mime はファイルの MIME タイプを教えてくれる
    • iconv というツールが存在する
  • 変更をレビューする人だけでなく、後からこのコミットを見つけた人も同じ情報を得られる
  • 十分な時間とコミットが積み重なれば、このようなメッセージはチームの 知識増幅装置 になりうる

共感と信頼を築く記録

  • コミットの最後には「Now the tests work! One hour of my life I won't get back..」という一文がある
  • この文は、1時間にわたって巧妙なバグを追跡した開発者の苛立ちと、解決したときの満足感を伝えている
  • 短期的なハックやプロトタイプのコードが本番環境に入り込み定着してしまった場合でも、このようなメッセージは、あらゆる変更の背後に、その時点で得られる情報の中で最善の判断をした人がいたことを思い出させてくれる

すべてのコミットが長くある必要はない

  • この事例は極端な例であり、とくにこの規模のすべてのコミットに同じレベルの詳細さを期待する必要はない
  • それでも、変更の背後にある文脈を説明し、他の人が学べるようにし、チームのコードベースに対する 共通のメンタルモデル に貢献する好例である
  • 良いコミットメッセージや変更の構造化ツールに関心があるなら、次の資料も参照できる

1件のコメント

 
GN⁺ 2024-02-02
Hacker Newsの意見
  • 良くも悪くも、GitHubの共同創業者であり『Pro Git』のようなGit本を書いた立場から見ると、Gitのコミットメッセージはコードを文書化する独特の経路だが、非常に非効率でもある。
    核心的な問題は、GitやGitHubなど大半のツールが通常最初の1行だけを表示することだ。例のコミットも「US-ASCII error」のような一般的な1行しか見えず、記事で称賛されている残りの内容は、現代のツールではほとんど誰も見ない。
    Gitはコミットメッセージを、プロジェクトの全員が読むメール本文として設計したが、今日では概ねその役割を果たしていない。メーリングリストでパッチシリーズとして議論されるのでない限り、タイトルの最初の50文字以外はほとんど読まれない。
    git blame-w -C -C -Cなのか-Cが2つなのかを調べて関連メッセージを追跡しても、最初の1行しか見えず、結局SHAを見つけてgit showしなければ長いメッセージは見られない。うまく書かれていても情報を再発見するのが難しすぎる、というのがGitに対する最大の不満の一つだ。
    Gitプロジェクトの履歴を見ると、コミットメッセージはほぼ毎回すばらしいが、1つのファイルにblameをかけても関数実装の文脈を追うのは非常に難しい。Jeff Kingがこの10年間に残してきた文書化は天才的なレベルなのに、ほとんど誰も味わえないという点がひどい。
    正確な解決策は分からないが、大半のコミュニティでは、コミットメッセージで優れた文書を書くことは、悲しいことにほとんど時間の無駄に近い。見つけるのが難しすぎるからだ。

    • GitHubができる前からGitに貢献し、レガシーコードを保守してきた立場からすると、まったく同意できない。ターミナルでgit blamegit loggit showを常に使っているし、ファイルの履歴を追うのは簡単で、git log -Gで追加・削除された時点を見つけるのにも数秒で十分だ。
      つらいのは、コミットを見つけたのにメッセージが「bleh」や「add a thing」だけだったときだ。開発者が60秒だけ使って、なぜそうしたのかを書いてくれていればよかったのに、と思う。
      逆に、ある変更がなぜ行われたのかを詳しく説明したコミットメッセージを見つけると本当にうれしい。良いコミットメッセージ1つで、数時間、数日の作業が節約できる。
      GitHubも悪いコミットメッセージ問題に加担している。運が良ければPRの説明に詳細があるが、コミットログのすぐ横にあるわけではなく、たいていPRはJiraリンクへ、JiraはSlackの会話リンクへ、SlackはGoogleドキュメントのリンクへと続く。
      業界は文書化が本当に下手だが、Jeff Kingのような人たちはきちんと戦っている。結局これは技術の問題ではなく、人の問題だと思う。文書を書くことには即時の報酬がないため追加作業のように感じられ、その報酬は数日、数週間、数カ月後になって初めて現れる。
      どうか良いコミットメッセージを書いてほしい。なぜやったのかだけでも1分かけて書けば、すべてのコミットが忌々しいチェスタトンの柵の謎解きにならずに済む。簡単に見つけられるコミットメッセージに入れておけば、未来の自分と私が感謝する。
      付け加えると、コミットメッセージは見つけにくいという主張にも同意しない。コードの1行、関数、ファイルの歴史を追うのにほとんど困難を感じないし、コミットメッセージは後で読まれなくても、書いた時点で価値がある。良いメッセージを書くと、自分が意図したコードを実際に書けているか確認することになるし、コードレビュー担当者にとっても価値がある。
    • 「Gitはコミットメッセージをプロジェクトの全員が読むメール本文として設計した」という話は信じがたい。実際にはコミットの主題や変更されたファイルに関心のある人が本文を読む、ということに近いのであって、なぜ他の人が不変の履歴文書を読まなければならないのか分からない。
      git blameのオプションを見つけにくいという話も冗談のように聞こえる。優れたIDEなら各行にblame情報を付け、ボタン1つで差分を見せ、その差分内の文脈行や削除された行について再帰的にblameできるべきだ。Tigのようなツールはまさにそれをやってくれる。
      GitHubがコミットメッセージを見にくくしていることは認める。
      コミットに付いた文書は、楽しみで書いているわけではない。後で存在しないかもしれない人から送られたパッチを受け入れるリスクを減らし、関連する考えを明らかにして他の人がその上で作業できるようにする仕組みだ。考えを隠すとコードに対する排他的な所有権が積み上がり、たいてい良いことではない。コミットメッセージは作業証明の役割も果たし、パッチが多すぎるときには重要になり得る。商用プロジェクトでは、その重要性の一部は低いかもしれない。
    • ターミナルのGitは得意ではないが、IntelliJやEmacsのMagitを使えば、ファイルを変更したすべてのコミットを簡単に見つけられ、コミットメッセージ全文も楽に辿れる。適切なツールを使えば難しくないし、ほとんどの人が似たようなツールを持っているのではないかと思う。本当にGit CLIだけにしがみついて、何百ものコマンドやフラグを暗記しようとしているのか、なぜそうするのか分からない。
    • これはGitHubなどの失敗だ。GitHubはユーザーがコミット履歴を推論できないと見ているのか単純化しようとしており、その結果の一つがこれだ。特に非公開GitHubリポジトリの混乱は、ときに信じがたいほどだ。
      こうした文書を置く場所は、実際ほかにない。コードコメントには合わない。ところが今では、GitとはすなわちGitHubであり、Gitの唯一の目的はGitHubに変更をアップロードすることだ、と考える開発者世代が生まれている。
      Gitは良くないが、他のものよりはずっとましだ。もう一度基本に立ち返り、バージョン管理の本当の目的を理解する必要がある。
    • それでも歴史研究には優れている。コードとともに永遠に残る数少ない文書の一つだ。GitHubやその他の中央集権型の形態は、人々が簡単にバックアップ・変換・移行できるオープンなデータ形式ではないため、プロジェクトを移すと通常はデータを置き去りにする。
      だから現在のコミュニティには大きな助けにならないかもしれないが、数年後にデバッグする人には役立つ。
  • 全体的な趣旨には同意するが、もっと具体的な BLUF を先頭に置くとよいと思う。たとえば「Fix test issues caused by non-breaking space character \xa0」のように書く、ということ
    何が問題なのかすぐ分かるし、もっと知りたければ下を読む自由も残っている

    • このメッセージのほうがずっと良いと思う。GitHubで差分を見れば、何が変わったのかもかなり明らかだ。差分が同じに見える変更セットなら、見えない文字が追加または削除された場合くらいしかないからだ
      だから履歴検索用には、良い 1行目のメッセージ だけで十分だと思う。バグの根本原因を探しにナルニアまで行って帰ってきた短い話は、あまり関係ない。ただし、PRの説明やコミットの拡張メッセージに、憂さ晴らしも兼ねて書くことまで反対するわけではない
    • 理想的なコミットメッセージもその程度だと思う。記事に出てくる残りの本文は、どうやって発見したかという話にすぎない。作業過程の説明はGitのコミットメッセージに入れる必要はないと思う。このメッセージは、なぜ、どんな変更が行われたのかを伝えており、それで十分だ
    • この考え方は気に入っている。常に一番上には 最も実行可能、または重要な内容 を置き、その後に文脈を置く。他人の時間を尊重し、要点を埋もれさせてはいけない
  • 優れたコミットメッセージを書くことへの誇りは感じたことがあるが、それが他人に与える価値についてはあまり確信がない。奇妙なエラーメッセージに遭遇したとき、新機能を追加するとき、あるいはほとんどどんな場合でも、多くの人はコミットメッセージを検索しないと思う
    少し悲しいが、美しいコミットメッセージにはプログラマーの 虚栄 に近い側面があるのではないか、という疑いが強くなっている。主に感嘆するのは書いた本人で、他の人は気づかないまま通り過ぎる
    ときにはそうした美的な装飾が入る余地はあるが、実用的価値が大きいとは確信していない。他人が「fix whitespace issue」とコミットしても、今ではあまり気にしなくなったし、そのおかげでより良い同僚になれた気がする
    GitやLinuxのように巨大な分散チームと膨大なコミットがあるプロジェクトでは違うかもしれない。自分が慣れているプロジェクトはコントリビューターが1〜100人で、ほとんどが同じ組織に属している

    • コミットメッセージは、一生見る人が自分だけだとしても価値がある。問題を 二分探索 するとき、ついに見つけたコミットのメッセージは本当に役に立つ。「fixed thing」だけでなければ、もっと役に立ったはずだ。同じような問題にまた遭遇しても、参照できるコミットノートがあるという意味でもある
    • 変に思われるかもしれないが、積極的に関わっているプロジェクトなら、すべてのコミットに少なくとも目を通そうとしている。オープンソースプロジェクトなら、そのコミットメッセージは永遠に残り、コード検索だけでなく一般の検索エンジンにもインデックスされ得る。ただし、GitHubがボットをますますブロックしている今では、以前ほどではないかもしれない
      GoogleやStack Overflowで答えが出ないときは、GitHubで似た内容がPRやコミットメッセージにあるか探すこともある。アクセス可能な非公開リポジトリまで含めて、数えきれないほど助けられてきた。良いコミットメッセージには、虚栄を超えた明確な価値がある。多くの開発者が見ないのなら、それは彼らの損失であり、経験を積めばいつか気づいてほしい。ジュニア開発者に検索方法を教えたり、元記事を送ったりするのもよい
    • 短すぎるコミットメッセージに何度か痛い目に遭ったことがある。誰かに「なぜこうしたんですか?」と聞かれてGitの履歴を確認し、3年前に自分が残した「it should be done this way」というメッセージを発見する、という具合だ
      それ以来、少なくとも未来の自分が変更が必要だった理由を思い出せるように、コミットメッセージを書くようにしている
    • コードを1行変える前に git blame をしないなら、それは間違っている
      明らかなバグを直そうとしてコミット直前に以前のコミットを見ると、その「バグ」は意図的に入れられたもので、リンクされたJIRAの作業には、なぜ自分の明白な変更が2年前のバグを再発させることになるのかがよく説明されていた、ということが何度もある
    • IDEの git blame 機能で、何が起きていたのかをよく把握している。良いコミットメッセージを見つけるとありがたく感じる
  • コミットメッセージの1行目は、git logチェスタトンの柵 を扱えるようにするものなので最も重要だ。この場合、筆者は空振りしていると思う
    1行目には何をしたかではなく、なぜしたかを入れるのが肝心だ。何が変わったのかは、関心のある人がコードや差分を見ればよい
    たとえば「nginx .conf files must be in us-ascii」と書き、その次に「changed blahblah.erb to remove nonbreaking space character」を置き、その後にかなり良い残りのコミットメッセージを続ければよい
    ニュース記事のように考えるべきだ。読者がどの時点で読むのをやめてもよいと仮定し、重要度は下がり、詳細は増える順に書くべきだ

    • 1行目は、まず問題のコミットを見つけられるように 何を変えたのかの要約 にすべきだ
      ニュース記事も見出しでは「なぜ」ではなく「何」を説明する
      この場合、元の1行目はぴったりだ。git log をざっと見ると、このコミットがテストの機能的な振る舞いを変えたものではない可能性が高いと判断して、読み飛ばせる
    • 任意のプロジェクトのコミットメッセージは、誰かにnginxのルールを教える場所ではない
      「nginx .conf files must be in us-ascii」はバグやPRのタイトルとしてはよいかもしれないが、複数のコミットがそれぞれ別の作業に対応することもあり、実際に何が起きるのかは教えてくれない。ファイルをUS-ASCIIに変換するのか、変換ツールを作るのか、ドキュメントを更新するのか、テストを作るのか、あるいはその組み合わせなのか分からない。なぜではなく から始めることで、その混乱を減らせる
    • 要するに、1行目だけが出力される場合があるのだから、1行目に意味の通る内容を入れるべきだ、という話でよいのではないかと思う
      「The files must be in us-ascii」でも「Changed the files to us-ascii」でも大差はない。どちらもファイルがUS-ASCIIに変わったことを明確に伝えている
  • このようなドキュメント化の欠点は、一度書いたコミットメッセージを事実上変更できないことだと思う。
    もちろん rebase で可能ではあるが、1年前に書かれてすでに main にマージされた内容を、本当に変更して全員の機能ブランチに苦痛を与えるのか、という気がする。
    .md ファイル、Wiki、Confluence に保存されたドキュメントと比べると、同僚が書いた内容を自分が改善でき、別の同僚も自分が書いた内容を改善できる。
    この事例ではバグが修正され、再発しないかもしれない。だが特定のコンポーネントをコミットするとき、その設計を説明したくなる誘惑があるが、今では避けている。ビジネス要件の変更などで後からそのコンポーネントが変わったらどうなるのか。コミット文書は差分だけを説明することになるのか。そうなると新しいチームメンバーがシステムを理解するには、複数のコミットメッセージを読み、頭の中でマージしなければならない。

    • それは欠点ではない。コミットは歴史記録なので、3年後に戻ってきたとき、当時の文脈でその目的が何だったのかを知りたいのであって、洗浄された歴史を見たいわけではない。
      .md ファイルや Wiki、Confluence と比較するのは、自転車とガチョウを比べるようなものだ。
      コンポーネント作成時の考慮事項やトレードオフ、あるいはそうしたものがなかったという事実は、元からの欠陥、その後の進化、ユースケースの変化から生じた欠陥を理解するうえで、しばしば有用だ。
      新しいチームメンバーがシステムを理解するには、別途「現在」のドキュメントを維持すればよい。その文書は、実装上のトレードオフや、初稿が時間的制約の中で書かれたという事実まで扱う必要はない。
    • コミットメッセージが編集不能である点は、むしろ利点だと思う。後から直すのは難しいが、コードベースの歴史を段階ごとに追うことは、本当に多くのことを明らかにしてくれる。
    • コミットメッセージは、時間とともに更新されるべきドキュメントではなく、単一の変更の歴史記録だ。後で重要な詳細を書き漏らしたと気づくのは残念だが、全体としては決して変わらないことが重要だと思う。
    • Git は、何が変わったかについての不変ログと、何がマージされたかについての ideally 可変な物語を混ぜてしまった点が失敗だったと思う。そのため、コミットの squash、rebase、マージをめぐって議論が生まれる。
      コミットを squash すると、機能追加の物語としては記録がよくなり、マージは実際のコード変更の不変ログを保存し、rebase はその両方を少しずつ行う。
      両方を持てない理由はない。コンピュータをプログラムする人たちなのだから、望むように作れるはずだ。
      自分が Git を書き直すなら、コミットを2つの部分に分ける。変更履歴は Git のように Merkle DAG 風の構造で不変のままにし、別の関連データストアにコミットメッセージを保存して、作業の流れを説明する合理的で編集可能なログにする。機能タグを中心にコミットをまとめ、タイポを直し、メッセージを好きなように変えられるようにしつつ、下にある差分ログ、つまり「コードで実際に変わったこと」はそのまま保存すればよい。
    • git notes で追記することはできる。ただ、メッセージがあれほど長いと、誰かが気づく可能性は低そうだ。
  • 同意できない点が1つある。「すべてのコミット、特にこの程度の大きさのコミットに、このレベルの詳細を期待しているわけではない」という箇所だ。
    経験上、むしろ小さく無害に見えるもののほうが、相対的に長い説明を必要とすることが多い。
    昨日 gh pr list--limit=999 を追加した理由を3段落で書いた。紛らわしいからだ。--jq 引数の中にもすでに limit( があり、PR 全体が無限にあると仮定すると、値が高いほど最終結果は実際には少なくなる。
    コードコメントも書いたし、おそらく文章を書く時間より、考えて練る時間のほうが長かっただろう。誰かがこの仕事をコードを乱造することだとほのめかしたときには、次の例として覚えておいてほしい。

    • 小さく無害に見えるもののほうが長い説明を必要とする、という点には同意するが、リンク先のコミットメッセージは長すぎると思う。読者の時間を浪費するか、文字の壁のせいで目が滑る。発見した内容と理由を文書化するために、全行程を記録する必要はない。
      “This was a non-ascii whitespace character that caused ArgumentError: invalid byte sequence in US-ASCII when running bundle exec rake” くらいで十分だ。後で似た問題を検索するためのキーワードがあり、根本原因を含んでいて、長すぎないので人々が読み飛ばす可能性も低い。
    • 言語バインディングを追加するコミットなら、追加・削除が100行を超えていても “Add X function” 程度で済ませられる。すでに確立されたパターンに従っているだけだからだ。だがリンク先のような種類の変更なら、複数段落の説明は確かに有用だ。
    • コードコメントも似たパターンで書くようになる。「興味深い」コードの小さな核心部分には、コードに対するコメントの比率が 1:1 以上付き、そのおかげで残りのコードベースは、あえて多くのコメントを必要としない退屈で自己説明的なボイラープレートコードとして維持できる。
  • 優れた Git コミットではないと思う。
    あれだけ大量のテキストがある割に、最初の行 “Convert template to US-ASCII to fix error” はもっと良くできる。どの空白文字がエラーを引き起こし、そのエラーが何だったのかを数語だけ足せばよい。その説明と差分だけで、必要な文脈は十分だ。
    正直、残りはほとんど役に立たない。害はないが、価値も大きくない。作者はこのバグを追跡した道のりを文書化しているが、誰が気にするのかと思う。

    • 学び、より良いプログラマーになりたい人は気にする。実際、その記事は追加内容の価値を説明しており、それは気にする人がいるということだ。
      記事には、その修正から学んだ人たちが残した複数のコミットを示す検索結果へのリンクもある。
      そのコミットメッセージは知識の宝庫だ。
  • 優れたコミットメッセージを見たいなら、Linux カーネルの Git 履歴を見るとよい。そこではこれが標準だ。
    1行目は常に変更の影響を受けるサブシステムに言及し、続けて命令形の1行要約を置く。その後で、3つの質問にできるだけ詳しく答える。

    1. 現在の挙動は何か
    2. 何がこの変更につながったのか
    3. 変更適用後の新しい挙動は何か
      例としては「現在のコードは X を行う。テストケース T を実行すると、予期しない挙動 U が観察された。これは理由 R によるものだ。F を行って修正する」のように書ける。
  • 最近のコントリビューターから、私の Git メッセージの書き方、つまり要件が「独特」だと言われた。Linux カーネル出身であることがにじみ出ているようだが、私のコミットメッセージはどれもここにあるもののような形をしている
    既存コードに関する内容なら、コメントはコードと一緒にあるべきだ。プロセスに関する内容、たとえば削除されたコードや動作していなかったコードについては、コミットにあるべきだ
    最も重要なのは、コミットメッセージは便宜上 issue を参照してもよいが、重要な詳細は必ず再現しなければならない、という点だ。GitHub は一時的なものだが、Git メッセージはそうではない

  • 文脈がたっぷり詰まったコミットメッセージはこちらにある[1]
    こうしたことはあまりにもよく起きるため、メンテナーがこの文章を書いた[2]
    [1] https://github.com/git/git/commit/d70f554cdf38b0b05cfaa8e8eb...
    [2] https://lore.kernel.org/git/xmqqedevo8ps.fsf@gitster.g/