金曜日の午後、本番修正の準備は整った。しかし Capacitor アプリの live update 機構のせいで、デプロイは当てずっぽうのゲームになっている。あるデバイスは新しい bundle を報告し、別のデバイスは古いアセットセットのまま。ロールバック経路も、どのバージョンが先に届いたかで挙動が変わる。導入時、updater は小さな統合に見えた。だが差し替えるのは、差分をセットアップする作業に近い。旧アセンブリをきれいに取り除き、新しいアセンブリをそのベースに合わせ、すべての測定を再現可能にする必要がある。
この比較が重要なのは、更新システムが単なるダウンロードボタンではないからだ。バージョンを追跡し、bundle を再構築し、ネイティブと JavaScript の境界を適用し、パッケージが不完全または互換性がない場合の処理を決める。実務上の問いは、差分配信をどう差し替えるかだけではない。移行中に第二の障害モードを生み出さずに、updater をどう差し替えるかだ。
目次
新しいアップローダーが必要と気づく
最初の警告は、通常、運用上の謎として現れます。リリースはローカルテストを通過し、サーバーはアップロード成功を示しているのに、サポートにはユーザーが古い画面を見ているという報告が届きます。エンジニアリングチームは bundle 識別子、ネイティブビルドのバージョン、チャネル割り当て、デバイスログを比較し、最終的にアップローダーがローカルに保存されているものとは異なるベースに対してビルドされたパッケージを受け入れていたことを発見します。
これは、ギア設定が誤っている差動装置に似ています。部品は互換性があるように見えますが、組み立てると負荷下でノイズが発生します。Capacitor アプリでは、同様のノイズは、一貫性のないアセット状態、展開の失敗、無制限のリトライ、または期待される bundle の一部しか復元されないロールバックとして現れます。
有用な診断パスは、前提ではなく症状から始めます:
- バージョン不一致: インストールされたネイティブシェル、ダウンロードされた bundle、アップローダーのメタデータが、意図された互換性範囲について一致していることを確認します。
- ロールバックの不透明さ: 更新が失敗した場合、完全な以前の bundle に戻るか、古いファイルが残るかをテストします。
- 観測可能性の低さ: 影響を受けたデバイス、パッケージバージョン、チャネル、失敗したステージ、および最後に正常に起動したタイミングを特定できるか確認してください。
- 制御されていないターゲティング: 本番環境のユーザーが、beta、staging、特定の顧客向けストリーム用のアーティファクトを受け取っていないか確認してください。
- 非効率な配信: Webアセットが少ししか変わっていないのに、システムが完全なアーカイブを送信しているかどうかを測定します。この設計トレードオフの背景は、以下で解説しています。 delta updatesでペイロードサイズを削減する方法.

