レシピ事業部のHaurta (@0x746572616e79 )です。グローバルサービスとの統合プロジェクト(One Experienceプロジェクト)に伴いiOSアプリケーションもグローバルと日本で別々のアプリケーションを開発していた体制から一変して、グローバルのアプリケーションベースの開発(グローバル版)へ移行を進めました。
グローバルと日本で異なるアプリケーションを開発してきたため、同じクックパッドでも細かな挙動の違いが見られます。気になる挙動がないかどうかチームで何度もウォークスルーを重ねた結果、レシピエディターやプロフィール設定画面で使われるフォトピッカーの挙動が問題として浮上しました。
フォトピッカーの改善を重要なタスクとして取り組むことにしましたが、フォトピッカーに限らずOne Experienceプロジェクトが始まってからはグローバル版のコードベースを読むところからのスタートになるため、このタスクの完了にどのくらい時間がかかるのか推測しづらい状態でした。
一般的に大きなタスクを小さく分割することは行われますが、具体的な分割方法や進行の手法については、慣れや経験だったり試行錯誤が必要です。
この記事では、フォトピッカーの改善を通じて、タスク分割とプロジェクトの進め方について幾つかの学びがあったため、どのようにタスクを分解し進めていったのかを紹介します。
フォトピッカー
クックパッドにおいてフォトピッカーは非常に重要な機能の一つで、旧日本版、グローバル版ともにフォトピッカーは標準のPHPickerViewControllerを使用せず、何種類か目的と体験にあった自前のフォトピッカーを実装していました。
旧日本版では、ユーザーがレシピに写真を追加する際、フォトピッカーが最初に起動し、そこからカメラスクリーンに遷移できるボタンが配置されていました。これに対し、グローバル版では、カメラスクリーンが最初に起動し、カメラスクリーンにフォトピッカーへの遷移ボタンが設置されていました。
グローバル版のカメラ機能はシンプルなため、多くのユーザーが外部のカメラアプリを利用するだろうと考えられます。この状況では、画像をアップロードするたびにカメラスクリーンが起動するのは不便です。また、X(旧Twitter)やInstagramといったSNSアプリでは、フォトピッカーを先に表示し、内部にカメラアクセスの導線を設けるのが一般的です。そこで、グローバル版もフォトピッカーを最初に起動する流れに改善することになりました。
問題解決への道筋を立てる
グローバル版は各機能がどのように実装されているか、画面の構造、実装を理解するところから改善を進めます。当初は画面の入れ替えだけで済むと考えていましたが、コードリーディングを進めるうちにかなりの工数が必要であることが判明しました。
グローバル版はCoordinator Patternを用いた画面遷移を採用しており、フォトピッカーにはカメラへの遷移を設置する必要がありました。この時、概算で10時間以上の工数がかかる可能性を感じつつも、どこから着手すれば良いのか具体的な見積もりができていません。
レシピ事業部はスクラム開発を採用しており、タスクの優先順位や範囲を決定するためにも、工数の見積もりが必要です。非常に正確である必要はありませんが、大雑把過ぎる見積もりでは不透明なため優先順位判断が難しくなります。
改善タスクには破壊的な変更が含まれており、複数スプリントにわたって開発が必要な場合モバイル特有の問題を考える必要があります。モバイルアプリはスプリントごとにリリース*1をしているため、未完成の機能が露出しないようにしなければなりません。そのため、mainブランチへ細かく変更を加えていくのか、開発ブランチを事前に用意しまとめてmainブランチに取り込む方法を採用すべきかどうかを検討しました。
mainブランチに直接細かな変更を積み重ねる方法では、コミットの粒度をコントロールしやすく、各変更の影響範囲を小さく保つことができます。また、最新のmainに追加していくため、安定性を確保しやすいです。しかし、開発中の機能や未完成の変更がリリースで露出してしまわないよう厳重な管理が求められます。
一方、開発ブランチを用いた方法では、mainブランチに影響を与えることなく機能を追加、変更することができます。ただ、開発ブランチでの作業が進む中でmainブランチにも並行して別の変更が加えられることがほとんどで、コンフリクトのリスクが高くなります。また、mainマージをするタイミングで全ての差分をチェックする必要があり、レビューコストの増加につながります。アルファリリースでは、開発ブランチを用いて変更を積み重ねていましたが、mainブランチと大きな差分が発生してしまい、コミットの統合時に多大な工数がかかることがありました。
最終的にはmainブランチへ変更を積み上げていく方法で進めることを決めました。
リリースを跨ぐ機能開発
mainブランチに変更を積み上げていくと決めましたが、ではどう破壊的な変更を閉じておくべきでしょうか?
Feature Toggle
グローバル版にはFeature Toggle *2 が用意されています。A/Bテストなどで広く使われており、代表的なサービスとしてFirebase Remote Config *3 が有名です。詳しくは取り上げませんが、One Experience向けの機能をグローバル版へ実装、展開する際にもFeature Toggleが利用されていました。遷移先の切り替えや、UITableViewで表示するコンテンツの出し分けなど色々なユースケースでの利用ができます。
class Coordinator: InteractorDelegate { ... func interactorWantsToInsertStepAttachments() { if appContext.featureToggle.supports(.フォトピッカーを先に起動する) { startPhotoPickerFlow() } else { startCameraFlow() } } }
今回のフォトピッカー改善タスクではCoordinatorの画面遷移処理にてFeature Toggleを利用し出し分けを行う方法を採用しました。別の方法としてFeature Toggleがオンのときにはフォトピッカーを、オフのときにはカメラスクリーンを初期表示する単一のCoordinatorを定義する方法も検討しましたが、実装の簡素化と管理のしやすさを考えると独立したCoordinatorを用意する方が適していると今回は判断しました。
開発中は端末内のFeatureToggleを強制的に有効化しフォトピッカーを立ち上げ、本番環境では以前のカメラを立ち上げることで意図せず開発中の実装がユーザーの方に見えてしまう事故を防ぐことができます。
タスクと仕様を整理する
Feature Toggleによって全ての問題が解決するかというと、そうではありません。Feature Toggleで分岐先を変えるのが良さそうだというのはわかりましたが、依然としてどれくらいの工数がかかるかわからないためタスクと仕様を整理する必要があります。遷移先の画面をFeature Toggleで分岐できるようになったので全く新しい画面を定義して表示することもできますが、また0からフォトピッカーやカメラスクリーンを実装するのは二度手間でその分工数がかかってしまいます。手間を省くためにも可能な限り、既存のコンポーネントや実装は使い回しをしたいです。
ただ、既存のフォトピッカーは使い回すには少々難しい実装になっていました。たとえばデバイス内の写真にアクセスするための権限をリクエストするPHPhotoLibrary.requestAuthorization
はカメラスクリーンを表示しているタイミングで実行していたり、権限がない時は専用のエラーコンポーネントをフォトピッカーで表示するのではなく、UIAlertControllerを利用したエラー表示を行っていました。このまま画面を入れ替えてしまうと、フォトピッカーのアクセスを拒否したときカメラスクリーンへの導線がなくなってしまい体験が悪いです。エラー画面の追加、アクセス権限をリクエストするタイミング調整など、新しいCoordinatorを定義する前にフォトピッカー自体の機能追加が必要だということがわかりました。
少しずつ取り組むべきタスクが明確になり、優先順位もまた見えてきました。新しいCoordinatorを追加する前にフォトピッカーの機能追加を進める必要がありますが、フォトピッカーにカメラセルを追加する、エラー画面を追加するといった変更はそれぞれ独立したタスクとして進めることができます。また、新しいCoordinatorを追加するのもFeatureToggleを利用することでユーザーの目に触れることなく開発を進められることがわかりました。
ここまで調査時間含めて3h程度で整理、設計をし、やっとある程度正確に工数を見積もることができました。これで他Epicイシューとの優先順位を決めタスクとして進めることができるようになります。
まとめ
フォトピッカー改善タスクを例にタスクの分解とリリースを跨いだ機能開発の事例についてご紹介しました。特に、大きなタスクを細かく分解するためにモバイル特有のリリース制約を考慮し、対応が必要な細かいタスクをリストアップする進め方は他のタスクにも応用できると思います。
また、今回は遷移先のコントロールにFeature Toggleを活用し、比較的綺麗な設計を実現しましたが、Feature Toggleの運用には注意点も存在します。実際に、Feature Toggleを使ったアプローチがうまくいかなかったケースもいくつか経験しています。機会があれば、Feature Toggle単体のお話もできればと思います。
この記事で紹介した方法や考え方が、皆さんの今後のアプリケーション開発において何かしらの参考になれば幸いです。
*1:レシピ事業部では1スプリントを一週間で回しているため週次リリースを行っています
*2:一般的にはFeature Flagとも呼ばれている(https://martinfowler.com/articles/feature-toggles.html