すべてのモバイルチームが知っておくべき5つのOTAアップデートのベストプラクティス
Product • React Native • February 24, 2026 • 16分で読める
Ishika Chakraborty Marketing
React Nativeチーム向けのOTAアップデートのベストプラクティス:プレビューチャンネル、フィンガープリント検出、expo-updates API、段階的ロールアウト、高速ロールバック。
すべてのモバイル開発者が知っている瞬間があります:本番環境でバグを発見し、修正の準備ができて、今度はデプロイボタンを見つめながら何が間違っているのかを考えている瞬間です。
Over-the-air(OTA)アップデートはその方程式を変えます。新しいバイナリ、ストア申請、待機時間なしに、JavaScriptとアセットの変更をユーザーのデバイスに直接配信できます。React Nativeチームにとって、これは利用可能な最も強力なツールの1つです。
そして、私たちが話すほとんどのチームはすでにそれらを正常に使用しています。アップデートが送信され、ユーザーがそれらを受信し、アプリは動き続けます。しかし、「動作する」と「信頼する」は異なることです。
1日に複数回配信しても汗をかかないチームは、魔法的なことをしているわけではありません。彼らはいくつかの重要なパターンの周りに習慣を築いているだけです。本番環境に到達する前にアップデートをテストする方法を知っています。どの変更がOTAで安全にプッシュでき、どの変更が新しいビルドを必要とするかを知っています。予期しないことが起こったときにブラスト半径を制限する方法、そして時間ではなく分でロールバックする方法を知っています。
この投稿では、理論的なアドバイスではなく、チームが本番環境を壊すことなく毎日配信できる実際のワークフローである、これらのパターンの5つを共有します。
クイックプライマー:OTAアップデートの仕組み
Expo Updateにすでに慣れている場合は、これをスキップしてください。
Expo Updateは、アプリのJavaScriptバンドルとアセットをネイティブコードとは別に更新できます。アプリが起動すると、サーバーで新しいアップデートをチェックします。存在する場合は、バックグラウンドでダウンロードし、次のコールドスタートで適用します。
アップデートは、チャンネル(ビルドが指すもの)とランタイムバージョン(JavaScriptがネイティブバイナリと互換性があることを保証する)によって整理されます。
Expo Updateでは、チャンネルは舞台裏でブランチにマップされますが、ほとんどの場合ブランチを無視してチャンネルと直接作業できます。
expo-updatesパッケージを使用すると、ユーザーが修正をすぐに確認する必要がある場合に、オンデマンドでアップデートをチェックして適用できます。
TL;DR:OTAアップデートチェックリスト
流し読みしている場合は、重要なことは次のとおりです:
✓ プレビューと本番チャンネルを使用する;本番に公開する前に常にプレビュービルドでテストする
✓ フィンガープリントを使用してネイティブの変更を検出し、アプリバージョンを更新するタイミングを知る
✓ ビルドが必要なものとOTAで更新できるものを知る
✓ expo-updates APIを使用してアップデートを検出、ダウンロード、適用する—ユーザーが次のコールド起動よりも早く修正を取得できるように
✓ 段階的ロールアウトを使用し、ロールバック方法を知る
今度は、それぞれを詳しく見てみましょう。
1. 本番に公開する前にプレビューでテストする
シンプルなフロー:プレビュー → 本番
ほとんどのチームはここから始まり、うまく機能します。重要な2つのチャンネルがあります:
- Preview:内部テスト用(多くの場合、ステージングAPI、TestFlight/内部トラックを指す)
- Production:ユーザーが取得するもの
ワークフローは簡単です:
eas update --channel preview --message "Fix login bug"
eas update --channel production --message "Fix login bug"
これは、正確にテストされたアーティファクトを昇格させるのではなく、新しいバンドルを本番に公開します。それで問題ありません。同じコミットから公開しており、意味のあるドリフトのリスクは低いです。本番に影響する可能性のあるほとんどの問題(環境変数、バンドラーの癖、アセットの問題)は、プレビューテスト中に表面化します。
なぜこれが実際に重要なのか
- ユーザーに到達する前に問題をキャッチする
- プレビュービルドは本番に影響を与えることなくステージングAPIを指すことができる
- 習慣になるほど十分にシンプル
さらに進む:ステージング昇格ワークフロー
一部のチームは追加の保証を求めています:QAが承認した正確なアーティファクトがユーザーが受け取るものです。「同じコード」は常に「同じビルド出力」ではありません。バンドリングは物事がドリフトする可能性があるもう1つのステップです。
これには4番目の環境が必要です:本番と同じように構成されているが(同じAPIサーバー、同じ機能フラグ)、内部テスターにのみ配布されるステージングチャンネル。
eas update --channel staging --message "Fix login bug"
eas update:republish --channel staging --destination-channel production
これが意味をなすとき:
- テストされたアーティファクトに関するコンプライアンスまたは監査要件がある
- 以前に微妙なバンドリングの違いに悩まされたことがある
- チームが「再度公開」が調整リスクを導入するほど大きい
必要なセットアップ:
- 4つのアプリバリアント:development、preview、staging、production
- ステージングは本番構成を正確にミラーリングする必要がある(APIエンドポイント、機能フラグなど)
ほとんどのチームにとって、シンプルなプレビュー → 本番フローで十分です。追加の保証が必要な場合にステージング昇格レイヤーを追加してください。
2. フィンガープリントを使用して新しいビルドが必要な時期を知る
問題:
すべての依存関係の更新がJavaScriptのみではありません。ネイティブの変更を必要とするOTAアップデートを古いバイナリのユーザーに配信すると、アプリがクラッシュします。
なぜこれが重要なのか:
ランタイムバージョニングは互換性ファイアウォールです。どのバイナリが特定のアップデートを読み込めるかを決定します。最も一般的な本番障害は?チームが依存関係が実際にネイティブコードを変更したときにJS専用だと仮定することです。
推奨アプローチ:appVersion + フィンガープリント検出
ランタイムバージョンポリシーとしてappVersionを使用します。これにより、ランタイムバージョンがアプリバージョン(1.0.0、1.1.0など)に結び付けられ、どのビルドがどのアップデートを受信できるかを簡単に推論できます。
{
"expo": {
"runtimeVersion": {
"policy": "appVersion"
}
}
}
重要な質問:アプリバージョンをいつバンプするかをどのように知るのか?
ここでフィンガープリンティングが登場します。ランタイムポリシーとしてではなく、検出ツールとして。
Expoのフィンガープリントは、コミット間でプロジェクトのネイティブサーフェスエリア(依存関係、構成、ネイティブコード)を比較します。フィンガープリントが変更された場合、ネイティブコードが変更され、OTAアップデートを配信する前に新しいビルドが必要です。
フィンガープリントを使用してOTAアップデートをゲートする:
eas fingerprint:compare --build-id <BUILD-ID>
フィンガープリントが一致する場合、アップデートはOTAで配信しても安全です。異なる場合は、最初に新しいビルドが必要です。
CIでこれを自動化:
変更がOTAアップデートとして送信できるか、新しいビルドが必要かを自動的に決定するフィンガープリントチェックをCIパイプラインに追加できます。これにより、手動の推測が排除され、互換性のないアップデートがユーザーに到達することを防ぎます。
実際の例:
Hipcampはフィンガープリントを使用して、変更がOTA安全かどうかを自動的にチェックします。フィンガープリントが一致する場合、OTA経由で配信します。そうでない場合は、次のビルドを待ちます。「これにより、手動チェックと潜在的な人的エラーが排除されます」と彼らは言いました。
アプリバージョンをバンプするタイミング:
- ネイティブコードを持つ依存関係を更新する
- Expo SDKをアップグレードする
- ネイティブ構成に影響するapp.json設定を変更する(アイコン、スプラッシュ、エンタイトルメント、スキームなど)
- ネイティブプロジェクトファイル(ios/、android/ディレクトリ)を変更する
フィンガープリントツールはこれらすべてをキャッチします。ワークフローに組み込めば、覚える必要はありません。
3. Over-the-airで更新できるものを理解する
最もよく聞かれる質問:「アップデートを配信したらアプリが壊れました。何でも更新できると思っていました?」
なぜこれが実際に重要なのか
- チームが新しいバイナリを暗黙的に必要とする変更をプッシュする「希望的OTA」を防ぐ
- プロダクト/エンジニアリングに明確なリリース決定を与える:「今すぐOTA」対「ストアリリース」
アップデートに含めることができるもの:
- JavaScriptコードとビジネスロジック
- UIコンポーネント、スタイル、レイアウト
- 画像、フォント、その他のアセット
- バグ修正とコピーの変更
新しいビルドが必要なもの:
- ネイティブモジュールのインストールまたはアップグレード(ネイティブコードを持つexpo-*パッケージを含む)
- ネイティブコンパイルを必要とする構成変更(アプリアイコン、スプラッシュスクリーン、ネイティブエンタイトルメント、構成プラグイン)
- 新しいネイティブ権限(カメラ、位置情報、通知など)
- Expo SDKアップグレード(管理されたワークフローのみを使用している場合でも)
- ネイティブコードに影響するapp.json構成変更(スキーム、関連ドメイン、インテントフィルターなど)
- ベアワークフローでのネイティブプロジェクトファイルの変更(ios/、android/ディレクトリ)
- React Native自体の更新
技術的な観点から、JavaScriptまたはアセットの場合、OTA経由で配信できます。ネイティブコードに触れる場合は、新しいビルドが必要です。
フィンガープリントに決定させる
これらのルールを覚える必要はありません。フィンガープリントは、プロジェクトのネイティブサーフェスエリアを本番ビルドと比較することで、ネイティブの変更を自動的に検出します。フィンガープリントが一致する場合、OTAで配信しても安全です。一致しない場合は、最初に新しいビルドが必要です。
EAS Workflowsで自動化:
最良のセットアップは推測を完全に排除します。EAS Workflowsを使用すると、すべてのプッシュでフィンガープリントチェックを実行し、変更を適切なパス(OTAアップデートまたは新しいビルド)に自動的にルーティングできます。
name: 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へのすべてのプッシュでフィンガープリントをチェックします。ネイティブコードが変更され、互換性のあるビルドが存在しない場合、新しいビルドをトリガーします。互換性のあるビルドがすでに存在する場合、OTAアップデートを公開します。手動チェックなし、推測なし、アップデートとワークフローはより良く連携します。
コンプライアンスについて:
両方のストアにはOTAアップデートのガイドラインがあり、それらを直接読むことが重要です。完全なストアポリシーは次のとおりです:Appleの開発者プログラムライセンス契約とGoogle Playのコードダウンロードに関するポリシー。
4. expo-updates APIを使用してアップデートをより速く配信する
何が起こるか:
ユーザーがOTAアップデートを公開した後にアプリをインストールします。アプリを開くと、アップデートがバックグラウンドでダウンロードされますが、次のコールドスタート(強制終了して再起動)まで適用されません。
これがデフォルトであり、推奨します。アプリの起動パフォーマンスはエンゲージメントにとって重要です。ユーザーは埋め込まれたバンドルで高速な初回起動を取得し、2回目のセッションまでに最新バージョンを持つことになります。
より多くの制御が必要な場合、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は回復力があるように設計されています。アプリがオフラインであるか、アップデートサーバーに接続できない場合、持っている最新のアップデートを使用し続けます。アプリは壊れません—ユーザーは単に新しいアップデートを取得できません