リプレイスを検討する前に診断を行う
アップデートの失敗をすべて、アップデーター自体の欠陥の証拠と決めつけないでください。ビルドアーティファクトの不備、チャンネルルールの誤り、署名シークレットの破損、ネイティブ互換性の変更でも、同じ症状が見えることがあります。まず失敗の経路を記録してください。パッケージの識別情報、ベースバージョン、デバイスの状態、アップデート適用後にアプリが正常に起動したかどうかを含めます。
仕組みが信頼できる答えを返せない、または安全にリカバリできない場合に、リプレイスは正当化されます。チームが、どのデバイスがbundleを受け取ったか証明できず、不良リリースをすぐに無効化できず、ベースが欠けている場合に完全なパッケージへフォールバックできる保証もないなら、設定をいじっても次のインシデントを先送りするだけかもしれません。
エンジニアリングの原則はギアアセンブリと同じです。ハウジングを外す前に、故障したコンポーネントを確認してください。完全なアップデータへの移行は初期コストが高くなりますが、不確実なインストールの上に調整を重ねるのではなく、クリーンなプロトコル、明示的なバージョンルール、テスト可能なロールバック境界を提供します。
旧メカニズムの安全な除去
除去は制御された分離手順として扱ってください。目標はパッケージを削除して、次のビルドで何が残っているかを確認することではありません。目標は、2つの更新メカニズムが同じWebディレクトリ、スタートアップフック、設定キー、または配信エンドポイントで競合することを防ぐことです。
まず、移行ブランチを凍結し、現在の動作を記録してください。アクティブなプラグインバージョン、ネイティブ設定、環境変数、CIコマンド、チャネル名、ダウンロードされたバンドルの保存場所を保存してください。最初のクリーンビルドが実行されたとき、意図的な変更と偶発的な漏れを区別するために、このインベントリが必要です。
クリーンなベースラインの確立
使い捨てのブランチまたは作業コピーを作成し、依存関係ツリーとCapacitor設定を調査してください。「update」のような一般的な検索ではなく、実際のパッケージ名でレガシーアップデータパッケージを特定してください。レビュー package.json、ロックファイル、 capacitor.config.ts、iOSプロジェクト設定、Androidリソース、およびdownload、sync、install、rollbackメソッドを呼び出すスタートアップcode。
リポジトリで使用されているパッケージマネージャーで依存関係を削除してください。例えば、npmの場合:
npm uninstall <legacy-updater-package>
npx cap sync
プロジェクトでpnpmやYarnが標準化されている場合は、それぞれに相当するコマンドを使用してください。プレースホルダーは意図的に置いています。移行ガイドがパッケージ名を勝手に発明し、誤った依存関係に対して破壊的なコマンドを実行させるべきではありません。
次に、リポジトリ内で旧プラグインの識別子、API の呼び出し、エンドポイント名、設定キー、イベントリスナーを検索します。アプリケーションレイヤーから不要なインポートと初期化のcodeを削除してください。不要なラッパーも削除します。削除されたアップデーターを呼び出すローカル抽象化を残すと、ビルドは成功しても起動時の動作が失敗するため、後のデバッグが大幅に困難になります。
ネイティブの残骸をクリーンアップ
依存関係の削除後、iOSとAndroidのプロジェクトを開きます。パッケージマネージャーがクリアしなかったネイティブ参照を削除します。手動で追加したPod、Gradle依存関係、マニフェストエントリ、リソース値、URLスキーム、レシーバー宣言、初期化フックなどが含まれます。名前が関連しているという理由だけでファイルを削除しないでください。各ファイルがレガシーアップデーターにのみ属していること、共有のCapacitor設定がそれに依存していないことを確認してください。
代替品のインストール前に、ネイティブ依存関係の更新を実行し、両プラットフォームをコンパイルします。これにより、クリーンアップ失敗と統合失敗を区別できます。クリーンなネイティブビルドが移行の成功を保証するわけではありませんが、クリーンビルドが失敗することは、旧メカニズムが完全に無効化されていないことを証明します。
同じ規律がスクリプトにも適用されます。CIから古いuploadジョブ、エンドポイント変数、署名ステップ、アーティファクトのコピーコマンド、リリース通知を削除してください。本番環境のワークフローだけでなく、Pull Requestのワークフローも確認してください。見逃されたプレビュージョブが、テスト担当者が受け取るまで無害に見えたチャネルに互換性のないパッケージを公開してしまう可能性があります。
実践的なルール: 古いupdaterを変更し、同じコミットで新しいupdaterを追加する形で移行しないでください。削除を可観測にし、ビルドしてから、初めて代替手段を導入してください。
次に進む前に、クリーンアップされたアプリをテストデバイスにインストールし、旧サービスへの接続を試みなくなったことを確認してください。適切な場合はアプリデータをクリアし、起動ログを調査して、アプリがバンドルされたWebアセットを正常に読み込むことを確認してください。このベースラインは、次の段階のための対照サンプルとなります。アプローチのより広い比較については、以下を参照してください。 Capacitor、Ionic、Cordova向けのCodePush代替手段.
新プラットフォームのインストールと設定
代替updaterは、不透明なライブラリとしてではなく、明示的なプロトコルとしてプロジェクトに導入されるべきです。bundleが何を包含するか、どのネイティブバージョンがそれを適用できるか、チャネルがオーディエンスにどのようにマッピングされるか、保存されたベースが利用できない場合クライアントが何をすべきかを定義してください。インストール前にこれらのルールが明確でなければ、移行はハッピーパスのテストは通るものの、本番環境では安全ではありません。
クライアントを意図的に追加する
リポジトリのパッケージマネージャーを使用して新しいupdaterパッケージをインストールし、その後ネイティブプロジェクトを同期してください:
npm install <new-updater-package>
npx cap sync
実際のプロジェクトでは、ベンダーがドキュメントに記載しているパッケージ名と設定キーを使用してください。依存関係の変更は、無関係な Capacitor のアップグレードとは分けて行い、ネイティブビルドが失敗したときに原因を絞り込みやすくしてください。
アプリ起動時の予測可能なタイミングで updater を初期化してください。現在のネイティブビルド、アクティブな channel、ローカルの web bundle バージョン、アップデート確認に関するサーバーポリシーを把握しておく必要があります。アップデート呼び出しを複数の画面に分散させないでください。チェック、ダウンロード、検証、インストール可否の判断、エラー報告は単一のサービスに集約すべきです。
クライアントは JavaScript bundle の更新とネイティブアプリの更新を区別する必要があります。web bundle では、ネイティブプラグインの追加やプラットフォーム権限の変更はできません。また、新しいネイティブブリッジが存在すると安全に想定することもできません。互換性をリリースメタデータに埋め込み、サポート対象のネイティブ範囲外のパッケージは updater が拒否するようにしてください。
公開前に channel を作成する
内部テスト、ステージング、本番用に別々のストリームを設定します。channel は単なるラベルではありません。どのユーザー層がパッケージを受け取れるかを決めるガードレールであり、起動、ナビゲーション、認証、オフライン動作、ネイティブブリッジ呼び出しを検証するための制御された環境を提供します。
プロモーションパスを文書で定義する:
- 開発 公開せずにローカルで build する。
- 内部 エンジニアリングと QA のデバイス向けに署名済みアーティファクトを受け取る。
- ステージング 本番に近いサービスやリリース設定で動作を確認する。
- プロダクション 以前のチェックに合格した成果物のみを受け入れます。
可能であれば、チャネル割り当てをアプリケーションのバンドル外に保持し、ハードコードされたテスト値ではなく、管理された環境設定を使用してください。本番ビルドが誤ってベータストリームを指すのは、リリースプロセスの失敗であり、ユーザーの誤操作ではありません。
バンドル境界の保護
サービスは、トランスポート最適化で変更ファイルのみを送信する場合でも、完全かつ決定論的なWebバンドルを公開する必要があります。成果物はクリーンなワークスペースから生成し、意図的な理由がない限りソースマップや開発ファイルを除外し、結果のパッケージのコンテンツアイデンティティを記録してください。
インストール前に完全性検証を必須にしてください。クライアントはダウンロードされたデータを検証し、期待されるディレクトリを再構築し、アトミックに切り替える必要があります。検証に失敗した場合は、現在動作しているバンドルを保持し、診断に必要な十分なコンテキストとともに失敗を報告してください。
安全な更新タイミングやネイティブの制限を含む、より広範なOTAモデルについては、 OTA更新の実行方法.

