はじめに

株式会社 WinTicket の仙石(@akihisasen) です。

2022 年 4 月 5 日(火)に Flutter 製の WINTICKET の Android 版新アプリ(バージョン 3.1.0)をリリースいたしました。


Androidの新アプリをリリース Google Playで手に入れよう

【お知らせ】Android 版新アプリをリリースいたしました

現在は Android アプリの機能改善を進めながら、既存 iOS アプリのリプレース開発を進めています。

目次

今回の記事では、Flutter での Android アプリのリプレースの全体像に触れながら、どのような戦略でリプレースを進めたかについてのお話をします。

なぜ WINTICKET の iOS / Android アプリを Flutter でリプレースを行うかの意思決定については、和田(@wadackel)の記事を見ていただきたいです。

不確実性に対応する開発戦略

バックグラウンドの異なるエンジニア開発組織

2021 年 3 月に Flutter リプレースプロジェクトチームを発足しました。プロジェクトの発足当初はエンジニア 4 名でしたが、現在はエンジニア 8-9 名、PM 2 名、デザイナ 1 名で開発を進めています。各エンジニアメンバーがチームにジョインする前のバックグラウンドは、iOS、Android、Web とばらばらで、Flutter を用いたアプリの開発経験があるメンバーはいませんでした。
Flutter のリプレースプロジェクトを進める上で、バックグラウンドの異なるエンジニアが集まったのは結果的にメリットとなりました。

Flutter での開発において MethodChannel を用いたネイティブコードの呼び出しの実装、iOS / Android のプロジェクト構成ファイルの記述、各プラットフォームに最適化したコンポーネント開発などが発生します。
これらの実装を進める上で iOS / Android アプリ開発の知見が必要になります。
初期段階で iOS / Android のエンジニアがいたことで、円滑にプロダクトの基盤を作ることができました。

また、Web のエンジニアがいたことで、React での大規模な Web アプリの開発の知見を取りいれることもできました。特に Flutter Hooks の導入時には、React Hooks を参考に Flutter Hooks の実装を行えたことで、コードの複雑性を抑えることができ、チーム全体の Hooks に関しての理解を底上げできました。さらに、宣言的 UI の実装方針やアプリ全体の設計に関しても参考にできた部分が多くあります。

バックグラウンドの異なるエンジニアが集まったことは前述したメリットを生み、結果的にプロダクト全体の品質向上に繋がりました。

柔軟な開発フロー

開発手法としてスクラムを採用しました。スクラムを採用した理由としては、初期段階でのプロジェクト全貌に不確実性が高かったためです。開発メンバーの人数、開発要件、Flutter での各機能の実現可能性など曖昧な部分が多く、プロジェクト全体の詳細な計画をするのは困難でした。

スクラムは不確実性が高い状態において有用です。プロジェクトを進めながら知見を貯め、貯めた知見を用いて、より精度の高い解決策を見つけていくことができます。下記がスクラムガイドに記載されている「スクラムの理論」になります。

スクラムは「経験主義」と「リーン思考」に基づいている。経験主義では、知識は経験から⽣まれ、意思決定は観察に基づく。リーン思考では、ムダを省き、本質に集中する。スクラムでは、予測可能性を最適化してリスクを制御するために、イテレーティブ(反復的)でインクリメンタル(漸進的)なアプローチを採⽤している。

2020-Scrum-Guide-Japanese

今回のリプレースプロジェクトにおいては「終了見込み」と「開発体制」の最適化を特に行う必要がありました。
実際にプロジェクトを進行しながら、スクラムを用いてどのように最適化を行ったかついてお話しします。

終了見込み

終了見込みは、事業にとって今後の事業計画を決める上で重要な指標となり、適切な見積もりを出すことが求めらます。
しかし、前回の記事で述べられているように WINTICKET の既存のアプリの機能は膨大で、すべての開発要件の工数を正確に見積ることは困難でした。また、人員変動や開発機能の追加が予定されており、適切な終了見込みを出すのはほぼ不可能でした。

スクラムガイドを参考にしながら、周期を 1 週間としたスプリントごとに下記の数値を出すことで、終了見込みの算出を毎週行いました。

  • バックログの想定工数
  • スプリントでのバックログの消化工数
