モバイルアプリの One Experience

こんにちは。レシピ事業部でAndroidアプリ開発をしているこやまカニ大好きです。
好きなイジンカードは行基近松門左衛門、最近気になるカードは松尾芭蕉です。

このブログの日本とグローバルのクックパッドを統合しましたという記事で、日本とグローバルのクックパッドサービスの統合が行われたこと、プロジェクトの名称が One Experience であったことについて説明がありました。
もちろん One Experience プロジェクトについては Web だけでなくモバイルアプリについても行われており、現在日本向けにリリースされているクックパッドアプリは、クックパッドがサービスを展開しているすべての地域での表示に対応した One Experience版のアプリになっています。
この記事では、モバイルアプリの One Experience について、どのような作業が行われたのか大まかに概要を説明したいと思います。

概要

前提として、クックパッドでは以前から JPアプリと Globalアプリという2つのアプリがリリースされていました。
この2つのアプリは完全に別のアプリとして開発されていて、コードの共有率はほぼ 0% 、認証やAPIなどバックエンドの構成もまったく異なるものでした。
今回行ったモバイルアプリの One Experience とは、一言でいうと Globalアプリのコードで JPアプリを上書きしてアップデートする作業になります。
GlobalアプリとJPアプリはどちらも既存ユーザーがたくさんいるため、現段階では2つのアプリを1つに統合することは行わずに、1つのコード、リポジトリから2つのアプリをリリースしていくことにしています。

ひとつのリポジトリから複数のアプリを配信するというのは Kindle ストア版アプリの配信やAndroidTVアプリの配信などでよくある構成ですが、それによって別のアプリを完全に上書きするというのはかなり珍しいと思います。
こやまカニ大好きのAndroidアプリ開発歴はそこそこ長いのですが、この作業を行ったのは初めてでした。

この記事では、アプリを別のコードベースで上書きする際にどういった考慮が必要だったかを大まかに説明していこうと思います。
One Experience によるモバイルアプリの機能面の変更や細かい技術上の工夫などは後続の別記事で説明されていく予定なので、この記事ではJPアプリの上書きに必要だった作業の概要について説明していきます。
また、この記事では主に Android の用語で説明していきますが、 iOS アプリについてもだいたい同じような雰囲気だと思ってください。

モバイルアプリ固有の特性

詳しい作業内容に入る前に、モバイルアプリのリリースに関する特性について説明したいと思います。
すでにモバイルアプリの開発者はよく知っていることですが、この特性が One Experience のリリースを複雑なものにしているため、あらためて説明します。

ロールバックが難しい(実質できない)

モバイルアプリは、常に以前のものよりも大きい version code を持つアプリでしか上書きできません。
この特性により、一度 One Experience版で上書きされたアプリをJP版にロールバックするためには、JP版のバージョンを One Experience版よりも大きい値に変更した上で再度上書きする必要があります。
この方法でロールバックを行うと2つのリポジトリ間でバージョンを細かく管理する必要があるため、リリースフローがとても複雑になります。

さらに、JP版へのロールバックを行った場合、JP版 -> One Experience版 とアップデートしたユーザーだけでなく、 One Experience版を新規にインストールしたユーザーもJP版で上書きされてしまいます。
JP版 -> One Experience版 へのアップデート時に認証情報や一部のローカルデータをマイグレーションすることは決めていましたが、逆方向のアップデートやJP版 -> One Experience版 を2回繰り返した場合のサポートはあまりにも大変すぎるため、リリースに関する制約として One Experience版からのロールバックは行わないと決めました。
これにより考慮事項がかなり減り、 One Experience に集中して進めていく意思表示にもなったので、この意思決定ができたことは良かったと思います。

アプリがユーザーの手に届くまでに時間がかかる(bugfix や挙動変更が瞬時に適用できない)

モバイルアプリでは、アプリをサブミットしたあともリリースされるまでに審査があり、さらに公開したあともユーザーの端末にインストールされるまでは時間がかかります。
これは bugfix などのリリースでも同様で、不具合を修正してもユーザーの手元の不具合が発生していたバージョンを上書きするためには数日掛かる場合もあります。

特に One Experience リリースの初期段階では様々な不具合が予想されたため、プラットフォームの段階的な公開機能を利用し、様子を見ながら少しずつ公開率を上げていくことにしました。 One Experience では、初回のリリースから100% リリースまでおよそ3週間掛かっています。 ロールバックが難しい&更新に時間がかかるという状況で後述の認証情報のマイグレーションが失敗すると何もかもおしまいになってしまうので、初回のリリースからマイグレーションに成功したユーザーが観測されるまではかなりドキドキしていました。

JP版アプリをリリースするために行った作業

