モノレポ vs ポリレポ:Web・モバイル・バックエンドを同期させる方法
Web、モバイル、バックエンドを同時に出すチーム向けにモノレポとポリレポを解説。依存関係、リリース調整、CIの戦術を比較し、開発速度を維持する方法を示します。

本当の問題:3つのコードベースにまたがる変更を出荷すること
チームがモノレポかポリレポかで議論するのは、Gitの哲学が原因ではありません。小さなプロダクト変更が Web、モバイル、バックエンドのそれぞれに別々の変更になり、どこかで何かが壊れることが問題なのです。
最初に壊れるのはめったにUIではありません。目に見えない接着剤の部分、つまりAPI契約が一致せずに変わったり、共有ライブラリが片方でバージョンアップされてもう片方が忘れられたり、あるいはビルドパイプラインに新しいステップが突然必要になったりします。一部が他より先にデプロイされると、ユーザーは「Webではボタンがあるのにモバイルは対応していないと言う」や「バックエンドの応答が変わってアプリがずっと読み込み続ける」といったバグとして感じます。
Web、モバイル、バックエンドはそれぞれリリースのリズムが違います。Webは一日に何度も出せます。バックエンドも頻繁に出せますが、慎重なロールアウトが必要です。モバイルは最も遅く、アプリストア審査やユーザー側のアップデートが実際の遅延を生みます。フィールド名の変更のような「簡単な」変更でも、たとえ一つの画面だけが必要としている場合でも、最も遅い経路に合わせて計画しなければならないことがあります。
次のようなことが頻繁に起きているなら、リポジトリ調整コストを払っている可能性があります:
- マージ後に破壊的なAPI変更が見つかる。
- バージョン整合が手動のリマインダーやスプレッドシートに依存している。
- 一機能に複数の調整されたプルリクエストが必要で、互いに待ち状態になる。
- CIが遅く、変更が触れた範囲以上をビルド/テストしている。
- どのコミットがどのリリースに対応するか不明でロールバックが怖い。
チームの規模やプロダクトの成熟度によって最適解は変わります。初期のうちは、調整コストを安くして可視性を高める方が勝ちやすいです。チームが成長すると境界は重要になりますが、それはインターフェースが安定し、所有権が明確な場合だけです。
もし意味のある変更がすべて三箇所に着地しなければならないなら、そのコストはどこかで払うことになります。リポ戦略は主にそのコストをどう払うかの話です。
モノレポとポリレポの基本(専門用語抜きで)
リポは単にコードと履歴が置いてある場所です。Web、モバイル、バックエンドがあるとき、選択は単純:すべてを一緒に置くか、分けるか。
モノレポは複数のアプリや共有コードを1つのリポジトリに入れる形です。Web、iOS/Android、バックエンドサービス、共有ライブラリが並んで存在します。
ポリレポは逆です:各アプリ(場合によっては各サービス)ごとにリポジトリを分けます。共有コードは別パッケージにしたり、小さな部分をコピーしたりします。
日常的には、モノレポはこんな感触です:コード共有が簡単で、クロスアプリの変更を1つのプルリクエストにまとめられ、ルールが一貫します。トレードオフはソーシャルな面で、所有権が曖昧になりやすく、リポ全体のチェックが厳しく感じることがあります。
ポリレポはこんな感触です:各チームが独立して動けてリポが集中し、アクセス制御が簡単になりやすい。トレードオフは調整で、コード共有には計画が必要で、クロスアプリの変更が複数のプルリクエストと時間差を生みます。
多くのチームはハイブリッドに落ち着きます:アプリは別リポにして契約だけをまとめる、あるいは1つのモノレポにして強い境界を設け各チームが自分の領域に留まるようにする、などです。
もしバックエンド、Web、モバイルを1つのソースから生成するプラットフォームを使えば、契約やロジックが一緒にあるためドリフトは減ります。たとえば AppMaster (appmaster.io) は単一モデルから本番準備済みのバックエンド、Web、ネイティブモバイルアプリを生成します。これでリリースの現実(モバイルはまだ遅い)は消えませんが、「全部更新したっけ?」という手間は大幅に減ります。
依存関係管理:共有コードを安全に保つ
共有コードはリポレイアウトに関わらず時間を食う場所です。共有ライブラリやAPI契約の小さな変更が、Webビルド、モバイルのリリース、バックエンドのデプロイをそれぞれ別の形で壊します。
UIコンポーネント、バリデーションルール、認証ヘルパーなどの共有ライブラリを使うときは、「全員が同じバージョン」か「複数バージョンを運用するか」を選ぶことになります。
- 全員が同じバージョンならシンプルで「自分のブランチでは動くのに本番で動かない」が減ります。
- 複数バージョンはチームごとの前進を許しますが、雑然としセキュリティ修正の横展開が難しくなります。
APIクライアントやスキーマには特に注意を払ってください。手動更新は遅くミスが起きやすいです。良いパターンはAPIスキーマを信頼できるソースとし、そこからクライアントを生成してチェックインするかCIで生成することです。目的は早期失敗:バックエンドが必須フィールドを追加したなら、モバイルクライアントはビルド時に壊れるべきで、QAで3日後に気づくべきではありません。
振る舞いが安全な移行経路なしに変わると破壊的変更が広がります。まずは加算的変更(新しいフィールド、新しいエンドポイント)を優先し、その後に非推奨にする方が良いです。どうしても破壊的変更が必要なら、バージョン化されたエンドポイントや短期の互換ウィンドウを使ってください。
具体例:バックエンドがstatusをstateにリネームしたとします。Webが今日更新できてモバイルが1週間後しか出せないなら、バックエンドはその週は両方のフィールドを受け付けるか、古いフィールドを新しい名前にマップするアダプタを出す必要があります。
依存関係の更新を退屈(良い意味で)にするルール:
- 定期的に更新する。大きな四半期更新より小さな週次更新が勝ちます。
- 破壊的変更には明示的な承認と短い移行ノートを要求する。
- 自動チェックを入れる:依存性のバンプ、クライアント生成、基本的な契約テスト。
- 「完了」を「私のリポが通った」ではなく「Web、モバイル、バックエンドのビルドがグリーン」と定義する。
生成コードはドリフトを減らしますが、規律を置き換えるものではありません。1つの契約、明確な非推奨方針、予測可能な更新が必要です。
リリース調整:Web、モバイル、バックエンドを合わせる
リリース調整はリポ戦略が理論で終わらない場所です。バックエンドがAPIフィールド名を変えたら、Webは同日更新して出せることが多いです。モバイルは違います:アプリストア審査やユーザーの更新で小さな不一致が1週間のサポートチケットにつながることがあります。
実践的な目標はシンプルです:どのパーツが先に更新されてもユーザー操作が動くようにすること。つまり完璧な同期リリースを前提にせず、混在バージョンを想定した計画を立てることです。
チームが実際に使うバージョニングパターン
多くのチームは次のどれかに落ち着きます:
-
1つの共有リリーストレイン:Web、モバイル、バックエンドを1つのバージョン単位で出す。
-
サービス別バージョン+互換ルール:各アプリ/サービスは独自にバージョンを持ち、バックエンドは定義された範囲のクライアントバージョンをサポートする。
共有リリーストレインは見た目がきれいですが、モバイルの遅延で破綻しがちです。サービス別バージョンは紙面上は煩雑ですが現実に近いです。もしサービス別を採るなら、どのバックエンドバージョンがどのモバイルバージョンをどれだけサポートするかを文書化して守ってください。
モバイルの遅延はホットフィックスの扱いも変えます。バックエンドのホットフィックスはすぐ出せますが、モバイルのホットフィックスはユーザーに届くまで数日かかるかもしれません。古いモバイルビルドでも動くようにサーバ側の修正を優先してください。クライアントを変える必要がある場合は機能フラグを使い、古いフィールドをほとんどのユーザーが更新するまで削除しないでください。
例:注文フローに「配達指示」を追加する場合。バックエンドは新しい任意フィールドを追加し、Webはすぐに表示し、モバイルは次のスプリントで表示する。バックエンドが古いリクエストも受け入れ、フィールドを任意のままにしておけば、モバイルが追いつく間も全体が動き続けます。
リリースカレンダーの所有者は誰か
「みんなが担当」となると誰も管理しないので調整は失敗します。オーナーはテックリード、リリースマネージャー、あるいはエンジニアをサポートするプロダクトマネージャーなどが務めます。役割はサプライズを防ぐことで、リリース期待値を見える化して一貫させることです。
複雑なプロセスは不要です。必要なのは繰り返しできる習慣:カットオフとフリーズウィンドウを含む簡単なリリースカレンダー、API変更前のクロスチームのクイックチェック、モバイル遅延時の方針(バックエンドを止めるか互換性を保つか)です。
ワークフローが1つのモデルからWeb、モバイル、バックエンドを生成していても、リリースオーナーは必要です。多くの場合「全部更新したっけ?」は減りますが、モバイルのタイミング問題からは逃れられません。
CI時間を抑える
CIが遅くなる理由は両方のセットアップで同じです:無駄に再ビルドしている、依存を何度も再インストールしている、すべての変更で全テストを回している。
よくある時間食い要因は、ささいな変更でのフルビルド、キャッシュの欠如、全てを走らせるテストスイート、並列化できるものを直列で実行していることです。
共通改善から始めましょう:
- 依存ダウンロードとビルド成果物をキャッシュする。
- 可能な限りリント、ユニットテスト、ビルドを並列で実行する。
- 速いチェック(各コミット)と遅いチェック(main、夜間、またはリリース前)を分ける。
モノレポで役立つ戦術
モノレポは「世界をビルドする」パイプラインが走ると苦しくなります。対処法は変更に影響するものだけをビルド/テストすることです。
パスフィルタや影響範囲だけを扱うアプローチを使いましょう:モバイルUIコードを変えたならバックエンドのイメージをビルドする必要はありません。共有ライブラリを触ったら、それに依存するアプリだけをビルド/テストします。多くのチームは単純な依存グラフを作り、CIが推測する代わりに決定できるようにします。
ポリレポでドリフトを防ぐ戦術
ポリレポは各リポが小さいので速くできますが、重複とツールの不一致で時間を無駄にしがちです。
CIテンプレートを共有して(同じステップ、同じキャッシュ、同じ慣習)、各リポがパイプラインを再発明しないようにします。ツールチェーン(ランタイム、ビルドツール、リンタ)のバージョンを固定して「あるリポでは動くが別のリポでは動かない」を避けます。依存ダウンロードがボトルネックなら共有キャッシュや内部ミラーを設定してください。
具体例:機能で新しいstatusフィールドが増える。バックエンドが変わり、Webは表示し、モバイルも対応する。モノレポならCIはバックエンドのテストと、そのAPIクライアントに依存するWebとモバイルの部分だけを走らせるべきです。ポリレポなら各リポが速いチェックを走らせ、別途統合パイプラインで三者のリリースが整合しているか検証します。
自身でソースをエクスポートしてCIを回す場合も同じルールが当てはまります:変わった部分だけをビルドし、キャッシュを積極的に再利用し、遅いチェックは本当に価値がある時だけ実行する。
ステップバイステップ:チームに合うリポ戦略を選ぶ
思想ではなく日常業務から始めると決定は簡単になります。
1) 一緒に変わる必要があるものを書き出す
最近の機能を5〜10件選び、それぞれ何を同時に動かす必要があったか書き出してください。UI画面、APIエンドポイント、データテーブル、認証ルール、共有バリデーションなどをマークします。もし大多数の機能が3領域すべてで調整を必要とするなら、分割は厳格なリリースプロセスがない限り苦痛になります。
2) 共有コードと共有判断を追跡する
共有コードはライブラリだけではありません。契約(APIスキーマ)、UIパターン、ビジネスルールも含みます。それらが現在どこにあり、誰が編集し、どう承認されるかを書き出してください。共有部分がリポ間でコピーされているなら、モノレポか厳格なバージョニングルールが必要なサインです。
3) 境界とオーナーを定義する
ユニット(アプリ、サービス、ライブラリ)を決め、それぞれにオーナーを割り当てます。境界はリポ配置より重要です。オーナーがないとモノレポは雑然とし、ポリレポは切り離されます。
簡単なチェックリスト:デプロイ可能なサービス/アプリごとに1リポまたは1フォルダ、共有契約は1箇所、本当に共有するUIコンポーネントは1箇所、ビジネスロジックの居場所ルール、そして各ユニットのオーナーをドキュメント化する。
4) 実行できるリリースモデルを選ぶ
モバイルがバックエンドより遅れるなら互換性プラン(バージョン化API、後方互換フィールド、サポートウィンドウ)を用意します。全部を同時に出すならリリーストレインは機能しますが調整コストが上がります。
ブランチ戦略はシンプルに:短命ブランチ、小さなマージ、明確なホットフィックス経路。
5) 日常的な変更に合わせてCIを設計する
初日から最悪ケースのためにCIを設計しないでください。日常的な作業に合わせて設計します。
大半のコミットがWeb UIだけを触るなら、デフォルトでWebのリントとユニットテストを実行し、エンドツーエンドはスケジュールやリリース前に回すようにします。APIドリフトが主な原因なら、まず契約テストとクライアント生成に投資してください。
例:Web、モバイル、バックエンドにまたがる1つの機能
小さなチームが3つのものを同時に作っていると想像してください:カスタマーポータル(Web)、フィールドアプリ(モバイル)、API(バックエンド)。あるリクエストが来ます:ジョブに新しい「サービスステータス」フィールドを追加し、どこでも表示する。
変更は小さく聞こえますが、調整のテストです。バックエンドはフィールドを追加し、バリデーションとレスポンスを更新します。Webは表示してフィルタを更新します。モバイルはオフラインで表示し、同期し、エッジケースを扱う必要があります。
問題はAPI変更が破壊的だということです。フィールド名がstatusからservice_statusに変わり、古いクライアントは対応していないと落ちます。
モノレポで変わること
モノレポではしばしば落ち着いて感じます。バックエンド、Web、モバイルの更新を1つのプルリクエスト(または調整されたコミット群)でまとめられ、CIは影響範囲のテストを走らせ、3つを含む1つのリリースをタグ付けできます。
主なリスクは技術的なものではなくソーシャルなものです:1つのリポは破壊的変更を素早くマージしやすいので、レビュー規則を強くしておく必要があります。
ポリレポで変わること
別リポだと各アプリは自分のスケジュールで動きます。バックエンドが先に出て、Webやモバイルが追いつくために慌てることがあります。モバイルのリリースはストア審査が必要なら修正に数日かかることがあります。
チームは通常、より多くの構造でこれを解決します:バージョン化されたエンドポイント、後方互換レスポンス、長めの非推奨ウィンドウ、明確なロールアウト手順。動きますが継続的な努力が必要です。
証拠に基づいて判断するなら過去数ヶ月を見てください:
- インシデントがバージョン不一致に由来することが多ければ、より厳密な調整を優先する。
- リリースが頻繁で時間がシビア(特にモバイル)なら、破壊的変更を避けるか中央集権化する。
- チームが独立していて同じ機能に触れることがほとんどないなら、ポリレポのオーバーヘッドは受け入れ可能かもしれません。
避けるべき一般的なミスと罠
多くのチームが失敗するのは「間違った」リポ構造を選んだからではありません。日常の習慣が徐々に摩擦を増やし、すべての変更がリスクに感じられるようになるからです。
共有コードがゴミ置き場になる
共有ライブラリは誘惑的です:ヘルパー、型、UIの断片、「一時的」な回避策。やがて古いコードがそこに溜まり、何を変して良いか誰も分からなくなります。
共有コードは小さく厳格に保ってください。「共有」は多くのチームが使っていて、慎重にレビューされ、意図的に変更されるべきです。
隠れた仮定による強い結合
別リポでもシステムは強く結合され得ます。結合は仮定に移るだけです:日付形式、列挙値、権限ルール、「このフィールドは常に存在する」など。
例:モバイルはstatus = 2を「承認」と扱い、Webは「確認」と扱い、バックエンドが列挙順を変えるとランダムに見える壊れ方をします。
契約(フィールドの意味、許容値)を文書化し、製品ルールとして扱ってください。
所有権が不明確
誰でも何でも変えられるとレビューが浅くなりミスが混入します。誰も所有していないとバグが何週間も放置されます。
Web、モバイル、バックエンド、共有モジュールにオーナーを定義してください。所有は貢献を阻むものではなく、適切な目が入ることを保証します。
CIが肥大化している
CIは小さく始まりますが、毎回のインシデントで「安全のために」新しいジョブが増えます。数ヶ月後、それは遅く高価で人々が避けるようになります。
簡単なルール:すべてのCIジョブには明確な目的とオーナーが必要で、実際の問題を検出しなくなったら削除します。
警告サイン:ジョブ間の重複テスト、何日も赤のままのジョブ、「ちょっとした変更」の検証にビルドより時間がかかる、バックエンド専用の変更でモバイルビルドが走る、など。
リリース調整がトライバルナレッジに依存している
誰か一人の記憶に頼ったリリース順と秘伝の抜け道に依存すると、出荷が遅くなりミスが増えます。
リリース手順を書き下し、繰り返せるようにし、退屈なチェックを自動化してください。たとえツールが一貫したバックエンドやクライアントを生成しても、明確なリリースルールは必要です。
モノレポかポリレポかを決める前の簡単チェック
リポを再編する前に、チームが今どう出荷しているか現実確認をしてください。目標は完璧な構造ではなく、1つの変更がWeb、モバイル、バックエンドをまたぐときのサプライズを減らすことです。
次の5つを自問してください:
- 独立デプロイ: バックエンドの修正を当日中にモバイルアップデートを強制せずに出せますか?
- API変更ルール: 非推奨化の書面ルールと古い挙動をどれだけ長く保つかの取り決めがありますか?
- 共有コードの規律: 共有ライブラリ(UIコンポーネント、APIクライアント、ビジネスルール)は一貫してレビュー/バージョン管理されていますか?
- 重要な部分だけを走らせるCI: CIは何が変わったかを特定して、影響のある部分だけをビルド/テストできますか?
- 統一されたリリースビュー: Web、モバイル、バックエンドで何が出るか、オーナーと日付が見える1つの場所はありますか?
簡単な例:チェックアウトに新しい「住所」フィールドが追加されたとします。バックエンドが先に出ても古いモバイルアプリが動くなら問題ありません。通常はAPIが古い・新しい両方のペイロードを許容し、クライアント更新は任意で済むべきです。
次のステップ:調整作業を減らして自信を持って出荷する
目的は「正しい」リポ構造ではなく、ハンドオフを減らし、サプライズを減らし、「どのバージョンが本番か?」と追いかける時間を減らすことです。
短い決定記録を残してください:なぜ今のアプローチを選んだか、何が改善する見込みか、どんなトレードオフを受け入れるか。6〜12ヶ月ごと、またはチームサイズやリリース頻度が変わったら見直します。
ファイルを動かす前に、実際の痛みを取り除く最小の一手を選んでください:
- 共有パッケージのバージョニングルールを追加して守る。
- API契約を定義し、CIで契約テストを強制する。
- Web、モバイル、バックエンド共通のリリースチェックリストに合意する。
- 複数領域に触れる変更はプレビュー環境で確認する。
- PRチェックの時間目標を決める(例:15分以内)。
クロスコードベースの結合が本当のボトルネックなら、変更箇所を減らすことがリポ構成より重要になることがあります。あるチームはこれを単一の真実のソースにより多くのロジックとデータモデリングを移すことで解決します。
もしそのアプローチを検討するなら、AppMaster (appmaster.io) は共有データモデルとビジネスロジックからバックエンド、Web、ネイティブモバイルを生成するように設計されています。リスクの低い評価方法はまず小さな社内ツールを一つ作って、どれだけ調整作業が減るかを見てから判断することです。
自信を持った道は意図的に退屈です:決定を文書化し、結合を減らし、チェックを自動化し、数値が改善を示すときだけリポ構造を変えてください。
よくある質問
まず、1つの機能がどれだけ頻繁にWeb、モバイル、バックエンドの変更を強いるかで判断してください。作業の多くが横断的で調整が主な問題なら、モノレポや「単一の契約」を強く守る方が壊れにくくなります。逆にチームがほとんど別々に動き、独立したアクセスやリリース管理が必要なら、厳格な互換ルールと共にポリレポが有効です。
APIのドリフト、共有ライブラリのバージョン不整合、そしてリリースのタイミング差(特にモバイルのストア審査遅延)が典型的な原因です。現実には完璧な同期を前提にせず、混在バージョンを想定すること、そして破壊的変更を稀かつ意図的にすることが解決策です。
APIスキーマを単一の信頼できるソースとして扱い、そこからクライアントを生成するようにします。そうすれば不一致はQAや本番ではなくビルド時に検出できます。まずは追加的な変更を優先し、古いフィールドは後で非推奨にする、という方針が安全です。
小さく、定期的な更新を目指しましょう(週次が四半期ごとより良い)。破壊的変更は明示的な承認を求め、各変更に短い移行ノートを付けます。「完了」の定義を「Web、モバイル、バックエンドのビルドがすべてグリーン」であるとすると、混乱を避けられます。
基本はサービスごとのバージョン運用にして、互換性ルールを明文化すること:どのバックエンドバージョンがどのクライアントバージョンをどのくらいサポートするか。リリーストレイン(全てを一緒に出す)は初期には機能しますが、モバイルの遅延があると辛くなる傾向があります。
バックエンドは古いモバイルビルドでも動くよう互換性を保つべきです。フィールドは追加的にし、古い挙動を早急に取り除かないでください。クライアント側の変更が必要な場合は機能フラグを使い、ユーザーの更新状況を見ながら段階的に展開します。
リリース調整は誰かに明確に担当させてください。多くはテックリード、リリースマネージャー、あるいはエンジニアを支えるプロダクトオーナーが適任です。単純で再現可能なカレンダー、カットオフやフリーズのルール、モバイル遅延時の判断基準(変更を止めるか互換性を保つか)を持たせることが目的です。
変更された箇所だけをビルド/テストし、可能な限りキャッシュを使うことです。速いチェック(各コミット)と遅いチェック(mainや夜間、リリース前)を分けると、開発者に素早いフィードバックを提供できます。
パスフィルタや“影響範囲のみ”の方針を使い、小さな変更で全体をビルドしないようにします。共有モジュールが変わった場合は、そのモジュールを使うアプリだけをチェックすれば十分です。加えて、所有権とレビューのルールを明確にしておきます。
リポジトリ間で同じツールやCIテンプレートを標準化し、各リポジトリがステップやキャッシュ、慣習を再発明しないようにします。重要な契約を検証する統合チェックを設け、ツールチェーンのバージョンを固定して「あるリポでは動くが別のリポでは動かない」を防ぎます。


