PickGo パートナーをガワアプリからFlutterアプリに移行するまで
皆さんこんにちは、モバイルアプリエンジニアのKurogoma4Dです。
今回はCBcloudのエンジニア事例紹介として、メインの事業である配送プラットフォーム PickGo のうち、配送パートナー(弊社では荷物を配送するドライバーをパートナーさんと呼んでいます!)の方々に使っていただくアプリ「PickGo パートナー」の技術スタックを大きく移行した話をしたいと思います。
以前の技術スタック
PickGo パートナー(以下PGP)では、2015年のリリース以来これまでWebViewでのコンテンツ表示を主体とした所謂ガワアプリ、ガワネイティブアプリの形態を取ってきました。更に、そのWebView内で動くWebアプリにはAngularJS(現在のAngularではなく、バージョン1未満のAngularJS 😨)が使われていました。そして、その古いAngularJSをリプレイスするべく立ち上がったNuxtJSのリポジトリも存在し、一部の画面を実装した状態で繋ぎこまれていました…
ネイティブ部分に関しては、リリース当初こそAndroid/iOSネイティブの言語でそれぞれ書かれていましたが、一度ネイティブ部分をFlutterに置き換える巨大なリリースがされました。当時のリプレイスに関しては携わったメンバーのnoteがあるのでそちらも是非に。
移行のモチベーション
私がこのプロダクトに関わり始めたのは丁度最初のFlutter移行(社内ではFlutterizationという単語を作って呼んでいました。ちょっと響きがいい)のタイミングの辺りで、当時プロダクト理解度の低い状態では「まぁそういう構成もあるよね、一時期ガワアプリ流行ったし」くらいの感覚でした。しかし、仕様を理解していくにつれて、「PGPはこのままではダメだ、少なくともAngularJSの部分はなんとかしないといけない😠」と思い始めました。
技術的観点
まず、AngularJSのコードは初回リリース時から存在するものをこれまでメンテナンスしてきたため、中には3年ほど手のつけられていないコードや、どう操作したら表示できるのかわからない画面が存在しました。長く続くプロダクトではあるあるの光景かもしれませんが、「ある顧客のためだけに用意した画面があるけど実装者は退職済み、仕様のまとまったドキュメントもPull Requestのdescriptionもない」みたいなコードがそこそこあるような状態です。
そして、この構成自体がそもそもプロダクトの特性と合ってない、とも感じました。PGPは配送パートナー向けのアプリのため、案件の提案のために位置情報を取得したり、配達の証拠画像を提出するためにカメラを使用したりと、モバイル端末の機能をかなり多用します。その時、ネイティブコード→Dartコード(Flutter)→JSコード(Webアプリ)と3つのレイヤーを扱うと…単純に非効率かつバグの入り込みやすい環境ですよね?
FlutterまではまだFlutterの機能として情報の伝達ができますが、Webに対してはWebViewインスタンスに対してJSを実行させる…みたいなハック的手法を取らないといけないため、必要なコードも嵩むうえに動く保証もあまりできません。
というわけで、このFlutterizationでは今までメイン機能を提供していたAngularJSのコードを完全に廃止し、Flutter上でほとんどの操作が完結する状態を一つの大きな目標としました。
サービス的観点
このFlutterizationは、PGPというプロダクトをより前進させるためにも不可欠です。
従来のアーキテクチャでは、まずWebView主体であるために実装としてWeb技術の割合が多く、モバイルアプリエンジニアとしては非常にオンボーディングコストの高い状態でした。これまで大抵フロントエンド領域のメンバーに手助けしてもらいながらタスクを進めていましたが、一つの機能実装や不具合修正でも時間がかかりすぎてしまううえ、モバイルアプリエンジニアとしてプロダクトの実装に責任を持ちづらくなってしまいます。弊社の掲げる7つのValuesの一つに「Focus on Partners(本質的支援を)」というものがあるのですが、実装に時間のかかる上にネイティブ動作でないアプリではこのValueを体現することができません。
Flutterizationは、モバイルアプリエンジニアが正しく責任を持ってアプリを開発できる状態を目指すプロジェクトでもありました。
Flutterization後の技術スタック
Flutterizationを一通り終えてFlutterアプリと言える状態になった今では、AngularJSの画面の一切を取り除くことに成功しました🎉
PGPのメイン機能である配送案件の検索や案件へのエントリー、実際の運行までFlutterの画面上で完結するようになり、機能の実装や不具合の調査のたびにブラックボックスに手を突っ込むようなこともしなくて済むようになりました。一部NuxtJSを使った機能も残されていますが、そこだけで独立した機能のため後々ゆっくりとリプレイスを進めることができる状態になっています。
移行期間にやったこと
Flutterizationにあたって、まず大まかにフェーズを分け、ある一つの大きな導線ごとに仕様調査・実装・QA・リリースのサイクルを回す方針を取りました。
一度に全ての機能をFlutterizationするにはあまりにも規模が大きすぎるため、ネイティブに近い機能から順にFlutterizationしていって段々とWebViewを気にしなくていいように、言わば外堀を埋めていく戦略です。またリリースする範囲を絞ることで、重要な仕様が抜け落ちていないかをQAで十分担保するための方針でもありました。
フェーズに関して、PGPにはグローバルナビゲーション(画面下のタブ)中に5つの画面が存在します。リリースフェーズはある程度作業量を見積もった上で導線を区切り、大きく以下の2つに分けました。
グローバルナビゲーションから直接遷移できる画面 & ホーム画面、アカウント画面内の機能
主要機能の中でも比較的ボリュームが少なく、独立して実装できる箇所の集約
案件検索画面、決定案件画面から遷移できる案件の詳細画面
最も実装がブラックボックス化しており、調査から重点的にやる必要のあった最重要機能
PGPのリポジトリではgit-flowを簡略化したようなブランチ戦略を取っています。主にmaster、release、featureの3ブランチを使うようにしていますが、Flutterizationではリリースする単位ごとにfeatureを集約したtopicブランチを切って運用していました。リリース前はtopicにfeatureをマージしていき、QAが終了したらtopicをmasterにマージしてリリース、といった流れです。
Flutterization前後で主要機能に変化がないことを目指しつつも、従来の画面の中にはlook & feelが整っていない箇所が多々あったため、そういった箇所は積極的に整えていくようにも心がけました。
またFlutterizationの進行中であっても、もちろん新規施策のための機能実装を並行して行いました。技術リプレイスを行う現場ではよくあることではありますが、前述のブランチ戦略を崩さないように新規施策の開発は都度masterブランチからfeatureブランチを切ってリリース、その後topicブランチに合流させるようにしました。
更に今までブラックボックス化していた「どう表示するのかわからない画面」や、今まで秘伝のタレと化していたJavaScriptコードの仕様も明らかになれば一石二鳥なので、同時に社内ドキュメントへの仕様メモを残すようにしました。
仕様を書き残すこと・従来の仕様通りに実装することを実現するために、従来のソースコードをそのままDartコードやFlutterのWidgetに置き換えるといったことはなるべく避けて、今まで行っていたQAの資料なども使って入出力パターンなどを明らかにした上で実装を進めました。重要なロジック等はそのままDartコードに置き換えることもありましたが、その場合単体テストを書くことを意識しました。
そして、これまで古いバージョンのAngularJSをアップデートすることができなかった状況や雰囲気(売上向上に注力していた、QA体制が整っていなかった、等々…)を変えるべく、Flutterization進行中でもFlutterのメジャーアップデートになるべく追いつくように努力をしました ✌
実際のところ、Flutterのバージョンは1.22.6から始まり、Flutterizationを本格的に開始した時期には2.2.3、そして最後のフェーズリリース時には3.0.3と推移しています。
移行してみて
Flutterizationを達成して、リリース時はかなりドキドキしていましたが、実際にリリースした後は大きな反響につながるバグもありませんでした。「逆に怖い…!」と思いながらも、しばらくしてからプロダクト史上最も大きな変化を成し遂げたことの実感が湧いてきました。
実際のところ、このリプレイスは社内的にも大きいプロジェクトでした。上の章で示していたアーキテクチャ図の中で、ServerのところにしれっとPlayフレームワークとRailsフレームワーク2つが描かれていることにお気付きでしょうか 😇 この記事を書いている時点ではかなりRailsのAPIを使うようになっていますが、ごく一部のAPIはテストが困難なため、プロダクトの立ち上がりからAngularJSとともにあるPlayのAPIを使っています。このように、社内ではいくつか移行途中のセクションがあって混沌としていたところでしたが、Flutterizationでは一つのレガシーな技術から完全に脱却したという事例を作り上げることに成功しました。
これはエンジニアとして働く上でかなり達成感を感じることではないでしょうか?
ちなみに社内Slackには何かしらいいことがあったときに投稿する「iine」というチャンネルがあるのですが、実感が湧いた&バグ修正が落ち着いた頃に嬉しさのあまりちょっと長めの文章を投稿してしまいました(他の人の実名等が含まれるので一部伏せています)。
PGPはFlutterizationによりパートナーさんにとってもエンジニアにとってもよりよいプロダクトに着実に進化していってます。
私はこの時点で新卒から数えて3年目と、まだまだ経験の浅い身分ではありますが、配送ドライバーという職業のあり方を変える、というビジョンを持ったプロダクトを背負った上で非常に貴重な経験をすることができました。
物流業界に変革を起こすべく、Partner Firstなアプリを目指すためにまだまだ開発者は必要なため、これを読んでいただいたモバイルアプリエンジニアの方は是非採用ページを覗いてみてください!
👇当社採用ページ