はじめに
UPSIDER の 支払い.com でバックエンドエンジニアを担当している水村です。今回は支払い.comのブランチ戦略とテスト環境を見直して改善している件をまとめました。
最近のチーム構成
技術境界でチーム分けをしています。フロントエンド、バックエンドをそれぞれ専任で開発しており、OpenAPI によるスキーマ駆動開発を行っています。
現在(2023年11月22日時点)は以下のチーム構成です。
- フロントエンド4名(フルタイム3名)
- バックエンド4名(フルタイム2名)
3ヶ月前までは以下のチーム構成で開発をしていました。
- フロントエンド2名(フルタイム1名)
- バックエンド3名(フルタイム1名)
小規模なチームのため、ブランチ戦略やテスト環境による悩みはそれほどなかったのですが、幸いなことに正社員でフルタイムのメンバーが増えてきたことにより、今までそれほど問題ではなかったことが徐々に問題となってきました。
開発の流れ
以下のような流れでブランチを作成し、本番リリースを行なっています。Git-Flow に則った開発フローです。
- develop ブランチから feature ブランチを作成する
- feature ブランチから Pull Request を作成する
- Pull Request をレビューし問題なければ develop ブランチにマージする
- develop ブランチにマージされたタイミングで develop ブランチがテスト環境に自動でデプロイされる
- テスト環境でテストする
- リリースしたいタイミングで develop ブランチを main ブランチにマージする
- GitHub の画面上からタグを作成して main ブランチをリリースする(フロントエンド、バックエンドは個別にデプロイ可能)
ブランチ戦略とテスト環境の課題
支払い. comは初期から git-flow で開発を行なっています。テスト環境は1つで、develop ブランチにマージされると自動でテスト環境にデプロイされます。ブランチ戦略については何度かチーム内で話し合いを行なっており、将来的には GitHub-Flow に変わるかもしれません。現在は以下の課題を認識しているので改善を行なっている最中です。
- 大きめの機能を develop にマージした場合、テストが終わるまでの間は develop にマージできない場合がある
- テスト環境が1つしかなく、並行してテストがやりづらい
リリース初期から開発メンバーがあまり増えていなかったことと、大きめの機能リリースが少なかったことから先ほどの問題があまり重要視されていませんでした。ですが、8月ごろからエンジニアが増え、開発する機能数も増えてきたため、ブランチ戦略などの見直しを行った方が良さそうとなりました。
ブランチ戦略見直しと複数のテスト環境をデプロイする
先ほどの課題を同時に解決するため、最近は次の対応をおこなっています。
※前提として、支払い. comのフロントエンドは Next.js を Firebase Hosting にデプロイしています。バックエンドは Server-Side Kotlin を Cloud Run にデプロイしています。
- 大きめな機能は feature ブランチとは別に scope ブランチを作成し、feature ブランチを scope ブランチにマージする
- Firebase Hosting の preview 機能を使って Pull Request ごとにフロントエンドのテスト環境を作成する
- Cloud Run の deploy preview 機能を使って Pull Request ごとにバックエンドのテスト環境を作成する(こちらについてはまだ完全にできているわけではないので対応中)
これらの対応によって、develop ブランチが長期間占有されてしまうという課題を解消し、複数の機能を並行してテスト可能になると考えています。まだ全ての機能が対応できているわけではないですが、一部の機能については develop ブランチとは別のテスト環境を用意して並行でテストができるようになりました。
develop ブランチから main ブランチに対して 特定の機能のみを cherry-pick してリリースするという案もありましたが、cherry-pick でのリリースは失敗するリスクが高いと判断したためやめました。
scope ブランチを作成するデメリットは develop ブランチへのマージが遅くなることです。develop へのマージが遅くなることによりソースコードのコンフリクトが発生する確率が上がります。よって、scope ブランチは定期的に develop ブランチをマージし、コンフリクトが発生しないようにした方が良いかもしれません。また、scope ブランチが長期間存在しないようにする工夫が必要になるかもしれません。
バックエンドのテスト環境を複数デプロイできるようになりましたが、テスト環境が接続するデータベースは1つにしています。理由は、複数データベースをセットアップするのが結構大変であること、銀行振込などに使用している外部サービスが指定した IP アドレスのみの接続を許可しているため、全部の機能をテストできるのは develop ブランチがデプロイしたテスト環境のみという制約があるためです。
バックエンドの Cloud Run コンテナは Cloud NAT を使用して外部 IP アドレスを固定しています。複数デプロイした Cloud Run の外部 IP アドレスを同じものにするにはもう少し工夫が必要そうです。
本番リリースの方針
今の所あまりルールはないですが、基本的に以下の方針でリリースしています。
- 週末、深夜、土日は避ける
- ただしシステム停止が必要な機能は利用者の少ない週末などにリリースすることを検討する
Cloud Run は割といい感じにシャットダウンしてくれるので、日中にリリースすることが多いです。データベースのマイグレーションは手動で行なっています。データベースのマイグレーションを自動にした場合、思わぬ不具合が発生することがあるので念の為手動で行うようにしています。データベースのマイグレーションが伴う本番リリースについても、本番環境の影響を考慮しなくても良いと判断できるものについてはシステムを停止せずにリリースするようにしています。
フロントエンドに関しては、画面のみの軽微な修正などに関してはバックエンドのことを気にせずにリリースしています。API のスキーマ定義が変更になる場合などは先にバックエンドからリリースし、その後フロントエンドをリリースするようにしています。
GitHub の画面上でリリースタグを作成すると本番リリースされるようになっていて、リリースが完了すると Slack に通知が飛びます。フロントエンド、バックエンドをそれぞれ個別にリリース可能です。
もっと簡単に、かつ、より安全にリリースしたいと考えているので、現在の monorepo 構成からフロントエンドとバックエンドで repository を分割した multi-repo にした方がいいのではないかという話を過去に行なっています。
monorepo で行くのか multi-repo にするのか
私たちのチームは、現在の一元化されたリポジトリ(monorepo)構成を維持するか、複数のリポジトリ(multi-repo)に分割するかを議論しています。どちらがベストかは明確ではないため、まずは新しいプロジェクトでmulti-repoを試すことにしました。
monorepo の利点には、チームの一体感、バックエンド開発者がフロントエンドの Pull Request を確認しやすいこと(もちろん、逆のパターンもあります)、そしてフロントエンドとバックエンドの両方が同じコミットで機能することを保証しやすいことがあります。
これらの点から、monorepo の継続も良い選択だと考えています。一方で、multi-repoにすると、フロントエンドチームはその領域に集中でき、バックエンドの変更がなければ気にせずリリースできるため、どちらが私たちに適しているかを検証したいと思います。
支払い.comのような技術的境界でチームが分かれている場合、フロントエンドとバックエンドを別々のリポジトリにするとリリースが容易になると考えています。しかし、実際に運用してみないと分からないこともあるため、様々な方法を試しながら、私たちに合った開発フローを見つけたいと思います。
トランクベース開発など、すでに確立された開発フローに基づいて進めつつ、細部を微調整して自分たちにとって最適な方法にする予定です。
開発フローやブランチ戦略などを考え直して分かったこと
まず、自分1人では思い付かないような改善提案がチームメンバーからたくさん出てくることがとてもいいなと思いました。2週間に1回やっている KPT でブランチ戦略の見直しやテスト環境を複数デプロイする案などが出てきており、実際に Try して改善できているので、今後も継続していきたいです。
ちなみに Firebase Hosting と Cloud Run の preview 環境デプロイはチームのエンジニアが積極的に動いて全部やってくれました!圧倒的感謝。
現在採用している Git-Flow を GitHub-Flow に変更するかどうかも検討しており、GitHub-Flow を採用するのであれば feature flag を使ってリリースできた方が良さそうなので、今後も開発体験を損なわず、かつ、リリース速度を落とすことなく継続的に改善していけたらいいなと思います。
こちらもモザイクだらけですが、KPT で上がってくる Problem の画像になります。Problem の中から厳選して Try を選ぶことが多いです。
できていないこと
計測ができていないです。リリースしたことによりビジネス的にどのようなインパクトがあったのかをちゃんと計測する必要があると考えています。具体的にはリリースした機能によって売り上げがどれぐらい上がったのかや、顧客の利便性がどのぐらい上がったのかなどを定量的に計測したいと考えています。計測できていることも一部ありますが、できていないことの方が多いです。
また、システムのメトリクス収集については Google Cloud の Cloud Monitoring を使っているだけなので、システムの規模が大きくなってより詳細なメトリクスなどが必要になった場合は Datadog などの導入が必要かもしれません。
Cloud Run の場合は Open Telemetry などを導入しなくても Cloud Trace で API の処理単位で何にどれぐらい時間がかかっているのかを計測できるので、現状はそれで十分かなという状態です。Cloud Trace とは別に Cloud Monitoring のダッシュボードで CPU 使用率やメモリの使用量、コンテナインスタンス数などを確認しています。
支払い.comはマイクロサービスではないため、 Cloud Run の出力するログを Cloud Trace が解析し、それぞれの処理時間を出力することができています。今後新たにマイクロサービスを構築し、サービス間でトレースを行いたいなどのニーズが出てきた場合は Open Telemetry などのライブラリの導入が必要になりそうです。
それ以外にも、ガベージコレクションの回数など、 JVM の詳細なメトリクスを収集して計測したくなった際は Open Telemetry の導入を検討しようと思います。
プロジェクトの運営についても慣れが必要かなと感じています。エンジニアが増えたことにより調整と管理のコストが増えたので、管理や調整のコストを最小限に抑えつつ最速でプロダクトをリリースできるようにしていきたいと考えています。具体的な改善案などは振り返りの時に考えようと思います。
おわりに
人が増えて開発する機能が増えると今までそれほど問題ではなかったことが本格的な問題になるという話でした。scope ブランチから preview 環境へデプロイすることで複数環境でテストできるようになる見通しです。さらに人が増えたら別の問題が発生すると思いますが、継続的に改善していけたらいいなと思います。
このテックブログは、私一人の功績ではなく、チーム全体で継続的に改善してきた成果を紹介しています。チームや事業が成長する過程で直面した様々な課題について、リリース後の約2年間を振り返り、ここにまとめました。これからもチームとして成果を上げられるように頑張ります!
これは個人的な感想でもあるのですが、他の会社のテックブログなどを読むと強いエンジニアがいい感じの環境を整えて理想的な開発をできていそう・・というふうに見えることが多いです。このブログもそのような印象を持っていただけると嬉しいのですが、正直なところ、支払い. comの開発チームはできていないことや不十分なことはまだまだあります。このブログだけで全てを伝えることは非常に困難なので、もし気になることなどがありましたらお気軽にご連絡いただければと思っています。
UPSIDERはエンジニアを募集していますので、ご興味がありましたらカジュアル面談などのご応募をお待ちしております!