設定はリポジトリではなく、プラットフォームのシークレットマネージャーや保護されたCI変数に保存してください。ビルドジョブの読み取りアクセスと、リリースの削除や本番ターゲットの変更ができる管理権限を分離してください。アップデータは完全パッケージのフォールバックも公開する必要があります。デバイスに必須のベースバージョンがない場合、システムはアンアンカリングされたパッチの適用を試みないようにしてください。
ビルドとCIパイプラインの調整
実験的な用途では手動公開でも許容されますが、リリースシステムとしては危険です。信頼できるパイプラインは、ソースのリビジョンを既知の成果物に変換し、その成果物に対してチェックを実行し、署名し、意図したチャネルに公開し、結果を記録します。ジョブが開発者のワークステーションで実行されるか、ホストされたランナーで実行されるかに関わらず、同じ入力から同じリリース判断が得られるべきです。
クリーンな環境で成果物をビルドする
再現可能な手順から始めましょう。ロックされた依存関係をインストールし、リンティングとテストを実行し、Webレイヤーをビルドし、生成されたファイルからCapacitor出力を作成します。開発者の作業ディレクトリに対して差分を計算しないでください。CIによって生成された2つの不変のバージョン管理されたバンドルを比較してください。
実用的なパイプラインは関心事を分離します:
- 検証: アップロード前に、ユニットテスト、型チェック、リンティング、本番環境向けWebビルドを実行します。
- 互換性: バンドルが要求するネイティブ機能が、ターゲットアプリのビルドと一致することを確認します。
- パッケージング: 決定論的なアーカイブまたはファイルマニフェストを作成し、一時ファイルを除外します。
- 署名: CI環境が保持する保護された認証情報でリリースに署名します。
- 公開: ブランチ、タグ、またはリリース承認によって選択されたチャネルに成果物を送信します。
- 本人確認: 配信サービスにクエリを実行し、公開されたバージョン、チェックサム、チャネルが一致することを確認します。

