1
/
5

入社してすぐにAndroidアプリの大規模リファクタリングに着手!その時自分に誓ったこと【後編】

こんにちは。コーポレートスタッフの茂木です。
前回に引き続き、MoneyEasyのAndroidアプリ開発を担当している照井さんに、その後、大規模リファクタリングをどのように進めたのか、語っていただきました。
前回は、着手前の状況などについてでしたので、結局どうなったの?問題無かったの?と気にされていた方もいたかもしれませんね。

リリースまでの感動秘話をお届けします!
どうぞ最後までお付き合いください。

前編はこちら

入社してすぐにAndroidアプリの大規模リファクタリングに着手!その時自分に誓ったこと【前編】 | 株式会社フィノバレー
こんにちは。コーポレートスタッフの茂木です。 今回は、2018年1月に入社以来、 MoneyEasyのAndroidアプリ開発を担当している照井さんに、入社後、最初に行った大工事であるAndroidアプリのフルリファクタリングについて、語っていただきました。エンジニアの方には、苦労も含めて共感いただけると内容になっていると思います! ...
https://www.wantedly.com/companies/finnovalley/post_articles/250465

具体的な進め方

必要な設計/検討はどれも重要でしたが、それとは別にどうやって進めていくのが最も良いのかを考えることも重要でした。
なにせまだAndroidアプリの仕様全貌はつかめておらず不明な機能もそこそこある状態でした。ドキュメントもテストコードも無いため動かして確認するしかありません。

最終的に次の2つを厳守した状態で進めていくことにしました。
1.既存アプリとリポジトリを完全に分割
2.常に動く状態を保つ

1については既存アプリとそのコードは完全にそのままの状態とし無用な事故を防ぐ目的もありました。
2については本当に細心の注意を払いました。

想定する設計でリファクタリングしてしまうと元コードの状態が跡形もなく消えて無くなるため、ある程度リファクタリングを進めてしまった後にビルドエラーや不思議な挙動でどうにもならなくなった場合、最悪動いていたところまで手戻りが発生し調査に無駄な時間がかかります。

当然、全てリファクタリングした後に動かすなんてリスクが高すぎて取れません。
こういう時テストコードがあれば心がとても安らぐのですがこの状態でテストコードを書くのは不可能に近かかったので(書くならせめてビジネスロジックをViewから分離した後)この時は「常にアプリが動作する状態にする」ことを徹底しました。

「常にアプリが動作する状態にする」にした強い理由がもう一つあって8月頭から全機能テストが始まるため、7末に実装を完了させたところで「アプリを1度も動かしていない状態」ではテスト工程に入れるレベルの品質に達していないのは明白で話になりません。

かといって、7月中旬あたりにリファクタリングを全て終わらせて2週間くらいで動作確認しまくる、というスケジュールも高確率で破綻すると考えました。

それならばチマチマ動作確認をしながら進めることで実装完了とともに簡単な動作確認も完了させてしまうのが最も早そうという結論に達しました。

この進め方にはメリットがあって、この方針でいく場合は機能間の結合度が高いとチマチマ動作確認する意味がなくなりますので可能な限り機能同士が疎結合になることを意識して進めました。

これは改めて意識するまでもなくコード書く上で当たり前のことですが、今回は後述するようにある程度元コードを保った状態で進めていくことにしたので、油断すると元コードの密結合をそのまま引き継いでしまう恐れがあったのです。それを回避するため改めて意識する必要がありました。

この状態を維持しつつ、FatActivityからいきなりAndroid Architecture Components(以下、AAC)を使用したMVVM構成にするのは難易度が高すぎるので、段階的にリファクタリングできるよう4つのポイントを設けることにしました。

4つのポイント

リファクタリング期間3ヶ月を大きく4つのポイントで区切って段階開発していきました。

各ポイントの概要は以下の通りです。
ポイント1:FatActivityからビジネスロジックを切り離しレイヤー構造にする
ポイント2:AAC以外のライブラリ導入
ポイント3:1機能のみKotlin+AAC対応
ポイント4:全機能Kotlin+AAC対応

これらはどのポイントでリファクタリングを中断しても一応コードは使える状態であることを意識しています。

6月にいきなり既存アプリへ大きな機能が追加となる可能性も0ではなかったため、その場合は4月〜5月のコストがなるべく無駄にならないように配慮した結果でした。
ポイント4まで到達しないと自分の目的は達成できないので、そういったことが無いことを祈りましたが。

ポイント1:FatActivityからビジネスロジックを切り離しレイヤー構造にする

何をやろうにもまずFatActivityだとどうにもならないので、真っ先にやることはViewからビジネスロジックを切り離すとともにクラスやメソッドを意味のある単位で分割しレイヤー構造にすることでした。

具体的には以下のイメージ図のように内部構成を変えました。
このポイント1は5月中旬くらいまでに完了させる予定で行い、ほぼオンスケで完遂できました。

ここでの注意点は「内部ロジックは可能な限り変更を加えない」ことでした。
これをやっている間、凄まじくリファクタリング衝動にかられましたが、無駄になるかもしれないし余計な混乱を招く恐れが高かったため、変数名一つ変えず極力そのままレイヤー分割に徹しました。

ただ、そうはいってもどうしても追加修正しないといけないコードは出てくるので、そういったものは修正しています。

なぜこんな注意点に徹したかという理由ですが、コンパイルエラーやおかしな動作をした時に元コードとの比較が容易になるためです。

