概要
こちらの記事では、プロダクト配属8ヵ月目にしてWebViewのリファクタリングに成功できた理由を共有したいと思います。
プロジェクト概要
環境
・Android Kotlin
・View
・MVVM
・受託開発
・スクラム
メンバー
3人(時々私1人)
期間
4ヵ月 (8月 - 11月)
プロジェクト発足の経緯
プロジェクトが発足した理由は、WebViewを扱うクラスが複雑化しすぎており、バグの温床になっていたためでした。
このプロダクトではログインや決済周りだけでなく、ネイティブで実装できる画面もスケジュールの都合等でWebViewを使用しています。これらのWebViewは各画面ごとに保持されておらず、WebViewだけを保持する2つのFragmentに処理を任せていました。
こちらのイメージのように、ある画面からWebViewだけのFragment1に遷移した後、必要であれば2に遷移していました。この構成により、画面Aと画面Bでは必要だが画面Cでは不要な処理を実施するために条件分岐が発生したり、一部の画面でしか使用していない変数など処理が入り乱れてカオスな状況でした。。。
簡単に思える修正タスクでも、入り乱れる処理の理解に時間を取られ、針の穴を通すような条件分岐を加えてスパゲッティ化させる必要がありました。それだけしても予期せぬバグを頻繁に生んでおり開発コストがだんだんと高くなっていたため、このプロジェクトが発足しました。
プロジェクトの流れ
- 対象画面の調査
- リリース方針の決定
- 対応画面の選定
- 対応方針の決定
- 実装・テスト
- リリース
※ POとのやり取りは省いています
対象画面の調査
このフェーズのゴールを下記に設定しました。
- 対象画面の洗い出し
- 対象画面数の確認
- 画面ごとのフローの確認
対象画面を洗い出すことで全体数の把握とメンバー間での認識齟齬を防ぎ、POへの共有時に困難さや緊急性を伝えることを目的に作成しました。
洗い出し後、各画面の処理の流れを視覚化するためにフローチャートを作成することで、共通化できそうな処理を大まかに把握でき、以降の初期リリース対象画面を選択する際に役立ちます。
リリース方針の決定
リリース方針としては、一度ですべての画面をリファクタリングしてしまうか、1画面だけをリファクタリングしてから横展開するかの大きく2パターンがあると思います。
前フェーズの資料作成で画面数の多さや複雑さがある程度可視化されていたため、一度にすべての画面を対応するのは現実的でなく、何かしらのバグや問題が発生した際にロールバック作業が大変になることから段階的にリリースすることにしました。
対応画面の決定
対象画面の調査フェーズで、多くの画面が使用している共通の処理とコアとなる重要な処理がメンバー間で理解できていました。また、Web画面をそのまま表示させているためWeb側の変更に弱いことも考慮し、WebView内遷移の少なさも重要と考え、選定基準は下記の通りになりました。
- 多くの画面が使用している共通の処理を持つ
- コアとなる重要な処理を持つ
- WebView内遷移が少ない
上記を踏まえ対象画面を選び、このフェーズは終了となります。
対応方針の決定
リリース方針の決定フェーズにて段階的リリースが決定していたため、既存と新規の混在での対応方針になりました。
初期リリース対象ではない画面は既存クラスを使用し、対象画面だけリファクタリング後の新規クラスを使用する方針であれば既存に影響がなく、安全にリファクタリングを進められると思ったためこの方針に決定しました。
実装・テスト
やっと実装のフェーズになります。しかし、事前にクラス図を作成してメンバー間での認識を合わせてから取り掛かることにしました。
クラス図の作成
実装におけるメンバー間の認識を合わせるためクラス図の作成を行いました。実際は実装時に修正が必要になることもありましたが、事前に作成した置いたクラス図から大きく外れることなく実施することで、レビュー時に意図の説明をする手間が省けます。
共通化の実現方法
上記のクラス図にあるように、共通の処理はBase系に記述しました。共通の処理でもすべての画面で確実に必要な処理のみをBase系に書くことをお勧めします。
例えば、10画面のうち2画面での共通処理をBaseWebViewClientに記述するとします。多くの場合、「Base」とあるため、全てのWebViewClientで継承するものだと考えます。しかし、実際には2画面でしか使用していない処理が存在すると、このプロジェクト未参画のエンジニアや新規参画のエンジニアが混乱する恐れがあります。
また、一部でしか使用していない処理を記述すると、Baseに対する処理の追加のハードルが下がり、どんどん処理が追加されてしまう可能性があります。
以上の内容からBaseには確実に必要なものだけを記述し、一部の画面だけで共通の処理は他クラス等に書くようにしました。
加えて、BaseにはKDocを記述することで「他とは違うんだな」感を出し、処理の追加をしづらくしています。
文章化では見えてなかった課題
特殊な処理
上記では省いていますが、ログインに関する処理もリファクタリング対象でした。ログイン処理自体は既存のものをそのまま使用したので問題はありませんでしたが、ログイン画面へのリダイレクトやログイン後のリダイレクトが少々特殊でした。
事前のフェーズで処理の流れの洗い出しはしていましたが、実際のログイン時のリダイレクトについては洗い出しをしていなかったため苦労しました。
改修の同時進行
ユーザー影響が一切ないリファクタリングとユーザー影響が発生する改修を同時にすると、バグが発生した際にどちらが引き起こしたのか判断が難しくなります。そのため同時進行は避けるべきですが、改修の重要度がそれなりに高く、改修後の方がリファクタリングも実施しやすくなるため同時に実施することにしました。
結果的には問題なく稼働していますが、お勧めはしません。
テスト
テストは普段と同様にUT、IT、UATを実施しました。ただし、リリース後に問題が発生した場合に備えてリファクタリングの内容を除いたブランチを作成しました。
リファクタリングのリリース日と同じ日にほかの改修もリリースされる予定だったため、リファクタリング以外の修正だけが取り込まれたブランチ(以下 除外ブランチ)を作成しておくことで、リファクタリング部分で問題が発生した際は除外ブランチをリリースすればユーザー影響を少なくできます。
この除外ブランチに対して「リファクタリング内容が含まれていないこと」を念のためテストしておきました。
リリース
リリースされて約2週間、今のところ問題なし!!
※ 2024/12/14現在
これから
上記「リリース方針の決定」にもあるように、今回のリリースは第1弾です。今後も引き続きリリースしていくことが必要です。
そんな中、別プロダクトへの参画もお願いされて現在2プロダクトに参画中。。。
時間ない。。。
常日頃からきれいなコードを書くことの重要さを今回のプロジェクトで学びました。
それが難しいのですが頑張りたいと思います!