ここでは One Experience リリース時に実装が必要だった項目について簡単に列挙します。
ここにあげた項目以外もたくさんの修正が入っていますが、特に重要なものについて記述しています。

日本リージョンへの対応

Globalアプリはもともと多言語対応していたので、アプリ内に ja-JP リージョン設定を追加し、翻訳リソースを追加すれば日本語対応できる状態でした。
文字列の翻訳や画像以外でもヘルプページのURLやごく一部の実装はリージョンごとに処理を切り替えていて、 One Experience では ja-JP リージョン特有の処理もいくつか追加しています。
ja-JP リージョンだけの分岐が将来的になくなるのか、そういう仕様のままでずっといくのかはまだ決まっていない箇所もあり、今後はこういったリージョン固有の実装箇所の保守性も高めていけると良いなと考えています。

JPアプリビルド設定 の追加

Globalアプリ(Android)プロジェクトにはもともと Flavor によってビルドするアプリの ApplicationId や versionName などを切り替える機能が実装されていました。
JPアプリではこういった切り替えはすべてモジュールを切り替えることで行っていたので、 Flavor で切り替えることに少し抵抗があったのですが、元々の実装をベースにリファクタリングを加えることで Flavor ベースでの切り替えによって実装することができました。

バージョニング

JP版アプリは数年前からリリース年・リリース週番号ベースで自動的にバージョンを採番していました。
対してGlobal 版は手動のセマンティックバージョニングで、 major、minor を繰り上げるタイミングについてはあまり明確になっていませんでした。
JP アプリのほうが version code が大きかったこと、週次リリースというフローはOne Experience後も変わらなかったことから、JP/Global両方のアプリでリリース年・リリース週番号ベースのバージョニングに合わせることにしました。

リリースフロー

もともとJP版アプリはかなり自動化された週次のリリースフローを採用していました。
Globalアプリも週次リリースでしたが、運用方法には大きな違いがありました。
一番大きな違いはコードフリーズやサブミットのタイミングが自動化されておらず、リリースマネージャーとなった人間が手動でタイミングを決めていたことです。
Global 版アプリでは機能や画面の更新に対して翻訳リソースの更新を待つ必要があり、持ち回りでリリースマネージャーになった人間の活動時間にも時差があったため、リリースマネージャーがコードフリーズのタイミングを調整できたほうが都合が良かったのです。
最初はJPのように自動化したほうが良いと考えていましたが、 Global 版をベースに開発していくうちに考えを改め、 Global版のリリースフローに合わせることにしました。
現在のリリースフローでは、コードフリーズ、サブミット、リリースなどの処理が Global と JP でほぼ完全に同期して行われています。

Firebase プロジェクトの切り替え

JP と Global はそれぞれ別々に開発・運用されているアプリだったため、 Firebase プロジェクトも完全に分離されている状態でした。
最初はビルドするアプリによって Firebase プロジェクトを切り替える方針も検討したのですが、以下の理由により、 Firebase プロジェクトを Global が使っているものに統一することにしました

  • 社内の push 通知送信サービスがマルチproject をサポートできるように改修が必要
  • コードベースが完全に切り替わるため、Crashlytics に送られるクラッシュ情報の傾向が大きく変わる
    • One Experience 以降のクラッシュ情報だけ管理できれば良い
  • アプリ内の Firebase Analytics ログが完全に切り替わるため、 Firebase Analytics のログ内容が大きく変わる
    • One Experience前後の比較がしたいので連続性はあったほうが良いが、もともと重要なログは FirebaseAnalytics ではなく自前のログで比較する文化だったので、致命的ではない
  • Firebase Dynamic Links を複数のプロジェクトから生成したくない

アプリのマイグレーションについて

JP版アプリから One Experience版アプリにアップデートしたとき、きちんとマイグレーション処理を実装していなければアプリからログアウトし、すべてのローカルデータにアクセスできなくなります。
One Experience版アプリでは、JP版アプリを利用していたユーザーがそのまま利用できるように、認証情報や一部のローカル保存情報にアクセスできるように特別な実装を入れています。

特に認証情報のマイグレーションに関してはこれだけのために Globalアプリに AccountManager の実装を入れていたり色々な仕組みが入っているのですが、説明するとこれだけで一つの記事になってしまうのでまた別の機会に書くことにします。

まとめ

One Experience版アプリをリリースするための取り組みについて説明しました。
機能面、コード面ではこの記事で紹介した以外でも様々な変更がありますが、ここではプロジェクト全体に関わるような大まかなものに絞って紹介させていただきました。
これからもモバイルアプリの One Experience に関する記事は公開予定なので、今後の更新にもご期待ください。