必要スプリント数 = プロダクトバックログの想定工数の総和 * 見積もり誤差平均 / 1スプリントの消化工数平均

見積もり誤差はバックログの想定工数と実際の消化工数から算出。

これらの数字を元にバーンダウンチャートを作り、プロジェクトの進捗の可視化とチーム内での認識合わせを行いました。スプリントごとのチームパフォーマンスもわかるため、開発体制の改善にも繋がります。さらに、バックログの追加や削除による終了見込みの再算出も容易に行うことができます。

バーンダウンチャート
バーンダウンチャート

柔軟で精度が高い終了見込みをスプリントごとに出すことができることで、事業のメンバーとリリース日やバックログの中身に関するコミュニケーションを適切に取りながらリプレースプロジェクトを進めることができました。

開発体制

開発体制は、スクラムイベントのスプリントレトロスペクティブ(振り返り)で KPT を行い、最適化を行ってきました。KPT は毎週できるように、1 回 15 分程度で開催しています。KPT を通して、直近の開発でうまく行っていることや課題を共有し、その内容を元に開発体制の改善します。プロジェクトで改善した内容は下記になり、開発に関わるさまざまな要素を毎スプリントごとに改善しています。

  • 不要な MTG の削除や MTG の追加
  • リモート化でのコミュニケーション方法の変更
  • レビュー体制の変更
  • 開発ドキュメントの整備の強化
  • パッケージのアップデート方法の変更
  • etc

KPT が毎週行われることで、変更した内容がチームに合っていなければ次の週の KPT で戻せば良いので、ハードルを低く変更や挑戦できます。今回のリプレースプロジェクトにおいて、新しいメンバーの参加やコロナ禍での働き方の変化がある中で、開発体制を柔軟に最適化していったのは、開発パフォーマンスの向上につながりました。

現在も既存の iOS アプリのリプレースに向けて、柔軟に開発体制を変化させて、現状の環境や目標に対して最適な開発体制を模索しています。

技術的な挑戦の促進

今回のリプレースにあたり事前に技術検証を行なっていましたが、パフォーマンスや複雑な UI の実装において不確実なところがある状態での開始となりました。特にパフォーマンスに関しては、Flutter で既存の複雑な画面を実装するまで既存の iOS アプリとのパフォーマンス比較を進めることが難しく、開発初期にパフォーマンスの懸念を潰しきるのは困難でした。
Flutter の技術的な課題に今後対応していくために、開発チームの Flutter のスキルレベルを向上させていく必要がありました。また、既存のアプリのリプレースであり Swift で実装された既存 iOS アプリと同等以上の品質が求められ、Flutter のパフォーマンスを最大限引き出すことができるレベルでの理解が求められるのは自明でした。

継続的にチームの技術を向上させていく枠組みとして、リプレース中から下記を取り組んでいます。

① チーム内勉強会の開催

  • 週 1 回 1 時間の PR の振り返りと知見の共有会
  • 不定期で輪読会などの勉強会の開催

勉強会では Flutter's Rendering Pipeline の輪読会を行い、Flutter のレンダリングの仕組みの理解をチームで行いました。Flutter の低レイヤーの理解することで、パフォーマンスの向上の糸口になっています。

② 外部への技術発信の推奨

CyberAgent 主催の Flutter × Kotlin Multiplatform や、Flutter の国内カンファレンス FlutterKaigi において、メンバーが数多く登壇し、知見を外部に発信しています。

外部に発信する過程で、チームの知見を整理しブラッシュアップする良い機会となっています。

③ 開発ガイドラインの整備

技術的な挑戦の文脈から少し逸れますが、チーム内の技術レベルのボトムアップの文脈で開発ガイドラインの整備にも力を入れています。
Riverpod の使い方から Bool の変数の命名まで幅広く 26 項目が開発ガイドラインに記載しております。

チーム内のオンボーディングコストの削減やコードの統一性の向上に繋がっています。
現在も PR の振り返りなどでメンバー間で認識が違っている箇所に関して、開発ガイドラインに追記する流れで整備を進めています。メンバーがジョインするときのコードのキャッチアップや Flutter の理解に役立っています。

