- レガシーAndroidアプリの targetSdkVersion をGoogleの要件に合わせて引き上げる小さな保守作業が、Android 13での本番クラッシュと長いGoogle Play審査待ちに発展
- 既存アプリは、ターゲットレベルより高いAndroidバージョンの端末ユーザーに引き続き提供されるために、API level 31以上をターゲットにする必要があり、筆者はAPI level 30から33へ引き上げて配布した
- 最初のリリースは1時間以内に承認されたが、Android 13の実機でログイン直後にクラッシュが発生し、以前の正常版へロールバックする方法がなかった
- 修正版ビルドはコード変更後すぐに提出されたが、約72時間審査待ちの状態が続き、その後9月4日にようやく、11日後に本番公開された
- ネイティブモバイルアプリは、ソースコードとインフラを自社で運用していても、最終的な配信チャネルをアプリストア運営者が管理するため、緊急修正、ロールバック、配布速度に制約を受ける
小さなSDK更新が本番障害に発展
- 保守対象は顧客企業が使うレガシーAndroidアプリケーションだった
- 機能は何年も前に完成しており、活発に開発中のアプリではなかった
- 理想的には、安定版をそのままにしておきたい状況だった
- Googleは2023年8月18日のメールでAndroid APIレベル要件を通知した
- Googleの案内の要点は、既存アプリがより高いAndroidバージョンの端末ユーザーに引き続き提供されるには、API level 31以上をターゲットにする必要があるという内容だった
- 既にインストール済みのアプリに及ぶ正確な影響は明確ではなく、より安全な解釈に従って更新を優先した
- 期限まで3週間を切っており、Googleはこの要件について、それ以前のリマインダーを送っていなかった
targetSdkVersion 30から33へ変更
- 作業は8月23日に始まり、アプリの targetSdkVersion をAPI level 30から33へ引き上げた
- 最初のビルドは、互換性のない分析系依存関係のため失敗した
- その依存関係はビジネスロジックと密接ではなかったため、削除できた
- その後、Androidエミュレーターでアプリを実行し、主要機能は以前と同じように動作しているように見えた
- この変更は、新しいビジネス価値を提供しないポリシー対応の更新だった
最初のGoogle Play配布とAndroid 13でのクラッシュ
- Google Playでの配布プロセスは、当初は順調だった
- レガシーアプリは年に1、2回しか更新されていなかったため、複数のアンケートを完了する必要があった
- リリースは1時間以内に審査を通過し、本番環境に配布された
- その日の夜21:30頃、顧客企業が新しいアプリバージョンでログインできないユーザーを報告し、問題が明らかになった
- 実際のAndroid端末でテストすると、ログイン直後にアプリケーションがクラッシュした
- 追加調査の結果、当時最新のAndroidバージョンである Android 13 で問題が発生し、以前のバージョンでは正常に動作することが分かった
- 初期作業では古いエミュレーターイメージだけでテストしていたため、互換性問題が表面化しなかった
- 複数のAndroidバージョンでテストしていれば、問題を見つけられたはずだった
- 変更範囲が小さく、期限のプレッシャーがあったことが誤った自信につながったが、テストカバレッジ不足はチームのミスだった
ロールバックができない配布構造
- 最も安全な初動対応は、以前の正常なリリースを復元し、翌営業日にクラッシュを調査することだったが、Google Play にはその選択肢がなかった
- 最新の本番リリースを取り下げ、以前のバージョンを再び有効化する方法がなかった
- 次の手として、targetSdkVersionを30に戻し、アプリバージョンを上げて新しいビルドを公開しようとしたが、Google Playに拒否された
- 公開されていた案内では9月1日までより低いターゲットを許可するように見えたが、新しい更新はAPI level 33をターゲットにしなければならないというエラーが発生した
- API level 30を11月1日まで許可する延長をリクエストしたが、公開エラーは変わらなかった
- 取れる道は、Android 13のクラッシュを修正して新しいリリースを提出することだけだった
プレッシャー下での修正と長い審査待ち
- 壊れたバージョンは自動更新を通じてユーザーへ段階的に配信されており、修正版ビルドが早く本番環境に届くほど、影響を受ける顧客数を減らせた
- クラッシュはAndroid 13エミュレーターで再現可能で、必要なコード変更は多くなかった
- ただし深夜に近い時間帯にプレッシャーの中で作業する必要があり、包括的な回帰テストを行う時間は限られていた
- 当時の計画は、実用的な対応に近いものだった
- 既知のクラッシュをすべて修正
- 最も重要なフローをテスト
- 修正版を即時提出
- より広範なテストを続け、必要なら追加更新を準備
- 新しいビルドはGoogle Playに提出されたが、2時間経っても審査待ちの状態で、01:00頃にも承認されていなかった
- 翌朝になっても、アプリは in review 状態のままだった
- その日の大半をAndroid 13のテストとGoogle Play Consoleの確認に費やし、ログインクラッシュほど深刻ではない小さな問題をいくつか見つけて修正した
- その日が終わるまでに、本番環境向けの修正版は承認されなかった
- 当時、修正版ビルドは約72時間待機中で、コードは修正済みだったが、配布は不透明な審査キューに縛られていた
その後の更新:審査、サポートチャット、段階的配布
- 8月27日にHacker Newsへ記事を投稿し、議論にはAndroidのリリース戦略と批判がともに寄せられた
- 記事はフロントページに載り、数日間で約13万人のユニーク訪問者を得た
- 月額約€5のVPSで運用していた静的サイトはトラフィックをさばいた
- この注目はGoogleの反応にはつながらず、アプリ更新は引き続き審査中だった
- 9月1日、Google Play Consoleの疑問符アイコンの奥にあるサポートチャットを見つけた
- サポート担当者は審査を迅速に進めると約束したが、明確な日程や解決策は提示しなかった
- 大きなHacker Newsの議論で、この経路に言及した人がいなかった点が目立った
- Googleサポートにアクセスしにくいという評判のため、開発者が直接の支援を期待していないのかもしれない
- 9月4日、修正更新は11日間の審査の末に最終的に公開された
- その後、すぐに100%のユーザーへ配布するのではなく、段階的配布を使ったより小さな後続リリースを提出し、この更新は1時間以内に審査を通過した
- 9月7日には、99.9999%のユーザーに到達していたバージョンを置き換えるため、さらに小さな更新を提出した
- 新バージョンが審査に入ると、Googleは警告なしに以前のロールアウトを自動停止した
- 新しい審査に数日かかっていれば、残りのユーザーには以前のリリースも提供されない状況になっていた
- 新しい提出を取り下げても、以前のロールアウトは復元されなかった
- 幸い、この審査は1時間以内に終わった
プラットフォームの管理が変える本番責任
- モバイル開発者は、GoogleやAppleが緊急の本番修正を遅らせたり、アプリを削除したり、決定についてほとんど説明を提供しなかったりする類似事例を数多く経験してきた
- 技術チームが不具合を素早く診断して修正しても、ユーザーが解決策を受け取る時点はアプリストア運営者が管理する
- 一般ユーザーには、ロールバック、代替の配布経路、信頼できる審査迅速化の方法がない場合がある
- ソースコードとインフラを所有していても、最終的な配信チャネルを管理できなければ、本番所有権の性質は変わる
- ネイティブモバイルアプリが必ずしも必要でない場合、オープンWebプラットフォームを選好する理由は大きくなる
- Webアプリケーションは必要な機能を提供しながら、配布、モニタリング、ロールバックをチームが直接実行できる場合が多い
- ネイティブアプリにも有効なユースケースはある
- 端末機能への依存
- バックグラウンド動作
- アプリストア配布が必要な場合
- モバイルを選ぶことは、プロダクト上の利点とともに、強力な第三者がリリース過程に入り込む運用コストを受け入れる決定でもある
1件のコメント
Hacker News のコメント
このメールは個人でも仕事でも受け取った
個人では、16都市の公共交通システム向けにオープンソースアプリを自主的に作って運用しているのだが、誰の得にもならない作業のために 16個のアプリを2週間以内に更新しなければならなかった
自分のアプリは PWA で、Android 版は Cordova といくつかのプラグインでネイティブのオプションを少し追加した形なのだが、新しい対象 Android API に対応するため Cordova を上げたところ、まだ更新されていないプラグインが壊れて、週末ずっと作業とテストをする羽目になった
正直、アプリをなくしてユーザーにウェブサイトへ行って PWA をインストールしてもらいたいが、平均的なユーザーはまだその方法を知らない。ユーザーがアプリを探す最初の場所も、いまだに Play Store だ。Google が PWA をアプリストアに直接提出できるようにしてくれるだけでもありがたいし、これを毎年繰り返したくはない
仕事でも慌ただしく対応している。年末まで一部のサポート顧客が使うレガシーアプリがあるのだが、かなり複雑なアプリなので、対象 API バージョンを変えただけで基本テストの時点であちこちが壊れた。延長は受けたが、Google の機嫌を取る以外には何の機能変更もない更新に、開発者1〜2週間と QA 1〜2週間がかかることは分かっている。すべての顧客が新アプリへ移行したら年末にストアから正式に削除する予定のアプリなのに、だ
https://rangle.io/blog/publishing-a-web-app-to-the-play-stor...
https://developers.google.com/codelabs/pwa-in-play#0
ただし、この締め切りは数か月前から継続的に案内されていたし、いずれもっと露骨に表示されるのは明らかだった。文言自体は気に入らなかったし、本番版は問題ないのにテスト版が基準を満たしていないと文句を言うのも筋が通らなかった
また、常に新しい更新をプッシュする問題であり、毎年こういう形だ。アプリをしばらく公開状態のままにしておくことはできた
だから 2週間しかなかった というのは正確ではない
Android 開発の難しさには同意するが、投稿者は大きなミスを2つしている
1つ目は、最新の Android バージョンでアプリをテストしていなかったこと。これは非常に大きなミスで、私たちがサポート対象のすべての Android バージョン向けに 11個の仮想マシン を維持している理由だ
2つ目は、Google Play にアプリを配布する際、最初からユーザーの100%に配布しては絶対にいけないこと。段階的ロールアウトが基本で、私たちはリリースを公開した後、最初はユーザーの10%を超えないようにしている。自信が持てたら、100%ではなく99%に配布する。そうしておけば、何らかの理由で配布を止める必要が生じた時、100%に配布されていない限り簡単に止められる
好むと好まざるとにかかわらず、この2つの方法は経験豊富な Android 開発者ならよく知っている戦術だ
しかし、その程度に複雑な体制を整えてもミスは起きるし、本当の問題はリリースを取り消したり、中止したり、ロールバックしたりする方法がないことだ
この状況で、段階的ロールアウトはすべての顧客にどう役立つのか。エンドユーザーが壊れたバージョンを受け取った時、正常なバージョンを受け取る方法はあるのか?
元記事の筆者を責め立てる反応が理解できない。もちろん、もっと慎重にできたかもしれないし、最新の Android でログインをテストできたかもしれない。だが、もしログイン時のクラッシュではなかったら? 最新版では動くが、別のバージョンでは動かなかったら? どこで線を引くべきなのか?
どこかの時点で、「これは文字どおり数百万のアプリと数百万の開発者が使うプラットフォームであり、ミスは起こり得る。自分で配信を止めたり段階的ロールアウトのようなコツを知らなくても、すでに承認済みの以前のバージョンをすぐ再公開する、といった形で簡単に直せるべきだ」と言うべきだ。特にアプリストアのような場所では、元に戻せて復旧可能にしておくことが基本的な設計原則だ
Google が期限を設定したし、繰り返すが、私は 8月18日に初めて聞いたのであって、それ以前には知らなかった。変更は当時ささいに見え、アプリは古い Android バージョンで以前どおり動作していたため、ここまで悲惨に失敗するとは予想していなかった
私は熟練した Android 開発者ではないが、ソフトウェア開発全般では15年以上の経験があるので、物事がどう動くか、何を期待し何を恐れるべきかについては自分なりの感覚がある。最新リリースを部分的にでも「取り消す」可能性を残すには、99.99999999% の段階的ロールアウトがベストプラクティスだなんて、本当に知らなかった
最新リリースをキャンセルしたり削除したりして、以前の正常なバージョンに戻す方法がないとは夢にも思わなかった。こういう状況を経験して初めて学ぶものなのだと思う
すべての機能をすべての Android バージョンでテストしなかった私を責めることはできるし、私もそう思う。ただ、目を開いて、現在の Play Store のリリース処理方式が、Play Store の外ならまともな人がやるやり方ではないという点を理解してほしい。誰もがいつかこうした問題に遭遇し得るし、この記事とこのスレッドが、同じような状況に陥る開発者の数を減らしてくれればと思う
新リリースに対するスモークテストは、基本的な職業倫理だ
ロールバックを備えた段階的ロールアウトも新しい概念ではない
「オープンな Web 標準に戻ってコントロールを取り戻そう」という言い方は、Web もまた、今問題を引き起こしている企業である Google が半ば支配していると知れば変わるかもしれない。iOS で事実上唯一のブラウザである Safari を持つ Apple も、特に味方というわけではない
追跡をしやすくするためだけに存在するようなアプリよりは、Web サイトのほうを間違いなく好むが、この記事は保守を半ばおざなりにした事例として読めた
これは勝ち目のない状況だ
Microsoft は後方互換性にものすごい労力を注いでいて、その点は大いに評価できる。だが、そのぶん攻撃対象領域も大きく広がる
同じように不当に叩かれがちな C++ の最悪な面の多くは、後方互換性に対する強硬な姿勢から来ている。可能な限り古いコード、さらには古い C コードでさえコンパイルされ続け、期待どおりに動作しなければならず、ソースコードを失った古いバイナリとリンクするところまで引き受けている
ほとんどのものはそこまで努力せず、保守性、信頼性、セキュリティを理由に古いものを無効化する
どの分岐点を選んでも、誰かは被害を受ける。そうでないなら、そのコードを誰も使っていないということだ
なぜ問題のあるリリースを「撤回」して、以前のリリースを最新バージョンとして表示し続けることができないのか? そうできれば、この問題の文脈ではほとんど解決するはずだ
ただし、同じシグネチャを持つ既存関数の挙動を変えるべきではない。API 提供者なら、コンパイルに意味を持たせるために最善を尽くすべきだ
多くの場合、まったく簡単ではないし、Play Store 版ほど使われることもないだろうが、逃げ道があるのは良いことだ
Android に配布されるアプリケーションの大半は Android バイトコードを対象にしている。ネイティブコンパイルされたアプリケーションではない
C++ が克服しにくいセキュリティ問題を生む理由は、その低レベルな性質と、ネイティブバイナリができたらそこで終わりだという事実にある
だがメモリ安全性のある言語のバイトコードなら、セキュリティ修正をバックポートできない理由がどこにあるのか? そうしたコードを実行すること自体が、バイトコードを継続的に再コンパイルする性質を持っている
この立場が Google にとってどれほどばかげているかを見るなら、JVM は今でも 1996 年に登場したJava 1.0 向けクラスを実行して利用できる
これはセキュリティ問題ではなく、「Google がプラットフォームをサポートしたがっていない」という問題だ
ネイティブコンパイルコードを含む成果物に対して Google がより厳しくなるのは、ある程度理解できる。だが「アプリが古い Android バージョンを対象にビルドされているので、もうサポートしない」という一括ポリシーは話にならない。セキュリティというより、古いアプリをストアから間引くための方法に見える
特にこうしたポリシーは、本質的に Android 開発者へ大きな保守負担を押しつける。アプリケーションをできるだけ広くサポートするには、可能な限り古い Android バージョンを対象にすることになる。最新の Android バージョンを対象にするとあまりに多くの顧客を排除することになるのではと考え、そうする人はほとんどいない
Google がセキュリティと最新 Android 技術への広いアクセスを真剣に考えているなら、Android ランタイムを OS バージョンから切り離そうとするはずだ。ART と Dalvik ランタイムを Android エコシステムの他の部分と同じように Google Play 経由で配布できない理由はない。「これを行うには Android 17 が必要です……ああ、ハードウェアメーカーがドライバを更新していませんね」といった愚かな状況もなくせる
だが、そうすると新端末の販売に打撃が出るので、許容できないのだろう
Play Store と、その中に閉じ込められた開発者「コミュニティ」が罠であることは、常に明白だった。強制的な API アップグレードは、その一側面にすぎない
スマートフォンやモバイルアプリが最も得意とする作業があるのは確かだ。たいていはナビゲーションのように移動に関係するもの、あるいはどちらが「上」なのかを判断するようにスマートフォンのセンサーに依存するものだ。だが、それ以外のほとんどはウェブサイトでできる
一人でクロスプラットフォームのモバイルアプリを保守している開発者なら、本当に大変だろうし共感する。ただ、私はずっと前に独占プラットフォームとゲートキーパーのゲームには参加したくないと決めたので、完全に外部の立場からしか共感できない
今のモバイルで簡単にできないものは、ほぼ唯一GPU コンピュートシェーダーくらいだ。だが WebGPU がデスクトップブラウザからやって来れば、それも崩れるだろう
もう1つ思い浮かぶのは、一般的なシステム全体のファイル管理だ。それはおそらく永遠に来ない気がする。File System API は特定のディレクトリへのアクセス権をユーザーに付与させることはできるが、その権限がページ更新後も持続するかどうかは分からない
Google の SRE 原則の一つが「ロールバックは正常」であることを考えると皮肉な話
https://cloud.google.com/blog/products/gcp/reliable-releases...
「Google における私たちの哲学は『ロールバックは正常』というものだ。新しいリリースでエラーが見つかったり、合理的に疑われたりする場合、リリースチームはまずロールバックし、その後で問題を調査する。ロールバックの要請は、リリースチームやバグのあるコードを書いた人への攻撃とは解釈されない。むしろ、ユーザーのためにシステムをできる限り信頼性の高いものにするための正しい行いだと理解される。ロールバックの変更リストに観測された問題が説明されている限り、誰も『なぜこの変更をロールバックしたのか』とは尋ねない」
より低い
versionCodeへダウングレードすることはできず、versionCodeは開発者が定義するAndroid 11 は、メディアの例外や、ローカルフォルダをクラウドストレージのように扱う
DocumentFileAPI の使用を除けば、スマートフォンの「永続」ストレージへの書き込みを禁止している。DocumentFileは多くのユースケースで使えない開発者は
hasFragileUserDataを設定でき、そうすると削除時にユーザーへデータを消すか尋ねる必要がある。しかし Google/Android のバグにより、このダイアログが表示されない場合があるアプリを削除したのにユーザーがデータを消さない場合、Android はより低い
versionCodeへのダウングレードを阻止するMANAGE_EXTERNAL_STRORAGEを要求すれば通常のjava.io.Fileを使えるGoogle Play は例外的な場合にのみ
MANAGE_EXTERNAL_STRORAGEを許可する私が作ったフレームワークベースのアプリ複数でも同じことが起きた。新しい API バージョンが既存の依存関係と衝突し、すでにあったまさにその状態へ戻すために、本当に途方もない作業をしなければならなかった
4年前に Android で書いたモバイルアプリよりも、90年代のものが最新の Windows でより問題なく動くMicrosoftを、はるかに尊敬するようになった
Windows 11 ストアで Adobe Flash が動かないからといって Microsoft を責めたりはしないでしょう?
アプリを単一の「自前で承認した」アプリストア経由でしか配布できないよう強制されることには、欠点があまりにも多く、利点はほとんどないと思う
利点と言えるものは皮肉にも、アプリストアの管理者が公開前にマルウェアやウイルスを確認してくれる点くらいだ。しかし、Google広告やGoogle/Facebookのトラッカーだらけのアプリは、あらゆる形で歓迎される
もう一つ、ユーザーがアプリを検索・インストール・更新しやすくなるという点もある。何十億ものゲームアプリや、広告を配信したり個人情報を集めたりするためだけに公開された、非常によく似たアプリの中から目当てのものを見つけられればの話だが
欠点は、アプリストア管理者の気まぐれに縛られることと、アプリ公開プロセスに対する制御権がないことだ。アプリを公開できるか、いつ公開できるかを自分では決められず、App Storeの管理者が自分にとって都合がよいかどうかで決める。この権利はユーザーだけが持つべきだと思う
商用のネイティブアプリ、たとえばホームバンキングアプリなら、開発者のWebサイトの個人向けエリアにログインしてダウンロードしたい。アプリはGPG署名され、パッケージの完全性を確認できるべきだ。自動更新機能も可能だ
オープンソースアプリにはF-Droidストアがあり、独自のリポジトリを追加できる。多少技術的で、誰にでも向いているわけではないことは認める。新しいスマートフォンにF-Droidがプリインストールされていれば大いに助けになるが、Googleが許すとは思えない
もう一つの現実的な選択肢はPWAだ。私の考えでは、ネイティブ機能を必要とするアプリはごく少なく、それ以外はほぼすべてPWA技術で実現できる。ユーザーに更新を配信したり、アップグレードを求めたりする必要もない
ユーザーの生活を楽にし、「より良いセキュリティ」を提供するという口実で運営される独占的で商業的なアプリストアは、AppleとGoogleが独立開発者の労働で何十億ドルも稼ぎ、Googleの場合はユーザーの個人情報を収集・販売し、あらゆる方法で広告を売ることを可能にする「素晴らしい」手段だ
80〜90年代にゲーム開発者たちが概して「Licensed by Nintendo」モデルに従い、2000年代末にモバイルアプリ開発者たちがiOS App Storeで同じことをした理由がこれだ。彼らはプラットフォーム所有者を迂回するためにユーザーと直接協力したいわけではない。プラットフォーム所有者にユーザーの手を縛ってほしいのであり、その過程で自分たちの手も縛られることを受け入れている
たとえば銀行アプリの場合を見てみよう。銀行は安全なリモート認証を望んでいる。アプリへのクレデンシャルスタッフィング攻撃を行うユーザーをブロックし、マルウェアが銀行アプリを開いて「詐欺師に送金」ボタンを押すのを防ぎ、ユーザーがタップ・トゥ・ペイ関連の暗号化シークレットを抽出するのを防ぎ、盗んだシークレットをスマートフォンアプリに注入することも防ぎたいのだ
アプリは、ユーザーの手が縛られているかを自力では検証できない。ブートチェーンやEL3に存在する、ユーザーに対してではなく銀行に対して信頼できる第三者が必要になる。だから単にアプリと署名を渡したいのではなく、Googleがユーザーの手を縛れるよう、Googleにやってほしいのだ
アプリストアのユーザー側の利点は、すべて後付けの正当化だという点が重要だ。最初から目標はユーザーを縛り付けることだった。彼らにとってユーザーは小さな泥棒蚊のような存在だからだ。これが彼らが大声では言わない「静かな部分」だ
どうかGoogleのWEI提案が現実にならず、PWAが証明機能にアクセスできないままであってほしい。Googleはすでに「未知のブラウザではログインさせない」というたわごとをやっており、その癌が広がる必要はない
F-Droidは素晴らしい。AndroidでGoogleがやっている悪ふざけは止められるべきだ。EUはそれをできたが、米国もそうすべきで、国際的にも適用されるようにすべきだ
[0] MPAAが非商業的な小規模コピー、つまりVCRでテレビ番組を録画することを呼んでいたコードネーム。Jack Valentiは本当にひどい人物だったよね?
モバイルアプリを開発すると決めた瞬間に、製品やサービスの制御権を第三者に渡すことになるという理由で、私も何年もモバイルアプリ開発に反対してきた
同意するのは難しくない
「サポート」がHNやTwitterで役に立つ注目を集めることを期待する程度のものなら、私たちは間違った道を進んでいる