UPSIDER Tech Blog

少人数チームにおける複数アプリの継続的デリバリー:Flutter開発の裏側 登壇レポート

こんにちは。UPSIDER でアプリ開発を担当している田中です。

先日、findy さん主催のイベント 【Flutter特集】Flutter開発の裏側 〜各社が向き合う課題と挑戦〜 というイベントで登壇し、少人数チームで複数アプリを継続的にデリバリーしている話をしました。

findy.connpass.com

この記事では、そのときお話しした内容を補足も交えながら整理してお伝えします。
Flutter で複数アプリを運用している方や、少人数でモバイルを回しているチームの参考になればうれしいです。

アプリ開発全体の話については、以下のインタビューもぜひ読んでください。

tech.up-sider.com

チーム構成と役割:3人でアプリもバックエンドも見る

アプリチームの構成はとてもシンプルで、エンジニアは 3 名です。そのうち 1 人が私で、私自身はだいたい工数の半分くらいをエンジニアとして書く側に振っています。
全体としては、体感的には 2.5 人日分くらいで、Android / iOS / バックエンドをすべて見ているチームになります。

技術スタックとしては、

  • モバイルアプリ: Flutter
  • バックエンド: Go

という構成です。Go を採用しているのは、学習コストが比較的低くチーム全員が触れる状態を作りやすいからです。Flutter も同様に、Android / iOS の両方に同じコードで届けられるため、少人数で両プラットフォームをカバーするうえでかなり助けられています。

役割としては、アプリを作るだけではなく、

  • バックエンドの保守・開発
  • BigQuery を用いた計測と機能評価
  • 一部 QA の設計・実施
  • 今後のホワイトレーベル展開を見据えた共通基盤づくり

これらを含めて開発サイクルをほぼ自前で回しているようなイメージです。

また、自分たちで開発サイクルを回しながら、同時に他チームからの要望にも対応しています。たとえば不正利用対策チームをはじめ、さまざまなチームから「この機能をアプリにも入れてほしい」といった依頼が日々上がってきます。 つまり、アプリチームとして自分たちの開発サイクルを進めつつ、他チームから渡される“お題”もスケジュール通りに管理し、きちんと届ける責任があります。

課題:複数アプリ × 2週間リリース × 少人数

ここからは、実際に直面している課題についてです。

今、UPSIDER では 2 週間サイクルでアプリのリリースを行っています。UPSIDER / PRESIDENT CARD ともにアップデートがありますし、今後ホワイトレーベルのアプリが増えていくと、その分リリースの数も増えていきます。

この状況で、2週間ごとのリリース作業が重なってくると、純粋な「新しい機能を作る時間」がどんどん削られてしまう、というのが大きな課題でした。

さらに、UPSIDER アプリと PRESIDENT CARD アプリには、重複する機能もあれば、それぞれ固有の機能もあります。

  • 片方に入れた修正をもう片方にも適用したい
  • どちらのアプリにも載る共通機能がある
  • 将来的には、ホワイトレーベル先のアプリにも同じ機能を展開したい

といった状況なので、アプリ間でコードを共有する場面がどんどん増えていきます。
このとき、リポジトリやパッケージの構成が複雑だと、それだけで開発のボトルネックになってしまうという実感がありました。

モノレポへの移行:複数アプリを 1 つのリポジトリにまとめる

そこで数ヶ月前に踏み切ったのが、モノレポへの移行です。ここからは、リポジトリ構成に絞ってお話しします。

移行前:アプリごとにリポジトリが分かれていた

以前は次のような構成でした。

  • UPSIDER アプリのリポジトリ
  • PRESIDENT CARD アプリのリポジトリ
  • 共通パッケージをまとめたリポジトリ
  • さらにいくつかのプライベートリポジトリ

この状態だと、

  • 片方に追加した機能を、もう片方にも持っていくのが面倒
  • どのパッケージがどこに依存しているか分かりづらい
  • CI/CD 上でプライベートパッケージをフェッチする設定が煩雑
  • 片方のリポジトリだけ修正して、もう片方に反映し忘れるリスク

といった課題がありました。

特に、CI/CD まわりは本質的ではないのに時間を取られがちな部分で、ここを何とかシンプルにしたい、という思いが強かったです。

移行後:apps / packages を持つモノレポ構成へ

現在は、アプリまわりをまとめた apps というリポジトリを用意し、その中を次のように構成しています。

開発の観点では、Flutter の pub workspaces 機能を使って、依存関係をまとめて管理しています。

Shared ディレクトリと relative import の使い分け

