より良いコミットを書き、より良いプロジェクトを構築する
こんにちは、クリエイティブセクションの長谷川です。
最近は段々と暑くなってきており、外に出るのが億劫になってきました。
それでも趣味の釣りはやめられないので、暑さに耐えながら頑張っています。
さて、本日は先日Githubの公式ブログにて掲載された記事について紹介したいと思います。
記事のタイトルは「Write Better Commits, Build Better Projects」。
翻訳すると「より良いコミットを書き、より良いプロジェクトを構築する」です。
詳細は以下からお読みください。
何気なく運用しているgitですが、改めて読んでみるとなるほど!と思うところが
いろいろありますので、要約していきます。
以下のように感じたことはありませんか?
- このコードに何の意味があるのだろう?
- このオプションは非推奨ではないのか?
- このコメントはあっているのだろうか?見ている内容と違う・・・
- この巨大なプルリクエストはどこから手を付けたらいいんだろう?
- このバグはどこから来たんだろう?
これらの疑問は、共同開発されたソースコードのコミュニケーション媒体としての限界を反映しています。
コードコメントやスタイルガイド、文書化要件など上記の問題を軽減する方法はありますが、それでもコードを理解しようとした際には多くの時間を費やしてしまいます。
これに対してGithubは、これらの問題を解決するツールは、ずっと前からgitにあるとよいうこと述べています。
コミットとは、単なる保存ポイントやログではない
Gitリポジトリのコミットは、単に保存ポイントや大きなプロジェクトの進捗を示すログと言うだけではありません。
GitHubの”Git Guides“によると、次のようにあります。
[コミット] は、特定の時間におけるリポジトリ全体のスナップショットです…論理的な変更の単位に基づきます。時間の経過とともに、コミットはあなたのリポジトリの歴史と、それがどのようにして現在の形になったかを物語るものになるはずです。
コミットは、各コードがどのように、そしてなぜ生まれたかを記録する歴史的なものです。
さらに、コミット時にコメントを付与することが出来るため、上手に活用することで、開発者がコードを説明し理解するための最高のツールになります。
以下はGithubが紹介する、より良いコミットの書き方のガイドラインになります。
物語を構成する
Gitリポジトリのブランチには、様々なコミットが含まれます。
- コンポーネントAのコミット、コンポーネントBのコミット、コンポーネントAの仕上げのコミット
- 以前のコミットのtypoを修正するコミット
- メインブランチとのコンフリクトを解消するためのマージコミット
- レビューのフィードバックに対応したコミット
1つのブランチに含まれるコミット履歴は、ある種のストーリーを物語ります。
しかし、明確なストーリーを持たない無秩序なコミットは、レビュアーと開発者の双方に影響を与えます。
解決策として「物語の概要を説明し、それにあわせてコミットを再編成する」です。
gitのrebaseという機能を用いてコミット履歴を編集します。
詳しい説明は以下のURLを参照していただきますが、これを用いることで、以下の図のように再編成することができます。
【参考】 Zenn – git rebaseで、コミットの順番を入れ替える!
https://zenn.dev/koniman/articles/56512abd73ad8d0c65bd
コミットのサイズを変更して安定させる
コードレビューを行う際、プルリクエストに大量のコミットや変更が含まれる場合は、コミット単位でレビューを行うことがあります。
しかし、その1つのコミットの変更量が多すぎたり、逆に少なすぎたりすると、レビュアーは混乱したり、誤解されたり、単に見落とされたりする可能性が出てきます。
解決策は「各コミットを「スモール」と「アトミック」の両方にします。」です。
アトミックは不可分を意味します。これはどういうことかというと、1つの意味をなす実装において、例えばWIP(Work in progress)のコミットとその後の完了コミットの2つに分かれている場合は、後から見たときに意味もなく分割されていることになります。それらは本来分けてはならないものであるため、不可分とみなします。
逆に複数の機能実装などが、1つのコミットに複数の目的が含まれ、巨大化したものを分割することがスモールになります。
背景を説明する
コミットメッセージは非常に価値のあるものでありますが、しばしば見落とされがちな要素でもあります。
しかし、コミットメッセージが曖昧な場合、レビュアーや後々の開発者自身でさえ、変更した内容がわかりにくいものとなってしまいます。
例として、「〇〇の不具合の修正」や「レビュー対応」などです。
そこで「コミットメッセージには、何をしているのか、なぜそれを行っているのかを記述」します。
コミットメッセージは、読者に向けて書くのですから、その内容は、読者が理解スべきことを明確に伝えるものでなければなりません。
開発者であるあなたは、その背景や実装を十分に理解した上で、それらを説明する必要があります。
「何を」「なぜ」をさらに細かく分類すると、高レベルと低レベルの詳細があり、これらはすべてコミットメッセージで答えるべき4つの質問として構成することができます。
何をしているのか | なぜそうするのか | |
---|---|---|
高レベル (戦略的) | 意図 これは何を達成するのか | 背景 なぜそのコードは今そうなっているのか |
低レベル (戦術) | 実装 目標を達成するために何をしたか | 正当化 なぜこのような変更を行うのか |
たとえば、以下のようなコミットメッセージがあったとします。
ユーザーに--grayオプションのスペルを使用させる
このメッセージは、意図の曖昧な説明のみであり、変更がどのように実装されたか、またはこの変更が必要な理由の説明が完全に欠落しています。
コミットを修正するときは、「何を」と「なぜ」のセクションを次のように並べ替えます。
- 意図(タイトルとして)
- 背景
- 正当化
- 実装
上記にそって、先程の文章を以下のように修正します。
`--gray`オプションを`--grey`のエイリアスとして追加
ユーザーが画像修正スクリプトにオプション`--gray`(有効なオプション`--grey`ではなく)を指定した場合、エラーが発生します。
image-modifier.py: error: unrecognized arguments: --gray
"grey"と"gray"は同じ単語の共通の綴なので、`argparse`の引数定義に`--gray`を追加して、グレイスケールへの変換操作を示すことを許可します。
上記のメッセージは、読者が必要とするすべての背景をカバーしていますが、少し過剰な説明になっています。
そのため、より簡潔で、なおかつ十分に説明されているよう、次のように変更します。
`--grey`のオプションエイリアス`--gray`を`argparse`の引数定義である`--grey`の別名として追加します。
これにより、ユーザーはオプションに共通のスペルを指定できます。
より良いプロジェクトの構築
上記で確立されたガイドラインを使用すると、コードレビュー、バグの発見、根本原因分析など、一般的なソフトウェア開発タスクの課題を軽減できます。
コードレビュー
コミット単位で変更を評価できるようになれば、どんなに大規模なプルリクエストでも管理しやすくなります。
以下のような手順で、確認していきましょう。
- プロリクエストの説明とコミットのリストを読んで物語を判断。
分かりづらい内容になっていれば、コメントを残して説明や変更を求める。 - 各コミットのメッセージと内容を、ブランチの先頭から軽く目を通す。
コミットのサイズが適切であるかをチェックし、正しくない場合は分割や結合を推奨する。 - 各コミットを徹底的に読む。
まず実装が意図と一致してるか。次にコードが記述されている実装と一致しているか、コミットメッセージがコードを十分に説明しているかをチェックする。
必要な情報が欠けている場合は、作者に説明を求める。 - 最後に、コミットの変更点と全体的な説明の完全なメンタルモデルを使用して、コードが効率的でバグがないことを確認する。
gitbisectでバグを見つける
git bisectはGitに組み込まれたツールで、既知の良いコミットと既知の悪いコミットがあったときに、その間のコミットをバイナル検索してどちらがエラーを起こしたかを探します。
これに関しては、以下がわかりやすいかと思います。
Qiita – git bisect で問題箇所を特定する
https://qiita.com/usamik26/items/cce867b3b139ea5568a6
根本原因の分析
バグを見つけた際、運が良ければ、根本的な問題は明らかであり、すぐに修正できます。
しかし、多くの場合は、それほど単純ではありません。
バグの原因となるコードは、別の機能に必要な場合や、表示されているエラーの原因としては意味がない場合があります。
コードが作成された理由を理解する必要がありますが、そのためにはコミット履歴を調査します。
これにはgit blameやgit logを使用します。
まとめ
主観的で、定量化が難しい場合もありますが、コミットの品質は、古いプロジェクトでも新しいプロジェクトでも、大規模でも小規模でも、オープンソースでもクローズドソースでも、開発者の生活の質に大きな違いをもたらす可能性があります。
コミットの改良を独自の開発プロセスの一部にするために従うべきガイドラインは次のとおりです。
- コミットを物語にまとめます
- 各コミットをスモールとアトミックの両方にします
- コミットメッセージで変更の「内容」と「理由」を説明します
さいごに
今回Githubの「Write Better Commits, Build Better Projects」という記事を読み、私自身確かに・・・と思わされました。
今まで、一旦別ブランチを見たいがために途中でコミットしたり、ついつい1つのコミットに複数の実装を含めてしまっていることがありましたが、レビュアーの視点で見た場合、相手が何を実装したのかを理解するのに非常に苦労していることを思い出しました。
これを機に、適当にコミットしたり、それを放置するのではなく、より良いプロジェクトの構築を心がけたいと思いました。
今回は少し長くなりましたが、このへんで終わりたいと思います。
ここまでお読みいただき、ありがとうございました。