Mobile Tech Leadの久保出です。
この記事は、技術書典15で配布するWANTEDLY TECH BOOK 13の内容となっています。
弊社では、2020年4月頃からKotlin Multiplatform (以降KMP)を導入しています。
この記事では、KMP導入から2023年10月現在までを振り返り、KMP自体についてや新技術導入という行為としてなどの観点でまとめ、今後の改善点を探ります。
KMPの導入事例、新技術導入の事例として参考になれば幸いです。
Kotlin Multiplatformとは
Kotlin Multiplatform (KMP) は、クロスプラットフォーム技術のひとつです。
KMPは、Kotlinの言語機能のひとつであり、Kotlinの単一のコードベースから複数のプラットフォーム向けの実行可能なコードを生成します。
KMPにより、異なるプラットフォーム間でのコードの共通化を実現でき、コードを書く時間と保守する時間を減らすことができます。
KMPは、様々なユースケースがあります。
一番利用されているユースケースは、iOSとAndroidのモバイルプラットフォーム間でのコード共有です。KMPの特徴として、コードを共有するレベルを自由に決められます。
ネットワークレイヤーのみを共有することもできますし、ドメインロジックのレイヤー、プレゼンテーションレイヤーまで共有するなど、選択肢があります。
また、Compose Multiplatformという技術も登場しており、UIまですべて共有することも可能です。
なぜ導入したか
弊社では、導入時期以前に大きな課題が2つありました。
ひとつは、iOSとAndroidアプリに部分的に導入されていたReact Nativeがメンテナー不在による負債となっていたことです。
モバイルアプリには、部分的にReact Nativeが導入されていました。これは当時、Webフロントのリソースを使ってアプリの開発を進めたい時期があり、そこから導入されたものでした。
リソース問題が解決すると、それ以降はReact Nativeの部分の開発が止まり、メンテナーが不在になりました。
もうひとつは、iOSとAndroidで仕様差異が多発していたことです。
プロダクトとして、モバイルプラットフォーム間での仕様差異は極力減らしたいですが、コードがiOSとAndroidで分かれていることにより、どうしても差異が発生します。
具体的には、データキャッシュの仕様が異なっていたり、エラーハンドリングが片方で甘いなどがありました。
こういった課題は仕様書により改善することもできますが、エラー処理などの細かい挙動は、仕様として明言されづらいため、仕様書だけでは解決しづらいです。
この差を埋めようにも、片方をもう片方にあわせに行く形になり、差を明らかにするためのコードリーディングやレビューがコストとになっていました。
このような課題を解決する手段として、KMPの導入を進めました。
チーム構成
弊社では、2023年10月現在、マトリクス型の組織構造を取っています。
1エンジニアは、プロダクト開発を軸としたSquadと技術領域を軸としたChapterそれぞれに所属します。
モバイルプラットフォームの技術領域は、iOSとAndroidで分けることなくMobile Chapterとして活動しています。
これはKMP導入当時から変わっていません。
Mobile Chapterは、KMP導入当時は6名で一時期2名まで落ち込みましたが、現在では5名で運営しています。
弊社視点のタイムライン
振り返るために、現時点までの社内とKMP自体の歴史を洗い出し、時系列にまとめてみます。
- 2017年12月 Kotlin 1.2からKotlin Multiplatformが登場
- 2019年12月 React Native再検討プロジェクトを立ち上げ
- 2020年3月 React Nativeを削除する方針を固める
- 2020年4月 KMPリポジトリであるvisit-app-sharedが誕生
- 2020年7月 AndroidアプリでKMPを導入したバージョンがリリース
- 2020年8月 Kotlin Multiplatform Mobile (KMM) という言葉が誕生
- 2020年9月 「React Nativeをやめる話とKotlin Multiplatform」というブログを出す
- 2020年10月 AndroidアプリからReact Nativeを削除
- 2021年1月 iOSアプリでKMPを導入したバージョンがリリース
- 2021年3月 iOSエンジニア視点の「Kotlin Multiplatform MobileによってWantedlyのiOS開発がどう変わったか」というブログを出す
- 2021年4月 iOSアプリからReact Nativeを削除
- 2021年10月 iOSからKMPの依存方法をsubmoduleからCocoaPodsに変更
- 2022年3月 KMPにGraphQLを導入
- 2022年8月 iOSアプリでKMPとSwiftUIを組み合わせた開発を開始
- 2022年10月 KMM Betaがアナウンスされる
- 2023年7月 KMMやKMPなどの命名のばらつきがKMPへ統合される
- 2023年8月 AndroidアプリでKMPとJetpack Composeを組み合わせた開発を開始
振り返り
ここからは、観点を分けて振り返りを書いていきます。弊社でよく使われるKPTの手法で、観点ごとに振り返ります。
KMPという技術観点
KMPという技術自体に対して振り返ります。
#### Keep
仕様差異が減った
課題としてあった、iOSとAndroidの仕様差異が確実に減りました。特にドメインロジックはプラットフォーム間での差がほぼなくなったと言えます。
しかしUIロジックは、ある程度iOSとAndroidに存在したりプラットフォームに依存するため、KMP導入後も差異はあります。そのため、完全になくなったわけではありません。
後追い実装のコスト削減
弊社ではiOSの実装を先行して行うことが多いです。これは、iOSエンジニア比率が高いこと、iOSユーザー比率が高いことなどが理由としてあります。
そのため、Androidの実装が後追いになることが多いです。(実際にはiOSが後追いになるケースもあります)
こういった後追い実装をするとき、KMPでの実装がすでに完成しているため、後追いでの実装がほぼUIのみとなり、実装コストが大幅に削減できました。
反面、先行実装はKMP実装とネイティブ実装を同時に行い、両OSのことも考慮したKMPの設計・実装を行う必要があるため、若干実装コストが増加します。
仕様書の作成
KMPは、共通のコードを実装するため、設計ミスやバグは両OSに影響を与えます。
そうなると、今まで片方の修正だけで済んでいたものが、両OSを修正する必要があるため、修正のコストが増加します。
この軽減策として、KMPのプレゼンテーションレイヤーに対して画面仕様書レベルの仕様書を作成するようにしました。
その結果、仕様を議論して固めてから実装に入るようになり、設計の手戻りが減りました。
また、先にインターフェースを決める形になるので、UI実装をKMP実装を並行して進めやすくなりました。
JetBrainsなどの後ろ盾
KMP導入当時は、KMP自体がAlphaというステータスでした。
KotlinにおけるAlphaというステータスは、「製品化を決定しているが自己責任で使用すること」と定めています。
当時からCash AppやTouchlabといった組織がKMPに取り組んでおり、KMP化の流れができつつありました。
その後、GoogleもKSP(Kotlin Symbol Processing)を公開したり、AndroidXライブラリを一部KMP化するなど、KMPを広める動きをしています。
こういったJetBrainsの方針や各社の後ろ盾により、今後の不安要素は少なめであると思い、導入の意思決定の助けとなりました。
#### Problem
iOS視点の問題
KMPの課題としてよく言われていることですが、KMPのバイナリはiOSからはObjective-C互換で見えます。そのため、様々な互換性の問題を抱えています。
例えば、KMPで定義したインターフェース上のジェネリクス型パラメーターが消失してしまうこと、sealed classがSwiftのenumにならないためExhaustiveにできないことなどです。
また、iOSエンジニアからするとKotlinという別言語に触れることになりますし、IDEやツールも異なる環境となります。そのため、iOSエンジニアからはKMPへのハードルが高くなります。
期待したほど生産性は上がらない
大きく分けて両OSでドメインロジックとUIの2つに分かれると考え、KMPによってドメインロジックが共通化されることで、最大25%の生産性改善、現実的にはオーバーヘッドがあり15%程度と考えていました。
しかし、体感のパフォーマンスに変化はありません。(そもそもパフォーマンスの指標を計測していないという問題もあります)
これは、片方のOS単体で見ると、KMP実装のオーバーヘッドが純粋に増えたようになるため、工期としては増えてしまうためだと感じています。
では、全体的なパフォーマンスは改善したのかというと、これも体感はあまり変化がありません。
しかし、長期的に見ると、仕様書を作るようになったという変化や、ドメインロジックに対するテストが増えたこと(iOSは全然テストが書かれてない)など、長期的に見ると以前より確実に改善している面があります。
そもそも短期的な生産性の改善を期待していたことが誤りだったとも思います。
#### Try
SKIE
TouchlabによりSKIE(スカイ)というツールが公開されています。
これは、KMPとSwiftの互換性を改善するためのツールです。
前述したようなiOS視点の問題を大きく改善することができるため、SKIEを導入することを検討しています。
Compose Multiplatform
KMPによりコードの共通化は進みましたが、UIについては両OSでそれぞれ実装しています。
UIについても共通化することで、UI実装時の問題や生産性の改善が期待できます。
Compose Multiplatformは、UIも共通コードにすることができます。
Jetpack Composeをマルチプラットフォーム化した技術であり、Jetpack Composeのノウハウを活かすことができ、Androidエンジニア視点では学習コストが低い技術です。
ただし、Compose MultiplatformのiOS対応はまだExperimentalであり、実用段階ではありません。
また、iOSエンジニアからすると、iOS固有の実装がほとんど無くなるため、抵抗感があることでしょう。
したがって、Compose MultiplatformはKMPよりも慎重に向き合う技術だと思います。
新技術導入という観点
新技術を導入する行為に対して振り返ります。
#### Keep
既存技術にも触ってみた
良かった点として、既存技術であるReact Nativeにも触ってみたことが挙げられます。
React Nativeが負債になってしまっているとは感じていたものの、本当に負債なのか、具体的に何が問題なのかが言語化できていませんでした。
触れた結果として、React Native自体は生産性高く開発できて利点があると感じましたが、モバイルエンジニアから親しみの薄い言語やツールに密結合し、それをモバイルエンジニアがメンテすることが課題だと言語化できました。
それにより、React Nativeを取り除くという意思決定をして、手法として両OSで書き直すか、KMPを導入するかという話に進めることができました。
中間振り返りの実施
KMP導入の途中、Android側にだけ結合した段階と、iOSに結合した段階でそれぞれKMPに対する振り返りを実施しました。
何が改善されて、何が課題として残っているのかを洗い出し、全体的に見てこのまま進めていくかどうかを議論しました。
上がった課題として、iOSエンジニアからのハードルがあることが挙げられ、モブプロやペアプロにより学習機会を増やして課題を解決するなどしました。
社外へのアウトプット
KMP導入直後、React Nativeをやめる話とKotlin Multiplatformというブログを公開しました。
英語化した記事はJetBrainsにも紹介され、広く認知を得ることができました。
採用事例として紹介されたり、他社のKMP導入時の参考にしてもらえたりと、様々な反響を得ました。
また、認知獲得が採用面でも大きく響き、採用のファネルの前半が大きく改善しました。
導入事例の少ない新技術についての導入事例であったため、今後に生かせるとは限りませんが、何であれアウトプットはしたほうが良いという認識になりました。
継続的な普及活動
React Nativeの失敗点として、メンテナーが不在になったことが挙げられますが、これは技術を横に広げる動きがなかったことが大きいと考えています。KMPもiOSエンジニアからしたらReact Nativeと変わりません。
そのため、iOSエンジニアにも積極的にKMPに触れてもらうように、モブプロやペアプロを進めました。
また、導入した自分がメンテナーとして居続け、導入した技術に責任を持ち、継続的にメンテナンスしたことが良かったと感じています。
#### Problem
検証不足
導入検証時、iOSでも問題なく動くということは検証していましたが、後ほどKMPの実装が進んだ状態で結合をしようとしたときに問題が発生しました。
具体的には、git submoduleで結合しようとしたのでiOSビルド環境にJDKが必要だったり、当時のメモリ管理モデルの問題で実行時例外が多発したりなどです。
今後は、PoCするにしても実際のコードや環境に合わせて行おうと思います。
社内合意不足
導入を決定し、実装をある程度進めたタイミングで、上長が変わることがありました。
その人には、KMPのことは伝わっておらず、KMPの導入を止められそうになったこともありましたが、なんとか納得してもらいました。
必ずしも全員に合意して貰う必要はないと思いますし、技術のリスク次第にはなると思いますが、直属の上長以外にも取り組んでいることを周知して認識してもらうべきだと思いました。
ニッチ
新技術のスキルを持っている人は、当然市場にはいません。結果としてスキルマッチによる採用はかなり難しくなり、社内から育てるような仕組みが必要になります。実際、リソース不足から業務委託を採用する際には、市場にはKMP経験者などいませんでした。
KMP自体は、Kotlinのスキルがあれば応用できるため、採用面ではネックになることはありませんでしたが、全く新しい技術となるとそうはいかないでしょう。
#### Try
より細かいフィードバックを回す
中間振り返りは実施しましたが、振り返りの期間はもっと短期で周期的にやればよかったと思います。改善点を見つけて対処しやすくなりますし、他者からのフィードバックを受けやすくなります。
フィードバック自体もチームに閉じず、取り組みを他の技術領域にも共有して、リスクや懸念をもらえるようにしたいです。
まとめ
主観にはなりますが、KMPの導入について振り返りました。
こうやって振り返ること自体、新技術導入の検証になると思いますし、様々な取り組みや反省点に改めて向き合えるいい機会になりました。
今後も機会があればこうやって振り返っていきたいと思います。
今後の展望としては、いくつかやりたいことがあります。
まずは、改善すべき点がある程度見えているため、改善を重ねていくこと。
振り返りや関係者からのフィードバックを受け、改善点を探っていくこと。
KMPという技術自体へ貢献し、KMP自体を改善していくこと。
やれることはたくさんあるので、今後もKMPに対して継続的に取り組んでいきます。