モノレポ化したとはいえ、いきなりすべてをきれいにパッケージ化できるわけではありません。そこで使っているのが、apps/shared というテンポラリな共有コード置き場です。

  • すぐにはパッケージに切り出せないコード
  • とりあえず両アプリで共通利用したいコンポーネントやロジック

といったものを、まずは Shared 配下に置いています。ここではシンボリックリンクを使うケースもあります。

Pub workspaces を使うとパッケージ名はユニークである必要があるため、普通に package import をすると、UPSIDER / PRESIDENT CARD で import のパスが変わってしまいます。
そうすると、コード上はほぼ同じでも import の行だけ diff が出る、ということが起きてしまいます。

この問題に対しては、対象箇所だけ relative import を使うことで、2 つのアプリから同一コードとして扱えるようにしています。

CI/CD の設計:変更範囲に応じた lint・テストの実行

モノレポ化に合わせて、CI/CD も次のような形に整理しました。

  • packages/apps/shared に変更があった場合→ それぞれに対応する lint / テストを実行する
  • PRESIDENT CARD 専用のパッケージが変更された場合→ PRESIDENT CARD アプリ側のチェックに加え、Shared パッケージに影響があればそのチェックも走る

変更があったディレクトリに応じて、必要なチェックだけを効率よく回すイメージです。

リリース時には、UPSIDER / PRESIDENT CARD それぞれを個別にリリースできるように、 release branchをアプリごとに分けて運用しています。ブランチ名に namespace を付けて、ストア提出(TestFlight / Production)までの流れを整理しています。

やってみて見えてきた課題と、Devinを使った解決

もちろん、モノレポ化して終わり、ではありません。実際に運用してみて出てきた課題もあります。そのひとつが、リリースのたびに「どの差分が今回入るのか」を把握するのが難しい、という点です。

これに対しては、Devin を使って main と develop の差分から今回のリリースに含まれるプルリクを Markdown 形式で一覧にする仕組みを入れています。

  • どの PR が今回のリリース対象か
  • どのアプリに影響する変更なのか

を自動で可視化することで、リリースノート作成や確認の手間を減らしています。

また、Devin にはワークスペースのセットアップまわりもあるので、モノレポの構成や Flutter のセットアップ方法をある程度プロンプトとして持たせておくなど、新規メンバーが入ってきても迷わないように工夫していきたいと考えています。

モノレポ化の効果:定性的だけれど確かな手応え

効果を一言で言うと、かなり定性的ではあるものの、 開発効率は明らかに上がったと感じています。

  • 依存関係のアップデートが楽になった
  • 複数アプリにまたがる修正を 1 リポジトリで完結できる
  • CI/CD まわり、特にプライベートパッケージ関連の煩雑さから解放された

個人的には、この CI/CD の簡略化が一番うれしいポイントです。
開発の本質ではないところに取られていた時間を、少しずつプロダクトの価値を高める作業に戻せている実感があります。

これからやりたいこと:AI ツールとテストのこれから

最後に、今後チャレンジしていきたいことにも少しだけ触れておきます。

UPSIDER ではすでに Cursor や Devin、v0 など、さまざまな AI ツールを日常的に使っています。特に、リリース作業やドキュメント整備といった「事務作業」に近い部分は、まだまだ効率化の余地があると感じています。

  • リリースまわりのチェックリスト自動化
  • changelog の生成や整形
  • Figma と v0 を連携した UI 実装の高速化

など、開発体験をよくするための AI 活用は今後も積極的に取り組んでいきたい領域です。

また、E2E テストについても、以前導入していたものの、 メンテナンスコストの高さが課題になっていました。

FlutterKaigi でも AI を活用した E2E テストの話がありましたが、そういったアプローチも取り入れながら、少人数でも現実的に維持できるテスト戦略を模索していきたいと考えています。

おわりに

少人数チームで複数アプリを回し続けるのは、正直なところ楽ではありません。
ただ、モノレポ化や CI/CD の見直し、AI ツールの活用など、仕組みを少しずつ整えていくことで、確実に「作る時間」を取り戻せている実感があります。

UPSIDER では、今後もホワイトレーベル展開などを通じて、アプリの兄弟が増えていくことを前提として動いています。その中で、どうすれば少人数のままでも、スピードと品質を両立しながら、プロダクトをどんどん前に進めていけるのか。

今回ご紹介した取り組みが、同じような課題を抱えているチームのヒントになればうれしいです。

発表資料はこちら

speakerdeck.com

We Are Hiring !!

株式会社UPSIDERでは現在積極採用をしています。 ぜひお気軽にご応募ください。

herp.careers

herp.careers

UPSIDER Engineering Deckはこちら📣

speakerdeck.com