コードプッシュとOTA更新

ストア審査を待たずにバグ修正を即日届けたい。JSバンドル差し替えの原理と規約の境界線、差分更新・段階的ロールアウト・自動ロールバックの内部動作が分かる。

応用React NativeOTA更新CodePushモバイル配信ロールバック最終更新: 2026-07-29
3つの要点
TL;DR
  1. React Native系のOTAはネイティブバイナリではなくJSバンドルとアセットだけを差し替える仕組みで、起動時にサーバーへ問い合わせ、新しい版があればダウンロードして次回以降のロードで適用する。
  2. ストア規約はネイティブコードの実行部分をあとから書き換えることを禁じており、OTAで変えてよいのはJS層のロジックとリソースに限られる。ネイティブモジュールの追加や権限変更はストア審査が必須になる。
  3. 差分更新はバンドルのハッシュ単位で変更ブロックだけを転送し、段階的ロールアウトとクラッシュ率監視による自動ロールバックで、壊れた版が全ユーザーへ広がる前に配信を止める。

なぜバイナリを差し替えずに更新できるのか

通常のアプリ更新はストア審査を通り、ユーザーが新しいバイナリをダウンロードして初めて反映されます。緊急のバグ修正でもこの経路だと数時間から数日かかります。React Native(や旧Cordova系)のアプリは、この待ち時間を回避する手段としてOTA(Over-The-Air)更新を持ちます。鍵は「アプリの中身が二層構造になっている」ことです。ネイティブ実行環境(ネイティブUIコンポーネント、JSエンジン、各種ネイティブモジュール)はストア配布のバイナリに焼き込まれる一方、画面ロジックの実体はindex.bundleのようにパッケージ化されたJavaScriptバンドルとアセット(画像・フォント)で、これらは起動時にJSエンジンが読み込む「データ」に過ぎません。データであるなら、バイナリを差し替えずに中身だけ入れ替えられます。これがコードプッシュの原理です。詳しい二層の橋渡しは /mobile-development/native-bridge-jni-jsi/ を参照してください。

更新の流れ:問い合わせ・取得・切り替え

OTAクライアントSDK(Expo Updates/EAS Update や、CodePush系のSDKなど)は、アプリ起動時に更新サーバーへ「自分はどの版を持っているか」を問い合わせます。サーバーは配信対象のバンドルとクライアントの現在版を比較し、新しい版があれば取得先を返します。ここで重要なのは、ダウンロード完了と同時にその場で切り替わるわけではない点です。

横にスクロール

コードプッシュとOTA更新について、更新の流れ:問い合わせ・取得・切り替えを中心に端末内外の処理経路と設計判断を示す図
「更新の流れ:問い合わせ・取得・切り替え」の処理境界と、実装・計測・判断の要点を整理します。
段階起きること設計上の狙い
チェック現在のバンドルのラベル/ハッシュとターゲット版をサーバーで照合対象条件(アプリバージョン・配信率)に合致するか判定する
ダウンロード新バンドル(または差分)を端末のストレージに書き込む実行中のバンドルには一切触れず、裏で安全に用意する
インストール次回起動時に新バンドルを読むようポインタを更新アプリ実行中の切り替えによるクラッシュを避ける
適用確認新版が起動して一定時間クラッシュしなければ確定(コミット)起動直後に落ちる版を「未確定」として自動で巻き戻せるようにする

多くのSDKでは適用タイミングを選べます。既定は「次回起動時」で、ユーザーがアプリを一度閉じて開き直したときに新バンドルへ切り替わります。即時再起動を強制する設定もありますが、操作中の画面が突然切り替わる体感が悪いため、緊急修正以外は次回起動方式が無難です。端末側にはロールバックのために直前の正常バンドルが保持され、新版が起動確認(後述)を通るまで旧版を捨てません。

ストア規約という動かせない境界線

OTAが「何でも後から変えられる」わけではないのは、AppleのApp Store ReviewガイドラインとGoogle Playのポリシーが引く明確な線があるからです。両者に共通する原則は、アプリの本来の目的・機能を変える更新や、ネイティブの実行コードを新たに持ち込む更新は審査を経なければならない、というものです。

OTAで変えてよいもの・いけないもの

変えてよいのは、既にバイナリに含まれるJSエンジン上で解釈される「JavaScriptのロジック」と、画像・スタイル・文言などのアセットです。逆に、新しいネイティブモジュールの追加、要求する権限(entitlements)の変更、アプリの主要目的そのものの変更は、OTAでは行えずストア審査が必須です。Appleは「実行コードのダウンロードは、Appの本来の宣伝された機能を変えない範囲で、かつApple提供のフレームワーク(JavaScriptCore)が実行するものに限る」という趣旨の条項を持ち、これがReact NativeのOTAが成立する法的な土台になっています。

技術的にJSバンドルを差し替えられることと、規約上それが許されることは別問題です。例えばA/Bテストで画面配置を変えるのは通常許容されますが、審査中は無害な機能を見せ、OTAで審査後にまったく別の機能へすり替える行為は明確な違反で、発覚すればアプリの削除や開発者アカウント停止につながります。権限や配布の審査モデルは /mobile-development/mobile-app-signing-provisioning/ で扱う署名の信頼モデルと地続きです。

差分更新:バンドル全体を送らない

