Product • React Native • 2026-02-24 • 16 minutes read
Ishika Chakraborty
OTA(Over-the-air)アップデートは、バイナリを再配布せずにユーザーの端末へ直接JavaScriptやアセットの修正を届けられるため、React Nativeチームにとって非常に強力なツールです。しかし「動く」と「信頼できる」は別物です。毎日複数回デプロイしても平気なチームは、いくつかの重要な運用パターンを習慣化しています。本記事では、実際に使える5つのパターンを紹介します。
クイック primer:OTA アップデートの仕組み
Expo Update(expo-updates)は、アプリのJavaScriptバンドルとアセットをネイティブコードとは別に更新できる仕組みを提供します。アプリ起動時にサーバーをチェックし、更新があればバックグラウンドでダウンロードし、次回のコールドスタートで適用されます。更新はチャンネル(ビルドが指す先)と runtime versions(JavaScriptとネイティブバイナリの互換性を保証する)で管理されます。
expo-updatesパッケージを使えば、必要に応じてオンデマンドで更新のチェック・適用ができます。
TL;DR:OTA アップデートチェックリスト
- プレビューとプロダクションのチャンネルを使う。公開前に必ずプレビューでテストする
- fingerprint を使ってネイティブ変更を検出し、アプリバージョンを上げる必要があるかを判定する
- 何がビルドを必要とし、何がOTAで済むかを把握する
- expo-updates API を使って更新を検出、ダウンロード、適用する(ユーザーが次回のコールド起動を待つ必要がなくなる)
- 段階的ロールアウトを行い、問題発生時には素早くロールバックできるようにする
以下でそれぞれ詳しく見ていきます。
1. 本番公開前に必ず preview でテストする
シンプルなフロー:preview → production
多くのチームは次の2つのチャンネルを運用しています:
- Preview:内部テスト用(しばしばステージングAPIやTestFlight/internal trackを指す)
- Production:ユーザー向け
基本ワークフローは以下の通りです。
# Step 1: Publish to preview
eas update --channel preview --message "Fix login bug"
# Step 2: Test on a preview build (internal distribution or TestFlight)
# Step 3: When validated, publish to production
eas update --channel production --message "Fix login bug"
この方法では、テストした正確なビルドアーティファクトをそのまま昇格(promote)するのではなく、同一コミットから再度バンドルを生成して公開します。多くの場合はこれで十分です。環境変数やバンドラの挙動、アセット問題はプレビューで検出されます。
進んだ運用:staging を挟むプロモーションワークフロー
QAで承認された「正確に同じ」アーティファクトを本番に配信したい場合は、さらに staging チャンネル(本番と同一の設定だが内部のみ配布)を用意し、QA済みの更新をそのままプロダクションへ昇格します。
# Step 1: Publish to staging
eas update --channel staging --message "Fix login bug"
# Step 2: QA tests on a staging build (identical config to production)
# Step 3: Promote the EXACT tested update to production
eas update:republish --channel staging --destination-channel production
この方法が適する場合:
- テスト済みアーティファクトに関するコンプライアンスや監査要件がある
- 過去にバンドル差分で問題が発生したことがある
- チームが大きく、再公開(publish again)が調整リスクになる
セットアップとしては、development / preview / staging / production の4つのアプリバリアントを用意し、stagingはproductionと完全に同一の設定にする必要があります。ほとんどの小〜中規模チームでは preview → production のシンプルフローで十分です。
2. fingerprint を使って新しいビルドが必要か判定する
問題点:すべての依存がJSのみの変更とは限らず、ネイティブ変更が含まれると古いバイナリにOTA更新を当てたときにクラッシュします。runtime version は互換性のファイアウォールです。
推奨アプローチ:appVersion を runtime version ポリシーに使う
これにより runtimeVersion をアプリのバージョン(例: 1.0.0, 1.1.0)に結びつけ、どのビルドがどの更新を受け取れるかを分かりやすくします。
{ "expo" : { "runtimeVersion" : { "policy" : "appVersion" } } }
では、いつ app version を上げるか? ここで fingerprint が役立ちます。fingerprint はプロジェクトのネイティブな表面積(依存、設定、ネイティブコード)をコミット間で比較します。fingerprint が変わっていればネイティブ変更が入っており、OTA の前に新しいビルドが必要です。
eas fingerprint:compare --build-id < BUILD-ID >
- fingerprints が一致すれば、OTAで安全に配信できます。
- 異なれば、新しいビルドが必要です。
CIに組み込むと便利です。CI上でfingerprintチェックを自動化すれば、変更がOTAで良いかビルドが必要かを自動判定できます。Hipcampのように、自動でOTA可能か判定している事例もあります。
いつ app version を上げるべきか:
- ネイティブコードを含む依存(ネイティブモジュール)を更新したとき
- Expo SDK をアップグレードしたとき
- app.json のネイティブに影響する設定を変更したとき(アイコン、スプラッシュ、entitlements, scheme など)
- ios/ や android/ のネイティブプロジェクトファイルを変更したとき(bare workflow)
- React Native 自体を更新したとき
fingerprint ツールがこれらを検出してくれるので、覚えておく必要はありません。ワークフローに組み込みましょう。
3. OTA で何が更新できるかを理解する
よくある質問:「全部OTAで更新できると思って公開したら壊れた。何がNGなの?」
この区別を理解しておくと、プロダクトやエンジニアリングの判断が明確になります("OTAで出す" vs "ストアリリース")。
更新できるもの(OTAで問題なし):
- JavaScriptコードとビジネスロジック
- UIコンポーネント、スタイル、レイアウト
- 画像、フォント、その他アセット
- バグ修正や文言修正
新しいビルドが必要なもの:
- ネイティブモジュールのインストールやアップグレード(expo-* パッケージでネイティブコードを含むものも含む)
- ネイティブコンパイルが必要な設定変更(アプリアイコン、スプラッシュ、ネイティブの entitlements、config plugins)
- 新しいネイティブ権限(カメラ、位置情報、通知など)
- Expo SDK のアップグレード(managed workflowでも)
- app.json のネイティブに影響する設定変更(scheme、associated domains、intent filters 等)
- bare workflow における ios/ や android/ のネイティブプロジェクトファイルの変更
- React Native 自体の更新
技術的には、JavaScriptやアセットであればOTAで配信できます。ネイティブに触れる変更はビルドが必要です。
fingerprint に判断させる
すべてを記憶する必要はなく、fingerprint がネイティブ変更を自動検出してくれます。EAS Workflows と組み合わせれば、push 毎に fingerprint をチェックして自動的に OTA か新しいビルドかへ振り分ける、という仕組みが作れます。
version : Deploy to production
on : push : branches : [ 'main' ]
jobs :
fingerprint :
name : Check fingerprint
type : fingerprint
environment : production
get_build :
name : Check for existing build
needs : [ fingerprint ]
type : get - build
params :
fingerprint_hash : ${{ needs.fingerprint.outputs.android_fingerprint_hash }}
profile : production
build :
name : Build (if native changed)
needs : [ get_build ]
if : ${{ !needs.get_build.outputs.build_id }}
type : build
params :
platform : android
profile : production
update :
name : OTA Update (if native unchanged)
needs : [ get_build ]
if : ${{ needs.get_build.outputs.build_id }}
type : update
params :
channel : production
このワークフローはmainへのpushごとにfingerprintをチェックします。ネイティブが変わっていて互換ビルドがなければ新しいビルドをトリガーし、互換ビルドが既にあればOTA更新を公開します。手動チェックは不要です。
ストアのコンプライアンスについては各ストアのガイドラインを直接確認してください。参考:Apple's Developer Program License Agreement と Google Play のダウンロードコードに関するポリシー。
4. expo-updates API を使って更新をより早く届ける
通常の挙動:ユーザーがアプリをインストールした後にOTA更新を公開すると、最初は埋め込みバンドルで起動し、バックグラウンドで更新がダウンロードされ、次のコールドスタート(強制終了して再起動)で更新が適用されます。これは初回起動のパフォーマンスを優先するための既定の挙動で、推奨されます。
ただし、重大な修正を即時に反映したい場合やユーザーの操作中に差し支えない場合は、expo-updates API を使って更新の検出、ダウンロード、適用を制御できます。これによりユーザーが次のコールドスタートを待たずに新しい更新を適用できます。
import * as Updates from "expo-updates" ;
const update = await Updates.checkForUpdateAsync();
if (update.isAvailable) {
await Updates.fetchUpdateAsync();
await Updates.reloadAsync();
}
expo-updates はレジリエントに設計されています。アプリがオフラインだったり更新サーバーに接続できない場合でも、既に持っている最新の更新を使い続けるため、アプリが壊れることはありません。ユーザーは単に次回のコールドスタート(次の強制終了後の再起動)まで新しい更新を受け取れないだけです。
5. 段階的ロールアウトと迅速なロールバックを行う
実運用で重要なのは、まず少数のユーザーにだけ配信してモニタリングし、問題がなければ段階的に拡大すること、そして問題発生時に短時間で元に戻せることです。
推奨パターン:
- Canary/preview チャンネルにまず公開して、内部または限られたユーザーで確認する
- モニタリング(クラッシュレポート、アナリティクス、サーバーのエラー率)を強化して、異常を早期検出する
- 問題がなければ production チャンネルへ公開
- 問題があれば、既知の安定版を production チャンネルへ再公開(republish)してロールバックする
ロールバックの一例:QAで安定している staging の更新を production に再適用する
eas update:republish
注意点:
- ロールアウトの粒度はチャンネル運用や内部配布で実現する(単一コマンドでの百分率ロールアウトを提供しない場合は、複数チャンネル・ユーザーセグメントで代替する)
- 監視とアラートを自動化しておくと、問題検知からロールバックまでの時間を短縮できる
- ロールバック用に常に「直近の安定版」を手元に保持しておくと安心
まとめ
OTA アップデートは、正しく運用すればリードタイムを短縮し、ユーザーへ素早く修正を届けられる非常に強力な手段です。実運用での成功は理論ではなく習慣に依存します:
- プレビューで必ずテストする(必要なら staging を挟む)
- fingerprint でネイティブ変更を検出し、ビルドが必要か自動判定する
- 何がOTAで可能か、何が新しいビルドを必要とするかをチームで共有する
- expo-updates API を使い、必要なら即時適用を行う
- 段階的ロールアウトと迅速なロールバック体制を整える
これらをワークフローに組み込めば、チームは毎日安全にデプロイできるようになります。