今後もチームの技術力向上に積極的に取り組み、技術で事業成長に貢献していきます。

Flutter アプリのデザイン戦略

Flutter は、Material Design に準拠した Widget を推奨しています。そのため、Flutter で作られている iOS アプリを触ると Android っぽさがあり、iOS のアプリに慣れている人にとっては違和感を感じることがあります。iOS のデザインを実装するために、Flutter には Cupertiono のパッケージが用意されており、iOS のデザインを簡単に実装できます。

ここで問題になってくるのが Flutter で開発しているアプリのプラットフォームごとの UI デザインをどうするかについてです。大きく 3 つの方針が考えられます。

  1. iOS と Android 両方 Material Design にする
  2. iOS と Android 両方 iOS のデザインにする
  3. iOS は iOS のデザイン、Android は Material Design にする

1,2 の方針を選択した場合、どちらか片方のプラットフォームの体験を損ねることになり、また、3 の方針を選択した場合には、Flutter の旨味(単一コードでのマルチプラットフォームの開発)を損なうことになります。Flutter でのプロダクト開発をする上でプラットフォームごとのデザインをどうするかの方針を決めるのは難しいです。

WINTICKET のデザインコンポーネント

WINTICKET の Flutter のアプリでは、WINTICKET の既存のデザインコンポーネントを両方のプラットフォームで使用しながら、プラットフォームの体験として違和感がある箇所に関してはプラットフォーム固有のコンポーネントを使用しました。

WINTICKET のデザインコンポーネントでは、下記のコンポーネントが定義されています。materialcupertino のパッケージを用いて実装し、アプリ内で WINTCIKET のデザインコンポーネントを使い画面を実装しています。

WINTICKET UI Catalog コンポーネント一覧

- List
- SnackBar
- Pulldown
- Form
- NavigationBar
- Dialog

WINTICKET UI Catalog

すべての UI の実装を WINTICKET のデザインコンポーネントで対応するには限界があります。例えば、ページトランジションのアニメーションやボトムシートなどは、デザインコンポーネントで定義されていないのでコンポーネントとして定義するのか、OS 固有のデザインコンポーネントを使用するのかについてデザイナーと議論する必要があります。議論の論点としては、体験の側面から各プラットフォームの体験やアプリの全体の体験設計と適切であるかです。あとは、エンジニアの視点から Flutter の旨味である開発パフォーマンスを落とさないかについてです。

実際にプラットフォームごとでデザインや挙動の出し分けを行なっている箇所は少なく下記になります。

  • コンポーネント
    • Switch
    • Dialog
    • RefreshIndicator
  • タップのフィードバックアニメーション
  • 触覚フィードバック
  • ScrollBehavior

開発当初はコンポーネントや挙動についての議論が多く、想定通りに機能実装が進みませんでした。ですが、開発初期にデザインの方針を決めたことで、後々のデザインに関するコミュニケーションを減らすことができ、開発をスムーズに進行できました。

リプレースとデザインのアップデート

リプレースに伴い古いデザインコンポーネントとデザインデータを更新しました。WINTICKET の既存のアプリはリプレース開始時に 2 年が経っており、新しいコンポーネントに対応できていないページが一部存在していました。また、デザインデータも既存アプリと一部乖離が発生している箇所がありました。
デザインコンポーネントの更新によりアプリの体験の統一性を高め、デザインデータの更新により今後の開発パフォーマンスを向上させました。

品質担保のためのリリース戦略

新規でのリリースでなく、既に多くのにユーザーに使っていただいているサービスのため、品質の担保は最優先項目でした。また、リプレース伴いプロダクトの負債を解消する機会にもなり、内部品質の向上を開発当初から理想状態と定義していました。

Test Code

