メインコンテンツへスキップ

React Native 0.71-RC0 Android障害の事後分析

2023年1月27日 ·8分で読めます
Nicola Corti
Nicola Corti
ソフトウェアエンジニア @ Meta
Lorenzo Sciandra
Lorenzo Sciandra
シニアソフトウェアエンジニア @ Microsoft

0.71が利用可能になった今、2022年11月4日にReact Native & ExpoのAndroidビルド向けに最初の0.71リリース候補をリリースした際に、すべてのReact NativeバージョンのAndroidビルドを破壊したインシデントに関するいくつかの重要な情報を共有したいと思います。

このインシデントへの対処に協力したコントリビューターたちは、最近、事後検証ミーティングに参加し、何が起こったのか、そこから何を学んだのか、そして将来同様の障害を避けるためにどのようなアクションを取るべきかについて詳しく議論しました。

何が起こったか

2022年11月4日、私たちはReact Nativeのバージョン0.71.0-rc0、すなわち0.71の最初のリリース候補を、いくつかの公開リポジトリに公開しました。

このリリース候補で行われた大きな変更は、ソースからビルドする代わりにMaven Centralにアーティファクトを公開することで、ビルド時間を改善するのに役立ちました。これについての詳細は、RFC#508関連する議論でご覧いただけます。

残念ながら、テンプレートから新しいプロジェクトをスキャフォールディングする方法が原因で、古いバージョンのAndroidユーザーは、プロジェクトで使用しているバージョン(例:0.68.0)の代わりに0.71.0-rc0の新しいアーティファクトをダウンロードし始めてしまい、ビルドエラーが発生しました。

なぜこれが起こったのか

React Nativeのテンプレートは、Androidアプリをビルドするためのbuild.gradleファイルを提供します。このファイルには、React Native Androidライブラリへの依存関係が次のように記述されています:implementation("com.facebook.react:react-native:+")

重要なのは、この依存関係の+の部分(Gradleの動的バージョン)が、Gradleに利用可能な最新バージョンのReact Nativeを選択するよう指示することです。Gradleの動的バージョンを使用することは、ビルドの再現性が低くなるため、アンチパターンと見なされています。

私たちは動的バージョンが引き起こしうる問題を認識していたため、0.71では新しいアプリのテンプレートをクリーンアップし、すべての+依存関係を削除しました。しかし、古いバージョンのReact Nativeを使用しているユーザーは、依然として+バージョンを使用していました。

これにより、0.71.0-rc.0より前のReact Nativeバージョンでのビルドが、すべてのリポジトリに対して利用可能な最新バージョンのReact Nativeを問い合わせるようになりました。Maven Centralに新しくプッシュされた0.71.0-rc.0が利用可能な最新バージョンとなったため、0.71.0-rc.0より前のReact Nativeバージョンでのビルドは0.71.0-rc.0のアーティファクトを使用し始めました。ローカルビルド(例:0.68.0)とMaven Centralのアーティファクト(0.71.0-rc.0)との間でReact Nativeのバージョンが不一致となり、これらのビルドは失敗しました。

このイベントに関するさらなる技術的な詳細は、このGitHub issueでもご覧いただけます。

どのように緩和し、解決したか

11月4日に問題を特定するとすぐに、コミュニティは問題を修正するための手動の回避策を見つけ、共有しました。これは、React Nativeを特定のバージョンに固定することで間違いを修正するものでした。

その後、11月5日と6日の週末にかけて、リリースチームは0.63までのすべての以前のReact Nativeバージョンに対してパッチリリースを行い、パッチを自動的に適用することで、ユーザーが修正済みのReact Nativeバージョンにアップデートできるようにしました。

同時に、私たちはSonatypeに連絡を取り、問題のあるアーティファクトの削除を依頼しました。

11月8日、アーティファクトがMaven Centralから完全に削除され、問題は完全に解決しました。

イベントのタイムライン

このセクションには、イベントの簡単なタイムラインが含まれています。すべての時刻はGMT/UTC +0です。

学んだ教訓

