AndroidとiOSのエンジニアでKMPの実装をペアプロしている話

これは Kyash Advent Calendar 2023 の5日目の記事です、こんにちは あるいは こんばんは。KyashでAndroidエンジニアをしている 牧山(@_rmakiyama)です。

自分の所属するチームがAndroid / iOS 2名ずつから、Android / iOS 1名ずつのチーム体制になったことを機に、チームのiOSエンジニアと相談してペアプロにトライしてみたところ、とても開発体験が良かったので紹介します。

ペアプロをはじめた背景

Kyashは去年からKMP(Kotlin Multiplatform)を採用しています。KMPは任意のスコープを共通のコードとして開発することができます。 KyashではUIをネイティブによる実装(Jetpack Compose / SwiftUI)をする選択を取り、UIレイヤーの中でもView以外の実装をKMPで共通化するようにしています。これは、UIの状態を持つStateHolderにあたる部分からはKMPの実装となっていることを指していて、大部分でコードの共有がされています。
各OSとKMPの関係は下図のようになっています。後述しますが、StateHolderにあたる部分がReactorです。

KMP導入時の課題であった「両OSで同じロジックを2度実装することによる問題」と「OSでロジックが異なったことによるインシデント」についてはKMP採用によりかなり解消されています。
そんななか、まだ大きく顕在化していないものの、下記のような課題がモバイルチームであがるようになってきました。KyashのValueのひとつは頂点志向なのです。

  • KMPの実装を担当するエンジニアの偏り
  • OS間差異を含めた認識の齟齬による手戻り

KMPの実装を担当するエンジニアの偏り

KMPはKotlinの実装になります。Kyashでは、導入当初からiOSエンジニアも積極的にKMPの実装タスクを拾ってくれています。とはいえ、プロジェクトが忙しくなったりスケジュールがタイトだったりした場合は、Kotlinの経験値という意味ではAndroidエンジニアのほうが短期的な実装スピードが上がるため、Androidエンジニアが実装することが多くなってきています。

短期的な方針としてAndroidエンジニアが実装する判断自体は良いと思っています。しかし、iOSエンジニアは普段からKotlinを書いているわけではないので、どうしてもKMPを久しぶりに書くときのコストが大きくなってしまいます。

OS間差異を含めた認識の齟齬による手戻り

KyashではMVIベースのアーキテクチャを採用しており、KMPのレイヤーではUIのState HolderとしてReactorが定義されています。これはSwift向けのライブラリであるReactorKitのコンセプトを取り入れたものとなっていて、Wantedlyさんの事例を参考にさせていただいています。詳細は各ドキュメントを参照してください。

Reactorには、UIの状態を表すState、ユーザーからのインタラクションやライフサイクルの変更を表すAction、UIへのイベント通知を表すEventを定義します。

実装者はどのようにState / Action / Eventを定義すればUIを表現・実装できるかを意識しながら実装をします。このとき、AndroidエンジニアならActivity / Fragment / Jetpack Composeを、iOSエンジニアならViewController / SwiftUIなどを頭に浮かべながら実装することになります。
各OSでUIコンポーネントの構築方法やインタフェース、ライフサイクルや画面遷移の仕組み、さらには既存実装との兼ね合いなどがあり、レビュータイミングや実際にUIとつなぎこんだときに認識齟齬や考慮漏れがあり手戻りが発生することがありました。

どうやってやっているのか

スモールな構成になったので、よりアジリティを高めるためにもペアプロをやってみたいと考えていることを同チームのiOSエンジニアに相談したところ、「僕も同じこと考えてた!」ということもありペアプロをトライしてみることになりました。とびらを開けました。

事前準備をした

トライするにあたり、ふたりともペアプロは初心者だったので、進め方や役割としてどういう振る舞いをするのが良いかのすり合わせを行いました。いろんな記事を参考にしつつ下記のような整理をラフに行いました。

  • ナビゲーター
    • プログラムの大局的な構成/方針を考える
    • ドライバーの疑問をかわりに調べる
    • ドライバーが書いたプログラムをリアルタイムにレビューする
  • ドライバー
    • 詳細の実装をする
    • わからないことを聞く
  • 気をつけること
    • ドライバーはなにを実装しているか喋る
    • ナビゲーターは気になったら質問もする

