こんにちは。ぐるなびウエディング開発チームの渡邊です。普段はバックエンドエンジニアとして開発・運用を担当しています。
ぐるなびウエディングのリニューアルでは、これまでに次の3本の記事を公開してきました。
- PHPのレガシーシステムをTypeScriptで刷新! フロントエンドとバックエンドの職能の壁は壊せるのか?
- 毎週大量のプルリクエストを捌く!Next.js App Router時代のスケーラブルなフロントエンドアーキテクチャ設計
- Turborepo × pnpm で実現するモノレポ開発基盤
これらの記事では、言語統一、フロントエンド設計、モノレポ開発基盤といった土台の話を扱ってきました。 今回はその続きで、構築した基盤をどう運用してきたかの話です。
テーマは、開発を続ける中で地味に困っていたことをどう減らしてきたか、です。 PR に何を書くかが人によって揺れる、変更内容とレビュー先の対応を毎回確認しなければいけない、緊急度の違う更新が同じ流れに来る——そういった類のことです。
この記事では、こうした課題に GitHub のどの機能やワークフロー設計を使ったのかを、運用の意図とあわせて紹介します。 実装の手順を追う記事ではありませんが、設計のヒントが伝わるように、効果の大きかった工夫を絞って取り上げます。
目次
- 目次
- Pull Request では what より why を残す
- 責務とレビュー先の関係を GitHub 上に置く
- 更新の種類ごとにレビューの扱いを分ける
- 自動化は、止まったときの検知まで含めて設計する
- リリースは部品化しつつ、承認は1回にまとめる
- 見える化は、競争ではなく状態共有に絞る
- まとめ
Pull Request では what より why を残す
最初に見直したのは、Pull Request(以下、PR)に残す情報です。
コード差分を見れば、何を変えたのかはある程度わかります。 一方で、なぜその変更が必要だったのか、今回は何をやらなかったのか、どこを重点的に見てほしいのかは、差分だけではわかりません。
そこで pull_request_template.md では、少なくとも次の項目を持つようにしました。
### 背景 ### やったこと ### やらなかったこと ### レビューポイント
この形にした理由は、レビューに必要な判断材料を残すためです。
- 背景があれば、なぜ今その変更をするのかがわかる
- やらなかったことがあれば、今回のスコープがわかる
- レビューポイントがあれば、見るべき観点がそろう
最近は生成 AI を使って実装速度を上げやすくなりましたが、その一方で、差分から why を正確に読み取るのは簡単ではありません。
責務とレビュー先の関係を GitHub 上に置く
モノレポでは、変更内容と責務の対応関係が複雑になりやすくなります。 今回の修正がフロントエンドの責務なのか、バックエンドなのか、共通設定なのかを毎回人が思い出しながら判断するのは、正直かなり面倒でした。
私たちのリポジトリでは、ブランチルールで CODEOWNERS のレビューを必須にしています。
そのうえで、CODEOWNERS に責務の境界を書き、変更ファイルに応じてラベルを自動で付ける仕組みも入れています。
実際の設定はもう少し細かいですが、考え方は次のような形です。
# .github/CODEOWNERS apps/web/ @team/frontend apps/api/ @team/backend packages/ui/ @team/frontend infra/ @team/platform
# .github/labeler.yml 'app:web': - changed-files: - any-glob-to-any-file: - apps/web/** - packages/ui/**
更新の種類ごとにレビューの扱いを分ける
依存関係の更新も、単に自動化すればよいわけではありませんでした。
私たちのチームでは、通常の依存関係更新は月1回の手動更新に寄せています。 一方で、セキュリティアラートのような緊急度の高い更新は別枠で扱いたい。 つまり、すべての更新を同じ優先度、同じ導線で扱いたいわけではありませんでした。
そのため、dependabot.yml では対象を絞り、通常更新の一部は別の運用に分けています。
さらに、bot が作成した PR については、自前の PR 整理用ワークフローで reviewer の扱いを調整し、人の PR と同じように reminder やレビュー要求の流れに残り続けないようにしています。
Security Alerts 以外の更新まで毎回レビューを催促されると、以下の運用に合わなかったからです。
- 緊急度が高い更新はすぐ対応できるようにする
- 通常の更新は月次の更新作業の中でまとめて扱う
- 通常の bot PR が、人のレビュー優先度を乱さないようにする
bot PR から reviewer を外す処理はかなりニッチな対応ですが、 月次の更新運用と毎時のリマインダー設定がうまく噛み合わなかったので、仕方なく作りました。
自動化は、止まったときの検知まで含めて設計する
cron で動くワークフローがどこかで止まっていても、しばらく気づかないでいた、ということが何度かありました。 merge queue も、キューに積まれた時点で安心してしまうと、失敗に気づくのが遅れます。その結果、マージが遅れたり、後から対応することになったりします。
そこで、自動化を設計するときは、成功時の処理だけでなく、失敗や滞留をどう検知するかもセットで考えるようにしました。
たとえば、次のような仕組みを入れています。
- cron で動くワークフローが失敗したときは Slack に通知する
- merge queue で失敗したときも通知する
- conflict が発生している PR には CI の中で通知する
ただし、通知用のワークフローを増やせばよいわけでもありません。 PR の競合確認や merge queue 失敗通知のように、メインの CI と同じ文脈で見た方が判断しやすいものは、同じワークフローの中に置いています。
リリースは部品化しつつ、承認は1回にまとめる
複数アプリケーションを含むプロジェクトでは、リリース時の操作そのものが運用負荷になります。 一つひとつのワークフローは単純でも、AWS CDK を含む複数のアプリケーションを対象にすると、個別に起動するだけで手数が増えます。
私たちも実際、アプリケーションごとにワークフローを個別に起動するのが単純に面倒でした。
そこで、各デプロイ処理は workflow_call で再利用可能なワークフローとして分けつつ、入口はリリース用のオーケストレーションワークフローに集約しました。
この構成で特に重要だったのが、GitHub Actions の environment を「デプロイ先」ではなく「承認ゲート」として使うことでした。
そのまま production 向けの各 deploy job に承認必須を設定すると、複数アプリケーションをデプロイするたびに job ごとに承認が必要になります。
そこで、承認専用の approval environment を先頭の job に置き、その承認が通った後に、後続で複数アプリケーションのデプロイを進める構成にしました。
実際の構成はもう少し複雑ですが、考え方は次のような形です。
jobs: approve: environment: approval deploy-api: needs: approve uses: ./.github/workflows/deploy-api.yml deploy-web: needs: approve uses: ./.github/workflows/deploy-web.yml
この構成を使い始めてから、リリース時の承認が1回で済むようになりました。
見える化は、競争ではなく状態共有に絞る
リポジトリの状態を週次で見える化する取り組みも続けています。 マージされた PR の数、変更の傾向、放置されているブランチなど、見ようと思えばいくらでも集計できます。
ただ、見える化できることと、それを前面に出すべきかは別の話です。 たとえば個人ごとの件数やランキングは作れますが、それを継続的に見せることがチームにとって良いとは限りません。 役割も担当も違う中で、同じ指標だけを並べると、必要以上のプレッシャーを持ち込むことがあります。
そのため、週次レポートでは、競争につながる指標ではなく、リポジトリの状態を把握するための情報を中心に扱っています。
以前は issue-metrics を使っていましたが、取れる情報が多い分、集計も重くなります。
必要な情報はそこまで多くないため、いまは以下の要素を gh コマンドと jq で収集するカスタム実装に切り替えています。
- どの種類の変更が多かったか
- bot と人の PR がどのくらいあったか
- stale branch が残っていないか
件数を競うためでなく、状態を確認して話のきっかけにする。その程度の使い方が、いまの自分たちには合っています。
まとめ
この2年間で GitHub 周りに対してやってきたことを振り返ると、 設定項目を増やしたかったというより、運用の曖昧さを一つずつ減らしてきたのだと思います。
こうした曖昧さは、一つひとつは小さく見えても、日々の開発の中では無視できません。
もし同じようにリポジトリ運用を見直したいときは、まず設定一覧を見るより、 「今、何が曖昧なのか」「何がノイズで、何を優先したいのか」を言葉にしてみるのが近道だと思います。 必要な仕組みは、その後に決まっていきます。
