🤖

DroidKaigiに現地参加してきた(2日目)

に公開

DroidKaigiに初現地参加しました
この記事では私が2日目のConference Dayに参加したセッションの内容をまとめました
特に続き物ではないですが、1日目が読みたい方はこちらからご覧いただけます
メモを取って整理した箇所、知らなかったけれど理解できた箇所が中心なので、私がすでに知っていたり、頑張って理解しようとしたけれど難しかった箇所は飛ばしてます
誤解してそうな箇所があれば、優しくコメントしてもらえると幸いです・・・
スライドは見つけたら掲載しています

Flutterからネイティブへの挑戦と学び - 評価1.6から4.0への道のり

最初はFlutterでアプリを開発、リリースまで行ったが、以下のような問題があってネイティブアプリに移行しました

  • WebViewが意図通りに動作しない(詳細な原因は不明だが、ネイティブで解消する可能性が十分にあった)
  • SDKを利用する処理はネイティブコードが必要なので、最初からネイティブで書いたほうが良さそう
  • Flutterとネイティブが両方できるエンジニアを見つけるのが困難だったので、ネイティブが書けるエンジニアに絞りたかった

移行した結果、不具合が多発してしまいました

不具合原因と分析

不具合と原因を大まかに分けると以下になります

不具合 原因
フォントサイズを変えると表示が崩れてしまう Figmaのサイズを固定値で実装していた
画面を横向きにするとUIが崩れてしまう デザインや実装時に横向きの考慮をしていなかった
エラー時にエラーと気付けない表示になる 異常系の実装が後回しになっていた箇所があった
クラッシュする APIレスポンスが期待通りの値でなかった、エラーハンドリングができてなかった(キャッチもできてない)
画面表示が遅い リコンポジションを考慮した実装になっていなかった
不要な処理が多かった(アプリ起動時に34回SharedPreferenceが動いていたことも)

これらを分析すると、以下のようになります

  • 表示系の不具合は、あるべきUI/UXを意識してデザインや表示を実装していなかった
  • API系はAPI開発にアプリエンジニアが一緒に参加できていなかった
  • 上位以外は、チームビルディングが機能しておらず、情報共有されるチームになっていなかった

これらを元に、品質改善していきます

品質改善

表示系

Flutterからネイティブ(Jetpack Compose)に移行したことで、Material3をフルに使えるようになりました
デザイナーにもこれらを使うよう協力してもらったところ、Figma上でも扱いやすいUIが作りやすくなりました
しかし、コストの高い独自UIは十分に議論しました
結果、開発生産性と品質が両立しやすくなりました

チームビルディング

今まで、アプリエンジニアがAPI開発に参加しなかったが、ドキュメント作成から参加するようになった
アプリ視点からのコメントが来る様になったので、API起因の不具合を抑制することができました

また、情報共有されるチームを作るためにフィードバックループを作りました
フィードバックループとは、自分がしている作業やタスクを他の人に見てもらってフィードバックをもらう仕組みです
今回のプロジェクトでは、AndroidとiOSが相互にフィードバックすることを軸に作っていきましたが、このようなフィードバックループをたくさん作ることが大事です
他にも以下のようなことをしました

  • タスクについて話すチャンネル
    • すぐに分かるような内容でもとにかく会話する
    • 作業の途中経過もアウトプットする
    • 会話する機会を増やすことで、会話しやすい環境になり、いろんな内容の会話が活発になる
  • 積極的にペア作業を行う

テストコードはもう書かない:JetBrains AI Assistantに委ねる非同期処理のテスト自動設計・生成

スライド

Androidでテストをする際の理想は、高いテストカバレッジや仕様変更への強さなどが挙げられます
ですが、現実はテスト対象に非同期処理が入ると、スレッドの差し替えや非同期の監視、依存のモックなど、お作法がいっぱいで時間が取れないです
これらをAIに任せるとどうなるのか?を検証してみました

重視する評価軸

評価軸 どんな評価
正確性 仕様に対して正しい実装か
網羅性 命令網羅、分岐網羅、重要パスがテストされているか
再現性 CIなどローカル以外での安定性は高いか
保守性 テストの意図が伝わるか、仕様変更時の追従コストが低いか
速度とコスト CI実行時間やレビュー時間が短いコードか

テストコードを書く対象

  • Roomのテスト(単純なCRUD処理)
  • WorkManager/CoroutineWorkerのテスト(API実行してその結果をRoomに保存)

書かせてみた結果

Roomのテスト

評価軸 簡単な評価
正確性 正常系は網羅されていて副作用も排除されているが、複数回のInsert処理など厳密な検証や例外は網羅出来ていない
網羅性 基本的なCRUD処理のテストは十分だったが、空のDBに対するテストなど堅牢性は不十分だった
再現性 基本的なCRUDの単発検証は十分だが、複雑な分岐や連続イベントなどは検証方法を変えたり別の方法で検証したほうが良さそう
保守性 一定水準は満たしているが、今後の仕様変更時のテスト追加で共通化した方が良い処理があった
速度とコスト 非常に効率的、共通化や効率化の工夫を取り入れられる箇所はありそうだった

全体的に、セットアップ→操作→検証という流れがわかりやすいテストコードになりました
また、基本的な正常系や定型パターンはAIにおまかせしてOKですが、それ以外は人がやったほうが良さそうな印象でした