バージョンのアイデンティティを明確にする
ソースリビジョン、ネイティブ互換性、公開されたバンドルを結びつけるリリース識別子を使用してください。トラブルシューティング時に「latest」のような変更可能なラベルに依存しないでください。アップデーターは正確にインストールされたバージョンと正確な候補バージョンを報告する必要があり、リリースシステムは意図的にロールバックや再公開を行うために十分な履歴を保持する必要があります。
有用なマニフェストには以下を記録します:
| 項目 | なぜ重要か |
|---|---|
| バンドルバージョン | インストール済みのWebアセットと候補のWebアセットを特定します |
| ネイティブ互換性 | Web bundleが利用できないbridge code を前提とするのを防ぎます |
| チャンネル | 対象となるユーザー層を定義します |
| ベースバージョン | 差分再構築の基準点となります |
| 整合性値 | 破損や改ざんを検知します |
| リリースステータス | プロモーション、一時停止、ロールバックの判断をサポートします |
ベースバージョンは、差分配信において特に重要です。RFC 3229では一般的なモデルが記述されています。クライアントが保持しているリソースを特定し、差分アルゴリズムをリクエストすることで、完全なリソースではなく変更分のみを受け取る仕組みです。アプリケーションレベルでも同様に、デバイス上のベースバージョンが有効な場合にのみパッチを計算し、無効な場合はフルbundleを送信するようにすべきです。
公開パスのセキュリティ強化
CIランナーを権限付きリリースマシンとして扱います。本番環境への公開をトリガーできる者を制限し、署名情報をログに残さないようにします。プラットフォームのシークレット管理機能で認証情報をローテーションし、信頼できないプルリクエストがリリースシークレットにアクセスできないようにします。 DevArmorのCI/CDセキュリティプラクティス は、権限、シークレット、パイプライン境界の強化に有用な文脈を提供します。
本番環境の前に、ドライランまたはステージング公開ステップを追加します。クリーンなシミュレーターまたはデバイスでダウンロードされた成果物をテストし、初回起動、再起動、ダウンロード中断、署名拒否、ロールバックの動作を確認します。可能であればリリースジョブを冪等に保ちます。失敗したジョブを再実行しても、識別子が重複する曖昧なパッケージの集合が作成されるべきではありません。
具体的な実装パターンについては、 Capacitor CI/CDパイプラインセットアップガイドを参照してください。目的は単にアップロードを自動化することではありません。ソースコミットからデバイス状態まで、すべてのリリースを監査可能にすることです。
ロールアウトと成功の監視
ロールアウトは、アップデーター設計が負荷の下で試される場です。初日には、ステージングでは問題なく見えたパッケージが、古いベースのデバイス、ダウンロードの中断、ストレージ不足、QAマトリックスでカバーしきれなかったアプリバージョンに直面します。パーセンテージベースのターゲティングは調整ダイヤルとして扱い、実際の安全機構は、プロモーションを止めてユーザーを既知の正常なbundleに戻せることだと考えてください。新しいデファレンシャルギアセットを組むときと同じようにロールアウトしてください。まずは制御されたフィッティングチェックから始め、トラブルの兆候に耳を澄ませ、噛み合わせが安定したと確認できてから本格的に広げてください。
アップデート経路全体を監視する
転送プロセスだけでなく、経路全体を監視してください。デバイスは全バイトをダウンロードできたとしても、署名検証、展開、アクティベーション、あるいは入れ替え後の初回起動で失敗することがあります。各ステージを、bundleバージョン、ネイティブアプリのバージョン、channel、プラットフォーム、プライバシーに配慮したデバイス識別子、チームが対応できるエラーカテゴリと共にログに記録しましょう。
最低限のシグナルは、次の運用上の問いに答える必要があります:
- デバイスは候補となるアップデートを検知したか?
- 完全なbundleかdifferentialパッチのどちらを受信したか?
- 整合性検証はパスしたか?
- アクティベーションは完了したか?
- その後、アプリは正常に起動したか?
- 次回の通常のセッションでもクライアントは正常に動作しているか?
このイベントの履歴があれば、各チームが必要な情報を得られます。サポートには、1台の端末で何が起きたかを説明するためのデバイスごとのタイムラインが必要です。リリース責任者には、フリート全体の採用率、失敗率、ロールバック傾向が必要です。実用的なモデルとして: Capacitor アプリでの OTA アップデート成功のトラッキング これらのステージにきれいにマッピングでき、ダッシュボードの設計がはるかに容易になります。

