Gitのコミットログに詳細を書く習慣をつけている

おはこんばんちは、SREの橋本です。この記事は、freee Developers Advent Calendar 2021の16日め記事となります。

わたしがソフトウェアエンジニアとして仕事をするうえで、コミットログを詳細に記述する習慣づけがあり、この機会にその具体例をあえて共有してみます*1。以降はとくに明示しない限り、組織全体でルールがあるわけではなく、あくまでわたしの一個人の意見である点に注意してください。

モチベーション

freeeでは、Webサービスからインフラ基盤およびその監視設定を含めてコードで管理されており、GitHub上でのPull Requestでのレビューを必須としています。わたし自身は社内の立候補制異動制度*2によってWeb開発の現場とSREを行き来してきましたが、どちらもリファクタリングのためにゼロベースでコードを書き直すこともあれば、機能追加やバグフィックスのために数年前のコードに手を入れることもあります。目的にもよりますが、肌感として既存サービスにおけるWeb開発では後者のパターンが多いです。

機能追加やバグフィックスにおいて既存コードを編集する場合、すぐにコードを書いて動作確認する場合と、過去になぜそのような実装にしたのかを探す場合があります。とくにバグフィックスにおいては既存の関数やメソッドに渡す引数や、想定外の挙動を回避するためのトリッキーな実装になっている場合には注意を払います。

これらは明らかに意図した実装のようにみえますが、実際にそうなのかを確かめるため、git blameの実行やそこからPull Requestをたどって歴史を掘り起こしたり、はたまたSlack検索の旅に出ることになります。しかし、検索しても実装経緯が見つからないことの方が多く、かつ実装者が退職していたり履歴が古いと実装経緯があやふやになり、手出ししようがないといったケースに直面することもまれにあります。

この事象はわたしが他人の書いたコードに対して思うこともあれば、わたし自身が書いたコードについても当てはまります。「ここはなぜこのような挙動になっているのか?(仕様かバグかを確認している)」「なぜこのパラメータはこの値に設定したのか」といった背景はそこに強い印象がない限り数ヶ月で完全に忘れてしまい、答えに詰まることもありました*3

これらの反省から、こと細かく作業ログをつける意識が生まれました。そして、その記録を読み書きしやすい場所としてGitのコミットログへ行き着きました。後述しますが、長文の実装メモを残す場合は別途Slackやドキュメントに書くこともあります。

最近のコミットログの例

わたし自身が読んで困らないか・誰が読んでも困らないかの観点があると思いますが、以下を書いていれば少なくともわたし自身が困らない状態です。

  • 実装内容の1行サマリ
  • 実装経緯となったSlackの会話履歴、ドキュメント、チケットのURL
  • なぜそのような変更をしたのか、どのような結果が期待されるかなど

コミットタイトル:xxxのメモリ消費を抑える。以下に文字起こしを示しています。
過去のコミットログの一例

🚀 hogefugaのメモリ消費を抑える

https://foobar.example.com/xxx/yyy

xxxの場合において、対象事業所が多い場合にこのメモリ消費がギガバイトオーダーまで増加していた。

Foobarbaz.in_batchesでFoobarbazを細切れに見ていくことで、このArrayを小さくし、メモリ消費を抑えることが期待される。

in_batchesのバッチサイズはデフォルトで1000件。この件数の変更はバッチの実行時間やin_clause_lengthと調整する必要がある。

実装内容の1行サマリ

実装内容の1行サマリでは、なにを実装したのか簡単な概要を書いています。機能追加、リファクタリング、パフォーマンス改善、バグフィックスなどです。 freeeのいくつかのリポジトリではgitmojiとgit commitの際にコミットログにいい感じにgitmojiをつけられるgitmoji-cliの導入がなされており、emojiの種類によってどのような変更がそのコミットでなされたのかが一目でわかるようになっています。ざっくりSemantic Commit Messagesのemoji版のイメージです。freeeにおいては、emojiの個数を集計してバグの傾向分析に使われたり、チームによってはそれ以上の分析も行なわれているようです。

gitmoji.dev

github.com

gitmojis.jsonにemojiとその意味のマッピングを定義します。

gitmojis.jsonの一例:

[
  {
    "emoji": "👍",
    "code": ":+1:",
    "description": ":+1:機能改善"
  },
  {
    "emoji": "♻️ ",
    "code": ":recycle:",
    "description": ":recycle:リファクタリング"
  },
  {
    "emoji": "❤️ ",
    "code": ":heart:",
    "description": ":heart:バグ修正"
  },
  {
    "emoji": "🚿",
    "code": ":shower:",
    "description": ":shower:不要な機能・使われなくなった機能の削除"
  },
  {
    "emoji": "🚀",
    "code": ":rocket:",
    "description": ":rocket:パフォーマンス改善"
  },
  ...
]

実装経緯の詳細

実装経緯となったSlackの会話履歴やドキュメント・チケットのURLはコミットログの3行目に書いています。これの有無で調査コストが大きく変わってくる印象です。URLがない場合はコミットログからPull Requestをたどり、Pull Requestのタイトルや番号からSlackやドキュメントを検索し.....という手順を踏むことになります。
GitHubではコミットログからPull Requestに1クリックで遷移できるため、Pull Requestに経緯となったそれらのリンクを書くこともできますが、Pull Requestよりこまかい粒度でのコード変更に関する議論も発生しうるため*4、コミット単位でリンクを書くようにしています。

リンク先は組織内のメンバがだれでも見られるツールがのぞましいと思います。一部のメンバしか見られないツールだと、そもそも実装経緯が見つからないのと同義だと感じるためです。明らかに限られた人しかコードを読み書きしないケースであればさほど問題にならないと思われるので、ここはチーム間で調整したほうがよさそうです。
ドキュメントは、Google Docsに実装仕様をまとめることが多いですが、必要に応じてZeplinのようなデザインデータや、分析・監視におけるダッシュボードのURLとなることもあります。
チケット管理は、機能開発においてはJiraやGitHub Issueなどチームによって使い分けられるケースが多いですが、バグレポートはお客様や社内からいただいた問い合わせはテンプレートに沿った内容でJiraで管理されています。

なぜそのような変更をしたのか・どのような結果が期待されるかなどは、文章で説明します。前述のURL先ですでにそういった議論や仕様書があればコミットログで詳細に書くことは少ないです。「このような使い方を想定している」といった場合にはサンプルコードを書いたり、いろいろ試した結果現在の実装に落ち着いたという場合には試した過程を書くこともあります。
TODOFIXME、またはこの設定にしないとまずいといった強いメッセージがある場合はコードコメントとして書いています。

まとめ

わたしの仕事の習慣のひとつとして、コミットログに概要・資料や経緯などのURL、より具体的な説明を書く話をしました。読者のみなさんのこだわりもぜひ教えてください!

*1:freeeのマジ価値行動指針のひとつに「あえて、共有する」というものがあり、人となりやチームを知るためにささいなことでもあえて共有してみる文化があります

*2:エンジニア戦国異動とよばれます。詳細は10分でわかるfreee エンジニア向け会社説明資料を参照ください。

*3:個人開発だけであれば頭の中にのこり続けることのほうが多いです。

*4:そもそもPull Request自体を小さくした方がよいという話もありそうですが。