とはいえ、お互いがやりやすいように役割に固執しないことも合意して進めました。

まずはトライしてみた

初手としてReactorの実装を進めてみました。
Reactorは対象の画面で、どんなユーザーアクションが起きるのか、どんな情報をUIに表示するのか、処理によってどんなイベントが発生するのかなど、コアな部分になります。
そのため、ペアプロ自体の体験は良かったものの、気づいたら数時間が経過していて、ドッと疲れが来るとともに、Slackのメンションやレビュー依頼に気を回せていなかったことに気づきました。

そこで、その日にすぐペアプロの振り返りをすることにしました。その中では下記のような意見が出てきました。たくさん出てきました。

  • コードコメント書いたほうがよいよねに気づきやすい
    • それを言うのもPRレビューの時より言いやすい
  • 考え方を話しながら、聞きながらできるのがよかった
  • つい話し過ぎちゃう側面もある
  • カチッと役割をわけすぎなかったのもよかったかも?
    • やるペアにもよりそう
    • 役割をある程度最初に話してペアプロをトライしたのもよかったかも
  • 時間の感覚分からなくなりがち
  • お互いの時間確保できるかも大事になってきそう
  • 他のチームのPRレビューができなくなってて悩み
  • KMPはビルド時間とかがあんまりないからやりやすかった
  • 悩みどころ、落とし所を作らないといけないところのコミュニケーションがすぐ取れるのがよかった
    • ここは省略しようとか別PRに分けようとか
    • レビュー入ってからの手戻りもないのがよい
  • テストのレビューのない(リアルタイムにできている)のよい
  • 少人数を強みにできる方法かもしれない
    • モブプロはまた感覚違いそう
  • Reactor以降に集中して実装を進められるのは良い
  • テンプレート的なやつ作りたいよね〜みたいな話も出るのも良い

結論、満場一致(ふたり)でとてもよかったので続けていこう!と、論理ハイタッチをしました。

振り返りのループをまわしている

さて、初回の振り返りから2回目はいくつかトライを実施しました。

  • ポモドーロを採用してタイマーをセットする
    • 25分ペアプロ・5分休憩(可変)
    • レビューやSlackの確認などの時間確保のため
  • 緊急の用事とかあったら遠慮せずストップする
  • MeetではなくSlackのハドルを使ってみる
    • 普段ハドルを使っていて慣れている
    • ハドルの画面共有にペンを使える機能が欲しくなった

このトライはうまくハマっています。
さらに何度かペアプロを重ねていますが、ポモドーロも厳密にせずチェックポイントとして利用するようにしています。あとちょっとなので実装しきっちゃおうとか、実装途中の関数までやろうとかを決めるタイミングのイメージです。強制的に休憩に入る効果もあると思いますが、ペアプロをするふたりの性質に合わせて決められたのも良かったと感じています。

この振り返りやトライは別チームのモバイルメンバーにも共有しており、組織としてもKMP実装の開発がよりスムーズになるように動き始めています。やっていくぞ。

やってみてのふりかえり

実装の認識齟齬が減った

ペアプロ前も事前に軽く話すことはありますが、実際に自分が実装するぞとなってから見えることもあります。この作業をふたりで行えたので、今のところ大きな齟齬がなくタスクを片付けられています。

特に、UIの責務にするかReactor(KMP)の責務にするか迷うケースはすぐその場で認識合わせができることは大きなメリットに感じています。最近の例だと、タップすると折りたたみするUIを実装するケースがありましたが、このUIが折りたたまれてるかどうかの状態をどこで管理するか考える場面がありました。普段なら、ひとりで両方のケースの良し悪しを検討しその上で実装したものをPull Requestとして出してフィードバックをもらう、という流れになりがちです。意外とうんうん考えるものです。ペアプロではすぐに口頭で相談して方針を決められるため、リードタイムが段違いでした。この例では、タップのみをトリガーとしてUIの開閉があるため、UIの責務とする方針となりました。こういう細かいニュアンスはPull Requestのコメントやり取りではコストがかかったりするのでとてもよい体験でした。