WorkManager/CoroutineWorkerのテスト

このテストでは、以下のテスト支援ツールを使用しています

  • MockK
  • Robolectric
  • TestListenableWokerBuilder
評価軸 簡単な評価
正確性 主要な分岐は正確だが、テスト支援ツールの使い方を誤用していた
網羅性 基本的なテストの網羅は出来ているが、境界値、異常系、複数回実行や呼び出しなどが未検証だった
再現性 基本的な再現性は担保されているが、実装方法は見たほうが良いかも(誤用していたリスクを考慮)
保守性 十分だがWorkerBuilderの共通化やテストデータビルダー導入して更に保守性向上が見込めた
速度とコスト テスト実行は非常に高速だが、テストケース拡張時に工夫がいるかも

Roomのテストとほぼ同じで、全体的に、セットアップ→操作→検証という流れがわかりやすいテストコードになりました
また、基本的な正常系や定型パターンはAIにおまかせしてOKですが、それ以外は人がやったほうが良さそうな印象でした

AIにテストコードを書かせるのは良いか?

単純なケースや雛形はAIにまかせても良いが、テストコードで誤用した実装があったり、保守性の課題があったり、境界値や異常系のテストは苦手なので、人力で補完が必要な箇所がありました

Metro で学ぶ依存性注入のナビゲーション

ちょっと使ってみるのはありかと思った
セッション的にはMetroの思想と書き方がメインだったので、Metroを実際に使った方が理解早そうだと思ったので、試して別記事に書いてみようと思います

OAuthを正しく実装する:Androidアプリのためのセキュアな認証

モバイルアプリの認可のベストプラクティス

RFC 8252 OAut 2.0 for Native Appがあり、ネイティブアプリから認可はネイティブアプリ内ではなく、外部のブラウザを使うべきだと推奨されています
ネイティブアプリで認可することが良くないとされるのは以下になります

  • そのアプリのための認証情報だけでなく、別アプリの認証情報を読み取れてしまうため
  • Webの認証情報とアプリの認証情報が共有できず、UI体験が悪いため

トークンをどう保管するか?

暗号化した状態で保存するSharedPreferenceがあったが非推奨となっているため使用できない
すでに十分なセキュリティがあるので、SharedPreferenceで保存するのが良い
どうしても暗号化したい場合、encrypted-shared-preferencesがあります

認証情報の場合、Credential ManagerというGoogleが出しているライブラリがあるので、これを利用しましょう

Compose MultiplatformとSwiftUIで作るハイブリッドモバイルアプリ:コード共有とUI融合の実践

話している内容は、1日目の「共有と分離 ─ Compose Multiplatform “本番導入” の設計指針」と重複する箇所が多かったので、このセッションで追加で理解できたところを掻い摘んでいきます
共通項で印象に残ったのは、Koinみんな大好きだなと感じたことです

Flutterとの比較

CMPはクロスプラットフォーム開発できるが、Flutterもクロスプラットフォーム開発できる
UI周りで性能比較すると以下のような差がありました
なお、ネイティブが一番性能として良いものとして扱った場合の比較になります

実装 初期化 メモリ消費 UIのスムーズさ
ネイティブ 最高 最高 最高
CMP + ネイティブ 気になるくらいの重さ 気になるくらいの重さ 最高
Flutter + ネイティブ 悪くはないものの、明確に重さを感じる 悪くはないものの、明確に重さを感じる 複雑な画面だとカクつきが発生

処理の共通化

処理内容 実装箇所
Web周りのキャッシュ KMPで共通化
タイムゾーンやサマータイムなど 各ネイティブで実装
フォントやタイポグラフィ 各ネイティブで実装

各ネイティブで任せた理由は、OS間での処理や、デフォルトで設定されているものが異なっていたからだそうです

Be a Business-Driven Android Engineer

Business-Driven Engineerとは、ビジネスの成果にコミットし、技術を駆使してその実現に貢献するエンジニアです
このセッションでの造語(おそらく)なので、最初に説明しました

背景

これまでアプリエンジニアチームのKPIには技術的なことだったが、とあるタイミングでKPIに売上が追加されました
技術だけでなく、ビジネスにも踏み出す必要性が出てきたが、技術以外の領域の知識を習得するにはかなりのハードルの高さを感じます
そこで、領域の広げ方を工夫して行くことにしました

専門領域を広げるポイント

技術からビジネスの売上に領域を広げるのはかなり難しいので、まずは身近なところから領域を広げます
今回はアプリエンジニアチームはAndroidとiOSを広げていくことにした
そこで出てくるのがKMPになります

KMPによる効果

KMPを利用した実装でAndroidとiOSを同時に実装するようになりました
iOSでも動作するコードを書くので、Androidエンジニアは意識せずともiOS開発に貢献することが出来ました
これにより、いくつか効果が表れ、特に高い効果が得られたのは以下の事例です

  • 特定の機能の開発時、Webやバックエンドを含めて開発全般をリードすることになり、数億円/年の売上貢献につながった
  • 新規画面パーツを開発するか判断する際、表示パターンが多く影響範囲が大きいことがわかったので、機能実装の中止の合理的な判断、リソースの有効活用につながった
  • KMP導入により、新規実装でUseCase部分をKMPで実装していった結果、iOSの開発生産性に貢献した
カラビナテクノロジー デベロッパーブログ

Discussion