初期のOTAはバンドル全体(数MB規模)を毎回ダウンロードしていましたが、これはユーザーの通信量とストレージを圧迫します。差分更新(differential/delta update)は、変更された部分だけを転送する仕組みです。

原理はこうです。バンドルは内部的に多数のモジュールやアセットの集合で、それぞれにハッシュが計算されています。サーバーはクライアントが持つ版のハッシュ集合(例 {a1, b2, c3})と新版のハッシュ集合(例 {a1, b2, d4})を比較し、差分(この場合 c3 を捨て d4 を追加)だけをパッチとして送ります。クライアントは手元の旧バンドルにパッチを適用して新バンドルを組み立てます。1行のロジック修正なら実際に転送されるのは変更モジュール分のごく一部で済み、全体ダウンロードに比べ転送量が桁違いに小さくなります。

差分適用が成立する前提

差分は「クライアントが特定の旧版を正確に持っている」ことが前提です。旧版のハッシュが一致しなければパッチは適用できないため、サーバーは複数の起点版に対するパッチを用意するか、起点が想定外なら全体ダウンロードにフォールバックします。適用後は組み立てた新バンドルのハッシュを検証し、期待値と一致して初めて有効化します。これにより転送中の破損や不正なパッチによる壊れたバンドルの実行を防ぎます。バンドルのロードは起動時間に直結するため、ここでの効率は /mobile-development/mobile-app-startup-optimization/ の観点とも関わります。

ロールバックと段階的ロールアウト

OTAの怖さは、壊れた版を一瞬で全ユーザーへ配れてしまうことです。これを防ぐのが自動ロールバックと段階的ロールアウトの二段構えです。

自動ロールバックの核は「起動確認(health check)」です。新バンドルを適用した最初の起動で、SDKはまだその版を確定(コミット)しません。アプリのコードが正常に立ち上がり、一定の初期化を終えてnotifyAppReady相当の合図を出したら初めてコミットします。もし新版が起動直後にクラッシュし、この合図が出ないまま次の起動を迎えると、SDKは「この版は起動に失敗した」とみなして自動的に直前の正常バンドルへ巻き戻します。つまり保持していた旧版が安全網として機能します。

起動確認が守れない失敗

自動ロールバックが救えるのは「起動できない・すぐ落ちる」種類の障害だけです。起動確認は通るが特定の操作でだけ落ちる、あるいはデータを静かに破壊するといった不具合は合図が出てしまうためコミットされ、自動では巻き戻りません。この層はサーバー側からの明示的なロールバック(対象版の配信停止と旧版への差し戻し)と、次で述べる段階的ロールアウトによる被害の局所化で守る必要があります。

段階的ロールアウト(staged rollout)は、新版を最初から100%に配らず、まず1%、次に10%といった割合で徐々に広げる方式です。サーバーは端末を安定したハッシュ(デバイスIDなどのハッシュ)でバケットに割り当て、割り当て値が現在の配信率のしきい値未満の端末だけに新版を返します。配信中はクラッシュ率やエラー率を監視し、悪化すれば配信率の引き上げを止める、あるいはゼロに戻して被害を初期の数%に閉じ込めます。React NativeのOTAはクロスプラットフォーム基盤の上で動くため、この配信制御は /mobile-development/cross-platform-rendering-strategies/ の描画モデルと合わせて理解すると全体像が掴めます。

押さえておきたい要点

OTAが差し替えるのはJSバンドルとアセットであり、ネイティブ実行コードは変えられない(変えるならストア審査必須)という境界を第一に押さえます。更新は「チェック→ダウンロード→次回起動でインストール→起動確認でコミット」の流れで進み、実行中のバンドルには触れないこと、差分更新はハッシュ単位で変更分だけを転送し適用後にハッシュ検証すること、自動ロールバックは起動確認を通らない版のみを救い、段階的ロールアウトが被害範囲を割合で制御することを説明できるようにしておきます。

まとめ

コードプッシュとOTA更新は、アプリがネイティブ実行環境とJSバンドルという二層で構成されることを利用し、後者だけをデータとして差し替えることでストア審査を待たずに修正を届ける仕組みです。ただし変更してよいのはJS層のロジックとアセットに限られ、ネイティブコードの追加や権限・目的の変更はストア規約が審査を要求する動かせない境界です。差分更新はバンドルをハッシュ単位で扱い変更分だけを転送してから検証し、段階的ロールアウトと起動確認ベースの自動ロールバックが、壊れた版の被害を初期の数%に閉じ込めます。技術的に何でも差し替えられることと、規約上・品質上それを安全に行えることは別であり、境界線と安全網の設計こそがOTA運用の要諦です。

モバイル開発の記事ガイド

コードプッシュとOTA更新を実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

React Native

比較で見る軸

難易度: advanced / カテゴリ: モバイル開発 / タグ数: 6

導入後に効く点

ストア規約はネイティブコードの実行部分をあとから書き換えることを禁じており、OTAで変えてよいのはJS層のロジックとリソースに限られる。ネイティブモジュールの追加や権限変更はストア審査が必須になる。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
モバイル開発
タグ数
6

判断チェックリスト

  • 自社の用途が「React Native / OTA更新」に近いか確認する。
  • 強みである「React Native系のOTAはネイティブバイナリではなくJSバンドルとアセットだけを差し替える仕組みで、起動時にサーバーへ問い合わせ、新しい版があればダウンロードして次回以降のロードで適用する。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

React NativeOTA更新CodePushモバイル配信