こんにちは!LIFULLエンジニアの吉永です。
最近、個人としてもチームとしても意識して取り組み始めた、Conventional Commitsについてと、このコメント書式を採用した場合のメリットについて紹介したいと思います。
Conventional Commitsとは?
基本的には上記のサイトにて解説されていることが全てですが、Gitでコミットする際のメッセージの書式を定義し、機械と人間どちらから見ても解釈がしやすいコメントにしていきましょうというものになります。
あくまで書式であり、かつ割と緩い書式なので、導入するにあたってのハードルはそこまで高くありません。
私の個人的な理解としては、Javadocを代表とする、各言語毎に用意されているソースコード内のコメントからドキュメントを生成する為のコメント書式と同じで、従うことで得られるメリットがあり、かつ書式に従うデメリットは基本的にないものと思っています。
Conventional Commitsに準拠したコメント例
こんな感じになります。
fix: xxxのバグを修正
feat: xxx機能を追加
基本形としては上記の二つなのですが、 Angularの規約を元にした下記の規約の型も私達のチームでは採用しています。
ci: リリース時の自動タグ付けワークフローファイル追加
refactor: Go Modulesに対応する為の修正
Conventional Commitsを意識し始めて気づいたこと
プライベートのQiitaアカウントで過去に下記の記事を書いたことがあります。
関数コメントを付けたりつけなかったり、もしくはI/Oの仕様記述方法が人により違ったり、設計資料と整合性がとれていなかったりなどなどの課題があり、コメントはどうせ書かなければいけないのなら資産になるコメントを書こうと思い立ったのが使い始めた理由です。
Conventional Commitsを意識して普段のコミットコメント書くようになってから、以前書いた上記の記事のコメントはどうせ書かなければいけないのなら資産になるコメントを書こう
に通じているなと思い出しました。
※ちなみに現在の業務でのメイン言語はGoなのでDoxygenは最近全く使ってないw
ちょっと誤解を生みそうなので、補足しておくと、Conventional Commitsに従っていないコミットコメントは資産ではないと非難する意図はありません。
あくまで、同じ時間をかけて同じような作業をするのであれば、分析したりビジュアライズしたりができるツールが期待している形式に従った方が資産になりやすい、そう思っています。
※同様にソースコード内のコメントも所定のドキュメント生成ツールに準拠した物じゃないとダメだとも思ってはいません。従った方が良いとは思っているけど。
Conventional Commitsに準拠する事のメリット
最後にConventional Commitsに準拠することを意識してから気づいた、準拠する事のメリットを紹介します。
リリース時に自動でタグ付けする際のバージョン管理が楽になる
元々、私達のチームではリリース時に実行しているCIでソースの差分からセマンティックバージョニング形式のタグを出力していました。
※以前Qiitaにも投稿した下記の記事
しかし、上記の手法ではメジャー、マイナー、パッチそれぞれをインクリメントするロジックを自分達で作っていますが、実際に運用してみると全ての場面で全員が納得感のあるバージョンタグになっていたかというとちょっと微妙でした。
そこで、Conventional Commitsに準拠しようと意識し始めた頃に、チームのメンバーが下記のGithub Actionsを見つけてくれたので、採用することにしました。
こちらに関しては、詳細な解説がクラスメソッドさんの記事で紹介されていたので、こちらも参考にすると良いと思います。
このワークフローを取り入れると、リリース時の差分に含まれるConventional Commitsに準拠したコメントからセマンティックバージョニングに準拠したバージョンタグを出力してくれます。
fixならパッチ、featならマイナー、perfもしくはフッターにBREAKING CHANGEがあるならメジャーが上がるといった感じです。
バージョンタグに加えて、リリースノートも自動でいい感じに出力してくれます。
※下記は私のプライベートで使っているリポジトリです。
こんな感じで、コミットコメントからそれぞれの見出しとコミットコメントのdescription部分とコミットハッシュへのリンクが出てきます。
手間がかからずにリリースノートが出来上がってくれて、かつバージョンタグもある程度納得感のある形で管理してくれるので、このワークフローを採用する為にConventional Commitsに準拠しても良いかもと思うくらい、気に入っています。
コミットの粒度が個人でばらつきにくくなる
Conventional Commitsに準拠しようとすると、自ずとコミットの粒度がtypeで定義されているものと関連している修正というようになっていきますし、descriptionであまりにも広範囲のファイルや機能を横断してのコメントも書きづらいので、スコープも狭まっていきます。
Conventional Commitsを意識していなかったころのコミットは正直作業の切りが良いところで一旦セーブするみたいな感覚でやってしまっており、粒度もばらばら、コメントは後から見返してもあまり意図が読めない意味のないもの、と言った感じでした。
なので、PRに含まれる変更行数も多くなりがちで、レビューアの負荷を軽減する為にPRの変更行数の上限の目安を決めて、小さくしていこうという動きをしていたりもしました。
ただ、Conventional Commitsに準拠するとレビューをコミット単位で行いやすくなり、複数要素が入り混じったPRでもレビューアの負荷はそこまで高くないように思います。
※まぁそれでも限界はあるので、変更行数の上限目安っていうのは引き続き意識はした方が良いとは思ってますが
合わせて、同じ文脈同士のコミットは一つにまとめようという意識にもつながりました。
例えば、リファクタをしている最中に、同じ文脈で修正すべき個所の漏れに後で気づいて、下記のような履歴でコミットしたとします。
1 refactor: xxxクラスのメンバーでpublicにする必要のないものをprivateへ変更
2 fix: xxxバグを修正
3 refactor: xxxクラスのメンバーでpublicにする必要のないものをprivateへ変更する際の作業漏れ追加
上記の場合、1と3のコミットはマージ前であれば一つにまとまっているべきなので、リベースしてまとめた方が良いです。
また、レビューでは何かしらの指摘をいただき、追加で修正コミットをする場面が多いと思いますが
1 refactor: xxxクラスのメンバーでpublicにする必要のないものをprivateへ変更
2 fix: xxxバグを修正
3 fix: xxxクラスのメンバーでpublicにする必要のないものをprivateへ変更する際の作業漏れ指摘対応
同様にレビュー中にもらった指摘に対するコミットも、その修正が本来一緒に含まれるべきコミットにまとめるべきなので、こちらも1と3はリベースした方が良いと思います。
※ちなみに、レビュー中の修正コミットをリベースするタイミングは組織によってもまちまちになりそうですね。私達のチームはレビュー指摘の修正コミットはそのコミットで再レビューしてもらった方がレビューアの負荷が少なそうだということで、レビュー完了後にリベースしています。もちろんレビュー前の時はローカルでリベースしてからレビューには出しますが。
まとめ
いかがでしたでしょうか?
個人的にはConventional Commitsをもっと早く知っておけば良かったなぁと思うくらい、普段の開発の意識が変わりましたし、私はレビューをする機会も多いので、メンバーがConventional Commitsに準拠した形でレビュー依頼してくれて、レビューしやすいなというのを実際に体感もできているので、双方によって良い効果をもたらす、とても良い書式だなと思います。
最後までご覧頂きありがとうございました!
また次の記事でお会いしましょう!