たまにおかしな変数がおかしな値を持っていることがあり、そのままにしておくと元コードでの検索が容易になるため何を入れていて何をしていたかの調査が容易でした。

実はSIer時代にこの時とはちょっと違いますが、巨大な機能のリファクタリングしたことがあって、その時の失敗経験をここで活かすことができました。

ポイント2:AAC以外のライブラリ導入

AACを導入するとViewModelが間に挟まり元コードの名残がだいぶ消えてなくなるので、まずそれ以外のライブラリを順次導入していきました。

ライブラリの選定はとても慎重にやりました。ここはAndroidエンジニアでないと選定が難しいので、自分一人で検討することになりますが、ありがたいことにOSS自由に選定して良いという方針でした。

ただ、MoneyEasyはお金を扱うサービスですので、ちょっと便利だからという理由でライブラリを入れまくることはしたくありませんでした。

自分がよく知っているもの、デファクトといっていいくらいAndroid界隈では有名なもの、活動が活発でドキュメントが需実しているものなど、いくつか基準を設けてかなり絞って選定しました。

導入の際にインパクトが大きかったのはDagger2とRxJavaでした。特にDagger2はApplicationからDIするので部分導入できず、ほぼ全クラス修正が必要となりなかなか苦戦しました。

また、Dagger2は以前から趣味アプリで使っていましたが、ComponentsやModuleを理解せず使用していたため、このタイミングで1からDagger2を学習しました。

Rxは自分のお気に入りライブラリトップ3に入るくらい好きで、以前から使っていたため学習は不要でしたが、これも導入時は大量に修正が必要でしたので時間がかかりました。

このポイント2までは5末までに完了させる予定でした。実はここでちょっと足が出て6月1週目に食い込んでいます。

ポイント3:1機能のみKotlin+AAC対応

次は1機能だけKotlin+AACで動作させるようリファクタリングしました。

これをやると完全に元コードの面影が消滅するので、エラー解析が今までより面倒になりますが、まずView〜RepositoryまでKotlin化してしまい一度動作確認、それからAACを導入することでUseCase〜Repositoryは動作確認できている状態になるため、調査範囲をViewModel&Viewに限定できて効率化できたかなと思います。

Kotlinらしいコードに書き換えるのはこの後にやりました。

ポイント3は6月中旬くらいまでかける想定でしたが、ポイント2でちょっと足が出ていたため予定より期間はありませんでした。
ただここはちょっとバッファを持っていたためリカバリでき、当初予定通り6月中旬ちょっと前にこのポイントを終えることができました。


この最終構成は、『Droidkaigi2019』などで弊社ブースにて配布したポストカードにも載せています。

ポイント4:全機能Kotlin+AAC対応

ここからは7末までひたすら全力全開で全機能リファクタリングをしていきました。当時は大雑把に分割しても25機能でだいたい70画面くらいあったと思います。

ポイント3で大体やり方がわかったのでこのポイントは比較的すんなり進めたのですが、それとは別に「KotlinがJavaと100%互換である」という素晴らしい特性を持っていたことですんなり進めたと思います。このおかげで1機能ずつ「リファクタリング→動作確認」という手段を取れました。

ポイント4は無事7末で完了でき、動作確認も逐一行なっていたため、そのままテスト工程へ突入しても差し支えない程度の状態にすることができました。

実際この後、8月に全機能テストをしていただき無事にリリースすることができました。

当然テストではバグがかなり出たのですが致命的なものはほとんどなく、またテストをしっかり行なっていただけたこともあってリリース後に大きなバグも発生しませんでした。

最後に

細かい点はまだまだ多く苦労した点も面白かった点もたくさんあるのですが、それを話すと長編になってしまうのでまたの機会にしたいと思います。

リファクタリングはエンドユーザーにはほとんど見えない部分ですが、実はTextFieldなどはMaterialDesignに準拠して改善していたので、所々UIに小さな変化を加えていましたし、全体的にかなり性能改善をすることができました。

特に起動周りとお店一覧は劇的に改善させたので、PMや開発メンバーが気づいてくれたことは嬉しかったです。
自分にとってこのリファクタリング作業はとても大きな成果でした。

強制的に全コードを読む羽目になったためAndroidアプリの全容を知ることができ、可読性も凄まじく向上したためその後の開発効率が格段に上がりました。

それから少ししてAndroidエンジニアがもう一名参画し、ユニットテスト/UIテスト、CI環境などを整備してくれて開発効率がさらに上がりました。

やはり一人では限界があるのでとても感謝しています。

この大工事からそろそろ2年が経ち、少しずつ当時の設計や導入ライブラリが劣化していると思っています。ライブラリは小まめにバージョンアップしているものの影響範囲が大きいものはなかなか入れ替えが難しいのが現状です。
しかし、継続的なリファクタリングはサービスの寿命も伸ばしますしエンジニアのモチベーションにも開発効率の向上にも繋がるはずです。

次の大きなバージョンアップではCoroutineを導入する予定で、これからも継続的なメンテナンスは大事にしていきたいと思います。


照井さん、ありがとうございました!

私も、普段エンジニアの作業内容を知る機会はあまりありませんが、こうやって過程を知ることができて良かったです。

今回もお付き合いいただき、ありがとうございました。

株式会社フィノバレーでは一緒に働く仲間を募集しています
2 いいね!
2 いいね!
同じタグの記事
今週のランキング
株式会社フィノバレーからお誘い
この話題に共感したら、メンバーと話してみませんか?