ロールバックをリリース機能として活用する
公開前にロールバック条件を設定してください。適切なトリガーには、起動失敗の増加、繰り返し発生する整合性エラー、候補バージョンに留まるデバイスの増加、特定のプラットフォームに限定されたクラッシュパターンなどが含まれます。デバイスに最終確認済みの正常なバンドルを保持し、App Store のリリースを待たずに逆転可能な切り替えを実現します。
ロールバックには証拠の保持も必要です。失敗したパッケージのメタデータとログを保持し、チームが問題を再現し、影響を受ける互換性範囲を特定し、以前の割り当てを復元するか修正された成果物を配信するかを判断できるようにします。ユーザーへの症状を解消するだけで痕跡を消去するロールバックは、次のリリースで同じ失敗が再発する原因になります。
差分配信の背後にあるトランスポートモデルは、長い間存在してきました。 HTTP デルタエンコーディングは、2002年1月に Internet Engineering Task Force によって公開された RFC 3229 により、正式な Web 標準となりました。、および RFC 3229 仕様 1つの実用的な理由で依然として有用です。デルタは、クライアントに有効なベースバージョンがあり、結果の再構築コストが完全なリソースの送信コストより低い場合にのみ役立ちます。
そのため、汎用的なHTTPデルタの挙動に頼るよりも、アプリケーションを認識するアップデーターの方がうまく機能します。サービス側はベースとなるbundleを把握しており、クライアント側は再構築された出力を検証でき、アップデーターは適合しない場合にフルパッケージへフォールバックできます。エンジニアリング上の判断基準は、配信プロトコルが互換性、完全性、ロールバック、オブザーバビリティを明示的に扱えるかどうかであり、その答えが差分配信の有効性を左右します。