Flutter プロダクトでは ロジック部分を Unit Test、UI コンポーネント を Visual Regression Test により品質を担保していました。
Visual Regression Test は、PR ごとにアプリ内で使用されている UI コンポーネントを状態ごとに表示したカタログアプリのスクリーンショットの差分を比較することで、意図しない UI の変更を検知できます。Visual Regression Test により担保が難しい UI コンポーネントの品質を担保することできます。
また、一部インテグレーションテストの導入を検討しましたが記述コストが高くなるため、自動 E2E Test でのアプリ全体の通しテストを行い UI とロジックの結合箇所の品質を担保しようとしました。しかし、E2E Test のメンテコストの高さと安定性の低さから、現在は 自動 E2E テストが品質の担保に繋がっていない状態となっています。今後、ツールの再検討や E2E の動作環境の見直し、まずは安定的に E2E テストが稼働できるようにしていきます。

現在の既存の iOS アプリとの比較が下記になります。

Unit Test Visual Regression Test Integration Test E2E Test
既存 iOS
Flutter

手動品質担保

コードでの自動テストだけでなく、手動での品質担保に関しても初期の段階から継続的に進めていました。

機能テスト

機能が完成した段階で、機能単体でのテストを実施し機能ごとに品質を担保していきました。
完成機能の担保を早い段階から進めたことで、リリース前に大きな出戻りやリリースの遅延することなく進めることができました。
最後にリリース前の 1 ヶ月間の期間で、機能すべての網羅テストを異なる端末で再度行い、開発途中でのデグレのチェックを行いました。

フリーテスト

月に 1,2 回の割合でフリーテストをチームメンバーや事業のメンバーを巻き込んで行い、機能単体のテストで漏れてしまった箇所や開発途中にデグレしてしまった箇所を定常的に発見していました。
また、最新のバイナリーを常に配布し、事業のメンバーが開発中のアプリをいつでも触れる状態を作っていました。早い段階でのデグレや体験が低下している箇所を発見できる体制を作っていました。

カナリアリリースとモニタリング

前述の自動テストと手動での品質担保を行なってきましたが、100%の品質の保証するのは難しかったです。
リリースしてみないとわからない部分もあり、Android のリリースではカナリアリリースを採用しました。
カナリアリリースに加え、カナリアリリース期間中にユーザーの声を素早く拾えるように、アプリ内の問い合わせ導線の強化やアプリに関するアンケートを実施しました。
実際にカナリアリリース期間中に多くのユーザーの声をいただくことができ、想定していなかったバグや体験の低下箇所を発見できました。
また、ユーザーからの報告だけでなく、エラーのモニタリングツールの導入とエラーの Alert を設定し、異常が発生した場合にすぐに開発者が検知できる仕組みを導入しました。
さらに、リリース当初には、ユーザーの行動ログでのアプリ内のユーザー行動のモニタリングも行い、体験や機能に問題がないかを確認しました。

エラーモニタリングツール Sentry
エラーモニタリングツール Sentry

現在のアラートルール

- 共有
  - 新規のエラーが発生した場合
- 警告
  - 1 時間で全体の 15 %のユーザーにエラーが発生した場合
  - 1 時間で 300 件のエラーが発生した場合

WebView の使用に関する注意点

最後に安定的なリリースのための戦略とは異なりますが、WebView を 使用した機能提供についてお話をさせていただきます。
ユーザー体験の担保ができていてアプリのコア体験ではない箇所において、開発コストの削減の目的として一部 WebView で機能を提供しました。
結果、リリース時に一部のユーザーにおいて WebView が原因で体験が低下しており、カナリアリリースの早い段階で修正対応を行うことになりました。ユーザーはアプリと アプリ内の WebView の区別を付けるのが難しく、WebView の体験はアプリの体験として捉えます。そのため、一部のユーザーにおいて WebView 内での長期滞在や WebView 内を回遊しアプリのコア体験を WebView で行うことで、開発者が提供したい理想の体験とは異なる体験になってしまっていました。

アプリの開発において WebView での開発コストを削減するケースが発生したときに、今一度デメリットとなる部分がないのかを慎重に確認してみてみるのが良いです。

まとめ

Flutter での既存アプリのリプレースに伴う、開発、デザイン、リリースの戦略についてお話しさせていただきました。今後 Flutter でのリプレースを行うと考えるている方々の参考に少しでもなれば幸いです。また、本記事で紹介しきれなかった技術的な部分に関しても、チームメンバーが発信していますので、ぜひお時間があるときにご覧ください。