※具体的な実装方法だけみたい方は「続きの記事(マルチモジュール化したりビルド時間を短くするコツ)」をどうぞ。
#はじめに
マルチモジュール化をしようと思ったきっかけは、ドロイド会議で偶然拝見したこちらの発表でした。
引用:「DroidKaigi 2019 2日目(2月8日)資料一覧」より
「multi-module Androidアプリケーション」
スピーカー:Jumpei Yamamoto様(Sansan株式会社 Eight事業部)
個人的には素晴らしい発表でした。
この場をお借りして、御礼申し上げます。
本記事「思想編」、続いて「実装編」と2回投稿予定です。
###筆者は日々、ビルド時間が長い事に悩んでいた
限られた時間が勿体無い、常にそう感じていました。
当時考えていたのは、マシンスペックを上げること。
しかし、その選択はエンジニア全員のマシンを買い換えることになる。
他に手段は無いものか。
###マルチモジュール化により増分ビルドが-51s(79s→28s)
65%の時間短縮に成功しました。
(%にすると数字のマジックになりがちで、あまり好きじゃないですが…)
5分コーディング→ビルド→5分デバッグのサイクルを1日6時間実施する試算で
1ヶ月あたり7.5hもの削減になります。
5人チームなら37.5h…恐ろしい数字ですね。
###効果はそれだけでは無い?
導入するにあたり、様々な角度から検証したところ、次々と新たな気付きを得ました。
以下、メリットとデメリットを考えていきます。
#メリット
###ビルド時間短縮
原理としては、リビルドが発生する範囲を、修正を実施したモジュールに閉じ込める事で、時間が短縮されます。
ただ、これはモジュールの依存関係を極力、並行にした場合に限ります。
依存関係が直列の場合、上位モジュールに変更が入ると下位モジュールにリビルドが発生してシングルモジュールと同じになってしまいます。
###保守性、拡張性の確保
モジュールは循環参照ができず、かつ、並列な依存関係にする為には、interfaceを定義してDIする手法が適しています。
interfaceを介する事で、後から実装クラスの差し替えが容易になったり、実装クラスを大幅刷新しても影響範囲を限定できます。
良くある「リネーム機能でクラス名を変更→レビュー時に大量の差分が発生して地獄」という地味にツライ状況が回避できますね。
###並行開発が可能になる(かもしれない)
interface経由でのモジュール間アクセスなので、if仕様さえ決めれば並行開発ができます。
(まぁ、理想通りにはいかない部分もあるでしょうが…)
シングルモジュールよりは、複数人のコンフリクトが回避し易いでしょう。
###技術資源の再利用(が可能かもしれない)
複数プロダクトでモジュールを共通利用すれば開発コスト削減できるのでは、と考えています。
例えば、何らかの事情で既存プロダクトの亜種を作るとなった場合を考えてみます。
丸っとコピペすれば、初動は早いと思われます。
しかし、その後のコストはどうなるでしょう?
ソース量が倍になるので、既存メンバーだけでは運用し切れなくなるのが目に見えていますね。
「既存のアプリを3人で運用していたので、亜種アプリの為に3人追加採用してください!」
と上司に相談しても、恐らく提案は通らず
「既存を流用してるし、これまで3人で運用できてたんだから、一旦、今の体制で頑張ってみない?」
となりそうです。
##亜種アプリをコピペ作成した場合を深堀りしてみる
その後のストーリーを、物凄く悲観的に考えてみます。
####やがて既存メンバーだけではタスクが回せなくなり、人を追加するもヒューマンエラー発生
単純に作業量が増えるので、タスクが回らなくなり、人を追加採用します。
しかし、新メンバーは、酷似したコードの海で迷子になりヒューマンエラーが発生するでしょう。
####生産性改善の為の施策で負のスパイラルに陥る
例えば、対策としてレビューが強化されるかもしれません。しかし、機能追加や不具合修正の水平展開の度にプロダクトの数だけ同じようなレビューが発生します。人件費が無駄になりますね。
生産性の低下を深刻に捉えたマネジメント層が、さらなる追加採用を推進するかもしれません。
しかし、チーム拡大に伴いエンジニアだけでなく、マネージャーの採用も必要になるでしょう。
人数が増えれば増えるほど、マネジメントやガバナンス作業のコスト増加、各種作業のオーバーヘッド増加による1人あたりの生産性低下が発生します。
こうして人件費がかさみ、収支のバランスが崩れて企業が危機に陥る…(かどうかは分かりませんが…)
####似たような機能を共通化しないと、運用コストの効率が悪い
上記の通り、質的には似たコードが倍になることで、人が増え、似たような作業やエラーが各プロダクトで発生し、効率が悪いです。
機能や不具合修正を水平展開する際のヒューマンエラーリスクの除去を考えないと、表面上は人員追加で組織拡大中!と見えるが、実態が伴わないチグハグな状況になるかもしれません。
危ういですね。
####筆者の実体験 - コピペで作られた亜種アプリ6個の保守 -
筆者は、過去に所属していた会社にて、請負開発として3種6アプリ(Android3個, iOS3個)の亜種アプリがコピペで作られている状況に遭遇したことがあります。
最終的には共通モジュール化を顧客に提案&一人で全対応し、生産性が飛躍的に向上しました。
共通モジュール対応前は、(前任者が織り込んだ)不具合が見つかる度に全アプリにコピペし、レビューし、試験し、顧客の受け入れ確認を申請し、それはもう大変でした。
特にしんどかったのは、6個のアプリが共通でもつ機能が、ほんの少しずつカスタムされていた事です。
アプリを運用する過程で、少しずつ継ぎ足されたのでしょう。
新機能や不具合の水平展開時に、この小さな差に気付かずにコピペ実装し、レビューもスルーされ、二次不具合が出てしまったのは良い(?)思い出です。
####「人間はミスをする前提で仕組みづくりをするべき」
「プロなら見逃さないスキルをつけるべき」というご指摘もあると思いますし、ごもっともです。
ただ、筆者は人間という最も不確実性の高い要素が最大のリスクだと考えますので、マルチモジュール化を推したいですね。
#マルチモジュール化のデメリット
さて気になるデメリットです。
ここまで良い事ばかり書いてきましたが、当然、デメリットもあります。
###モジュールを再利用する場合のバージョン管理が複雑になる
これは大きな課題です。
亜種アプリそれぞれは、どのバージョンのモジュールを使っているのか、外部ライブラリとのバージョン整合性は取れているかが問題になるでしょう。
しかし、筆者としては、バージョン管理の複雑化を受容してでも、将来的なコスト削減と、水平展開の容易さからモジュールの再利用を推進したいと考えています。
再利用をやめるのは簡単なので、まずはやってみて、発生した問題ベースで考えようと思います。
###抽象度が高くなる
複数のモジュールやプロダクトで動作するプログラムを書くので、抽象度が高くなります。
つまり、別の人が見て「このコードは何をやりたいのか全く分からん。。。」という事が起こりやすくなります。
この辺はコメントを充実させるなど、意図が後任に伝わるよう配慮が必要です。
###マルチモジュール化への移行はコストとリスクを伴う
作業開始に際してお伝えしなければならないのは、マルチモジュールに変換するのには適さないアーキテクチャがあるということです。
つまり、最初のステップとして、適したアーキにリファクタする必要が出てきます。
筆者の経験上、まずは水平な「レイヤードアーキテクチャ」にリファクタリング後、レイヤー毎にモジュールにしていく事をお勧めします。
適さないアーキからマルチモジュールにしようとすると、恐らく相当大変です。
というのも、マルチモジュール化すると依存関係の相互参照が不可能になりますので、参照が絡み合った機能を分解しながら配置するモジュールを変更するのは非常に大変です。
こうして2段階のリファクタリングを経る事で、大掛かりな変更が発生し、単純にコストがかかりますし
不具合混入のリスク、ヒューマンエラーのリスクが非常に高くなります。
#いかがでしたでしょうか
弊社の場合、作業の見積もりと、それをペイする計画・提案(例えばビルド時間がXX秒短縮されるから月間XX時間の効率化になる、とか、将来的な運用コストの話とか)を経て、一部のプロダクトのマルチモジュール化が実現しました。
全プロダクトへの水平展開は、現在も検討中です。
皆さまにおかれましても、「何のため、どんな目的のため、手段としてマルチモジュール化を推進する」のかご検討された上で、生産性が改善されれば幸いです。
次回の投稿では、実際にプログラミングする上でのtipsを投稿します。