メンバーの実装の考えや手癖を覗けるのがよかった

ひとりで実装しているときは、Pull Requestでのアウトプットしか共有できていませんでした。もちろんPull RequestのDescriptionに実装意図を記載するなどを行っていますが、些細な迷いニュアンスなどはなかなか知ることができません。

ペアプロでは、わいわいと話しながら実装をするので、「こういうときってクラスにするか迷うんですよね」「when式の中括弧そろってないと嫌なタイプなんですよね」「テストのアサートのやりかたどっちがよいかな」「「わかる」」みたいな会話が自然と出てきます。迷ったときに秒のフィードバックが行えるのはペアプロならではだなと強く感じました。
また、今回はAndroid / iOSと別のプラットフォームを扱うエンジニアふたりで行ったペアプロということもあり、各OSで気にしていることや難しい部分なども話せたのも良かったポイントです。

実際に手を動かしてるところを見るのもよく、知らないショートカットコマンドを知れたり、便利なプラグインを教え合ったりすることもできます。意外とこれも、当人にとっては当たり前だったものが意外と知られていないということも多いのでペアプロ効果でした。

開発効率が高まった

これまで、ひとりがKMP実装をしているときにもうひとりが先にUIのデザイン実装を進めるやりかたで進めることが多くなっていました。こうすると、UIの状態を持っているのはKMPになるので、デザインの実装が終わったあとに、KMPとのつなぎ込みの実装が発生していました。また、つなぎこみのタイミングで、KMPの実装漏れや認識齟齬に気づくことも多々あり、少し効率が悪いように感じていました。

ペアプロを導入してからは、先にKMPの実装を終わらせてからUIの実装を行うようにしています。これにより、デザイン実装をするための仮のインタフェース実装やStateのモック実装などをせずにすむようになり、KMPの実装漏れや認識齟齬もかなり減る結果が出ています。定量的に測ってはいないものの、開発効率が高くなった感覚がふたりの中にもあります。

ペアプロはふたりで1つのタスクに取り掛かるので、リソース効率としては悪くなるため開発効率が落ちるかもという懸念は一定ありました。ここは、各種手戻りの少なさやナレッジシェアの観点、マージまでのリードタイムなどの面からも、今のチーム構成にはマッチしているようです。

のびしろ

ドライバーのときの実装の言語化

ドライバーの役割になったときは、意識的に今考えていることや実装中のコードの意図などを話すようにしています。しかし、普段からやっていることではないため、まだまだふたりの共通理解ありきな部分もあると感じています。言語化することで、ナビゲーターにも伝わることはもちろん、自分の書いてるコードのWhyを意識することにもつながると思っているので、ここはペアプロを続ける中で慣れていきたい部分です。意外と難しい。

テンプレート的な実装の時間短縮

ペアプロでは、迷いポイントなどを秒のフィードバックとして解決できるのは良い面としてありますが、ボイラープレートコードを書いているときは少し時間がもったいなく感じてしまいました。ただ、これは良い気付きだなと感じていて、そういったところをうまく共通化する改善に繋げたり、IDEの機能を使ってスニペットやテンプレートとして用意することで、チーム全体の開発効率の改善にも繋げられそうです。

おわりに

今回はモバイルチームのペアプロのトライについて紹介しました。 今のチームのモバイルメンバーでは引き続きペアプロを続けていき、練度をあげていこう!と話をしています。

KyashのValue、「動いて風を知る」にあるように、モバイルチームでは日々さまざまなトライを行っています。他にも、トランクベース開発のトライも今年から行っているので、ペアプロによるリードタイムの削減はよりトランクベース開発がスムーズになる効果もありそうです。トランクベース開発については来週のアドベントカレンダーでも触れるので興味があればぜひ読んでみてください。