多くの点で、このインシデントを引き起こす条件はReact Native 0.12.0から存在していましたが、私たちはReact Nativeを開発しリリースしていく基盤をより強固なものにしたいと考えています。以下に、学んだ教訓と、将来より迅速かつ強力に対応するためにプロセスとインフラストラクチャをどのように適応させていくかについてのアクションアイテムをいくつか挙げます。

インシデント対応戦略

このインシデントは、React Nativeに関連するオープンソースの問題に対する私たちのインシデント対応戦略のギャップを浮き彫りにしました。

コミュニティは2時間以内に迅速に回避策を見つけました。しかし、この問題の影響範囲に関する可視性の欠如と、古いバージョンで修正するために必要な複雑さのため、私たちは影響を受けた人々がGitHub issueで回避策を発見することに頼っていました。

この問題のより広い範囲を認識し、誰もがGitHub issueを見つけることに頼れないと理解するまでに48時間かかりました。私たちは、人々のプロジェクトを自動的に修正するために、より複雑な積極的な緩和策を優先する必要がありました。

私たちは、開発者が適用する回避策に頼るべき時と、自動的にデプロイできる修正に頼るべき時について、プロセスを見直す予定です。また、エコシステムの健全性について、より良いライブパルスを得るための選択肢も検討します。

リリースサポートポリシー

rn-versionsツールで可視化されているように、インシデント発生時にReact Nativeの開発者ベースの90%以上をカバーするためには、バージョン0.63までパッチをリリースする必要がありました。

これは、歴史的に多くの摩擦があったReact Nativeのアップグレード体験が原因であると考えています。エコシステムのこの断片化を緩和するため、現在、アップグレード体験をよりスムーズで迅速にする方法を検討しています。

新しいバージョンのReact Nativeをリリースすることが、古いバージョンのユーザーに影響を与えることは決してあってはならず、皆さんのワークフローに混乱を招いたことをお詫び申し上げます。

同様に、私たちが導入した改善やセーフガードの恩恵を受けるために、依存関係やReact Nativeを最新のバージョンに保つことの重要性も強調したいと思います。このインシデントは、公式のリリースサポートポリシーが定義されつつあり、まだ公表も強制もされていない時期に発生しました。

将来的には、コミュニケーションチャネルを通じてサポートポリシーを伝え、npm上で古いバージョンのReact Nativeを非推奨にすることを検討します。

サードパーティライブラリのためのテスト改善とベストプラクティス

このインシデントは、より良いリリーステストとサードパーティライブラリへのより良いガイダンスの重要性を浮き彫りにしました。

テストの面では、0.63.xまでのバージョンをリリースすることは、現在安定版リリースで導入している自動化やテストが不足していたため、困難であることが判明しました。私たちはリリースとテストインフラの重要性を認識しており、将来的にはさらに投資していくつもりです。

具体的には、現在、react nativeのリリースの一環としてサードパーティライブラリのテストを奨励し、サポートしています。また、Core Contributors Discord Serverに新しいチャンネルと役割を追加しています。

これに加えて、create-react-native-libraryのメンテナーであるCallstackとの協力をより密にし、ライブラリテンプレートを改善し、React Nativeプロジェクトと統合するために必要なすべてのベストプラクティスに従うようにしました。create-react-native-libraryの新しいバージョンは、後方互換性を提供しつつ、0.71プロジェクトと完全に互換性があります。

結論

このインシデントが世界中の開発者のワークフローに引き起こした混乱についてお詫び申し上げます。上記で強調したように、私たちはすでに基盤を強化するための行動を開始しており、さらなる作業が必要です。

これらの洞察を共有することが、皆様がこのインシデントをよりよく理解する助けとなり、私たちの学びを活かしてご自身のツールやプロジェクトでより良い実践を適用できることを願っています。

最後に、アーティファクトの削除にご協力いただいたSonatype、私たちのコミュニティ、そしてこの問題にできるだけ早く対処するために精力的に働いてくれたリリースチームに、改めて感謝の意を表します。