はじめに
TRIAL&RetailAI Advent Calendar 2025 の 5日目の記事です。
昨日は @kakine_juri さんの「ライティングソフトウェアを履修したい」という記事でした。
「ライティングソフトウェア」はアーキテクチャに関するなかなか難しい書籍ですが、分かりやすく解説してくれています。ぜひご一読ください!
保守性を高めるために「分かりやすいコミットメッセージを書きましょう」という話は誰しも聞いたことがあると思います。ただ、どういうコミットメッセージが「分かりやすい」のか、具体的に何を書けばいいのかで悩んだことがある人も多いのではないでしょうか。
絶対的な正解があるわけではないですが、保守で未来の自分(や同僚)が泣かないために、考え方のヒントになりそうな話を整理してみたいと思います。
良くないコミットメッセージとは
コミットメッセージは、後から読む人のために“差分では伝わらない情報”を補うためのものです。
それが達成できていない「良くない例」の典型的なパターンを整理してみましょう。
差分を見れば分かることしか書いていない
-
Parser.ktを更新しました - 変数名をxxxに変更しました
どのファイルを更新したのか、どの行をどう変更したのかは差分を見れば分かります。それだけだと、メッセージで補えている情報がほとんどありません。
ほぼ情報がない
- update
- fix
更新したからupdate、修正したからfixというだけだと何も書いていないのに等しく、新しい手がかりになる情報がありません。
どう困るのか
こういったコミットメッセージを書いていると、未来の自分がどう泣くのでしょうか。
例えば、ユーザへの通知機能を改修するために既存のコードを確認している状況を考えてみましょう。
fun sendNotification(userId: String, message: String) {
// この分岐はどういう経緯で書かれたのか?
if (userId.startsWith("M_") || userId.startsWith("X_")) {
return
}
notificationService.send(userId, message)
}
- ユーザーIDの先頭に
M_もしくはX_が付いていたら通知しないという謎の分岐が見つかった - Gitのログも見てみたが、コミットには
updateとしか書かれておらず、authorは退職済みの知らない人
残念ながらこういうケースは結構あります。
意味ありげに分岐が入っている以上、何らかの意図があったことは伝わりますが
- なぜこの分岐を入れたのか?
-
M_とX_はそれぞれ何を意味しているのか?同じ理由で除外している?別々の経緯? - 他にも特別な意味のあるプレフィックスがあるのか?
- 今回の対応でもこの仕様は踏襲すべきなのか?
これらを判断できる材料がほぼありません。
当時のことを覚えている人がいたり、仕様がドキュメントとして残っていれば(そしてメンテナンスされていれば)まだいいです。しかし現実にはそうでないことも多いでしょう。
コミットメッセージから意図が読み取れないと、「この分岐をどう扱ったらいいのか?」の調査や判断に多くの時間が奪われることになります。
コミットメッセージには「意思決定」を残す
ではどうすればいいのでしょうか?
最初にも少し触れましたが、コミットメッセージでは差分から分からない情報を補うことが大切です。変更の「理由」や「背景」、言い換えれば「どんな意思決定を行ったのか」を書き残しましょう。
たとえば:
- 期待する振る舞い(仕様)
- どのように振る舞うことが正しい(と考えた)のか
- 何を問題とみなし、どうなるのが正解だとみなしていたのか
- 採った方針と代替案
- 他にも選択肢があった場合、なぜ他ではなくこの変更を選んだのか
- 他の案を見送った理由が残っていると、同じ検討を繰り返さなくて済む
- 懸念点やリスク
- パフォーマンスを優先して可読性の落ちる書き方にした、などトレードオフにしたものがあれば残しておく
もちろん、これらすべてをコミットメッセージに書けというわけではありません。そのコミットで重要だった意思決定を残しましょう。
また、複雑な議論の詳細をコミットメッセージに残すのも現実的ではありません。そういった詳細はタスク管理ツールなどにまとめておき、コミットメッセージには概要だけ書いておくなどの使い分けも必要です。
差分を見れば分かることも書いていい
ここまで「意思決定を残そう」という話をしてきましたが、差分の概要を書くこと自体は何も問題ありません。
悪いのは差分を見れば分かること"しか"書かれていないことであり、「何を変えたか」の概要と「なぜ変えたか」の意図がセットになっていれば、コミット一覧からも探しやすく、かつ経緯も追える良いコミットメッセージになります。
具体的な改善例
では実際に、良くないコミットメッセージを改善してみましょう。
- 「Parser.ktを更新しました」
- "なぜ"、"どういう"更新をしたのか書こう
- e.g.「パース処理のパフォーマンス改善のため、正規表現をコンパイル済みのものに変更しました」
- 「変数名をxxxに変更しました」
- 何で変数名を変更したのか?が欲しい
- e.g.「変数の役割を適切に表している名前ではなかったので、より正確になるようリネームしました」
あとは、「理由を書いて」と言った時にありがちなパターンについても触れておきます。
- 「レビュー指摘により変更」
- それは変更理由ではなくきっかけ
- レビューで「こういう理由でこう変更したほうがいい」という議論があったはずだし、それに納得したから変更したはずなので、その判断を残す
- 「エラーが出たので修正しました」
- どういうエラーが出て、なぜ今回の修正で直るのかを教えて欲しい
- e.g. 「検索結果が0件の場合に
IndexOutOfBoundsExceptionが発生していたため、空チェックを追加しました」
プレフィックスも活用しよう
コミットの情報を増やす方法の1つとして、プレフィックスを付与することも挙げられます。
プレフィックスを付与することで、その変更が機能追加のつもりだったのか、バグ修正だったのか、リファクタリングだったのかといった意図を明示できます。
コミットメッセージで使うプレフィックスは、人によって、組織によって色々違うと思いますが、私が主に使っているのは下記のものです。
| prefix | 用途 |
|---|---|
| feat | 新しい機能やロジックの追加 |
| fix | 不具合修正など、既存コードの問題点を修正するもの |
| test | テストに関する変更 |
| doc | 関数の説明やコメント、Markdownファイルなどドキュメント関連の変更 |
| style | コードの見た目を変更(インデントや改行の変更など) |
| chore | ツールなどで自動生成されたものや、上記のどれにも当てはまらない軽微な変更 |
e.g.
fix: 検索結果が0件の場合に`IndexOutOfBoundsException`が発生していたため、空チェックを追加しました
理由が思いつかない場合はどうしたらいい?
これまでコミットメッセージに意思決定を残してこなかった人が、いきなり「理由を簡潔に書いてね」と言われても困るかもしれません。
その場合は、リポジトリのコミット履歴で他の人のコミットメッセージを読んで、どんなことを書いているのかを見てみましょう。最初はそれらを真似するのでもいいと思います。
また、「こういう理由でこんな変更をしたんだけど、コミットメッセージはどうしたらいいと思う?」と生成AIに相談してみるのもやり方の一つとしてありだと思います。
「理由が思いつかない」の注意点
一方で、「理由が思いつかない」のは言語化できていないのではなく、「変更内容を理解できていない」ことの兆候かもしれません。
典型的なのは「生成AIの提案通りに修正したらエラーが消えたけど、なぜ直ったのかは分からない」みたいな状況でしょう。
昔でいうところの「見つけたブログの通りに修正したら直ったが、なぜ直るのか分からない」と同じ状態ですね。
こういう場合、生成AIにコミットメッセージの案を出してもらうのはむしろ危険です。生成AIは結構「それっぽい」案を出してくれるので、理解していないままコミットすることを助長してしまいます。
もし理由が思いつかないと感じたら、理解できていない部分はないか自省することも大切です。
調べ直したり質問したりして、納得感と責任を持ってコミットを残しましょう。
最後に
「どう変えたのか」は差分をみれば分かりますが、「なぜ変えたのか」は意識して残さないと失われます。
未来の自分を泣かせないためにも、意思決定をコミットメッセージとして残すようにしましょう。
明日のアドベントカレンダーは、@mrsd さんの「🚀 リバースプロキシの良さと必然性を再確認する」です。
リバースプロキシは「なんとなく分かるけど人に説明しろと言われると自信がない」という状態なので、個人的にもとても楽しみです!
Retail AI と TRIAL ではエンジニアを募集しています。
この記事を見て興味を持ったという方がいらっしゃいましたら、ぜひご連絡ください!