こんにちは。ANDPAD の Android アプリを開発している平池です。
この記事は ANDPAD Advent Calendar 2024 の 7 日目の記事です。
ANDPAD はスマホ向けアプリ開発においてマルチアプリ戦略を取っており、主要な機能ごとにアプリを開発、リリースしています。
これらのアプリでは全ての機能がアプリごとに独立しているわけではなく、複数のアプリで共通の機能を使っています。例えばログイン/ログアウトや写真の撮影/選択などの機能は多くのアプリで同じです。
そのような共通機能を個別に開発するのは効率が悪いため、社内で SDK として切り出して開発、メンテナンスを行なっています。しかし、SDK として切り出すことで発生しがちな問題もあります。
この記事では社内 SDK を継続的に開発、運用していく上で発生した課題と、それに対する取り組みをまとめます。同じように社内 SDK のメンテナンスに困っている方や、これから機能の共通化に取り組もうとされている方の参考になりましたら幸いです。
ANDPAD での社内 SDK 開発の前提
前提として、社内 SDK を開発する際には、SDK ごとにリポジトリを作るか、複数の SDK を 1 つのリポジトリでまとめて管理するかの 2 パターンが多いかと思います。
どちらもメリット、デメリットはあると思いますが、私たちは以下のように考えています。
メリット | デメリット | 採用パターン | |
---|---|---|---|
SDK ごとにリポジトリを分割する | ・コミットや Pull Request、リリースノートが SDK ごとに完結する ・開発担当者(組織)を割り当てやすい |
・リポジトリごとに開発スタイルが分かれる ・横断的な修正に対応しづらい |
事業への影響度が大きく、今後も頻繁に機能追加される SDK の開発 |
複数の SDK を 1 つのリポジトリにまとめる | ・開発基盤(Linter や CI の設定など)を共有できる ・さまざまな情報を集約しやすい |
・情報が多いため整理が難しい ・担当が曖昧になりやすい |
比較的安定した小規模な SDK の開発 |
ANDPAD では上記のように SDK の規模に合わせて管理方法を分けています。この記事では主に「複数の SDK を 1 つのリポジトリにまとめる」方法で管理している SDK 開発の課題と改善について記載していきます。
「SDK ごとにリポジトリを分割する」方法では一部当てはまらない課題や取り組みをこれからご紹介しますがご了承ください。
社内 SDK 開発で直面した課題
1 つのリポジトリで複数の SDK 開発を行っていると、以下の課題に直面しました。
- 依存ライブラリのメンテナンス問題
- Pull Request 混在問題
- リリースノート混在問題
依存ライブラリのメンテナンス問題
SDK によっては継続的に開発されず、リリースしたらそのまま使い続けるケースもありますが、その場合でも依存ライブラリのメンテナンスは重要です。古いライブラリを参照し続けると、新しい OS への対応が難しくなったり、不具合の対応ができなかったり、場合によってはセキュリティ上の問題も発生します。
ANDPAD の社内 SDK リポジトリでは dependabot が各 SDK のライブラリを更新するように設定していましたが、SDK の数が多すぎるためか dependabot が更新を拾いきれないこともありました。
そのため、依存ライブラリを更新するときはすべての SDK の依存を網羅できているか確認したり、SDK ごとにライブラリの差分を確認する必要があり(SDK_1 は v1.1.1 から v1.2.0 に、SDK_2 は v0.9.0 から v1.2.0 になど)、ライブラリのメンテナンスコストが高いという問題が発生しました。
Pull Request 混在問題
1 つのリポジトリで複数の SDK を開発すると、各 SDK の Pull Request が集まるので一覧すると見づらくなる問題がありました。
ある Pull Request がどの SDK に関するものであるかを区別するために GitHub Actions で Pull Request のタイトルに SDK の名称をつけるという運用をしていましたが、気づかずに上書きしてしまったり、Pull Request 一覧のフィルターでうまく絞り込めなかったりして、情報の整理が難しくなっていました。
リリースノートの情報不十分問題
Pull Request が混在しているのでリリースノートを作成することも難しくなっていました。とりあえず差分をすべてリストアップして、リリースする SDK に関連する差分を目視で選別する作業が発生していました。
その結果、リリースノートの書き方が人によって異なってしまい、利用者が SDK を更新する際に必要な情報がまとまっていないこともありました。
また、各 SDK のリリースノートが 1 つのリポジトリに溜まっていくので、利用したい SDK の最新バージョンを探すことも大変でした。
このような状況を打破するために、いくつかの改善に取り組みました。
社内 SDK を誰でも継続的に開発できるように取り組んだこと
社内 SDK はさまざまなチームの開発者が開発・利用するので、「誰でも」「継続的に」開発できるように、大きく 3 つ改善しました。
version catalog の導入
まず「依存ライブラリのメンテナンス問題」を解消するために、version catalog の導入を行いました。version catalog を導入することで各 SDK が利用しているライブラリとそのバージョンを管理できるので、ライブラリ更新時の手間を削減できます。
その際に、SDK 自体のバージョンも version catalog に定義することで、各 SDK の最新バージョンを確認しやすくしました。
# libs.versions.toml [versions] sdk_1 = "0.1.2" sdk_2 = "1.0.1" sdk_3 = "2.0.0"
これで「リリースノートの情報不十分問題」における各 SDK の最新バージョンがわかりづらい問題も解決されました。
(可能なら GitHub のリリースノートからデータを取って README などにそれぞれの SDK の最新バージョンを表示したかったのですが、現状では 1 つのリポジトリでは 1 つの最新バージョンしか取得できないようだったので諦めました...。)
Pull Request をラベルで管理する
次に「Pull Request 混在問題」を解消するために、Pull Request をラベルで管理するように変更しました。ラベルで管理することで GitHub の Pull Request 一覧で簡単にフィルタリングもできます。
しかしここで問題なのが、dependabot が出すライブラリ更新の Pull Request に対してどのようにラベルをつけるかです。version catalog を導入したことで、更新するライブラリがどの SDK で使われているのかを探すのが難しくなりました。
最終的には GitHub Actions で以下のようなスクリプトを実行することで、利用している SDK を洗い出して Pull Request に機械的にラベルをつけるようにしました。
# set_label_to_dependabot_pr.yml name: Set label to Dependabot's Pull Request on: pull_request: jobs: set_label_to_dependabot_pr: timeout-minutes: 3 runs-on: ubuntu-22.04-2cores-x64 permissions: write-all env: IS_DEPENDABOT: ${{ contains(github.head_ref, 'dependabot') }} GITHUB_TOKEN: ${{ github.token }} PR_NUMBER: ${{ github.event.number }} GH_REPO: ${{ github.repository }} steps: # version catalog の変更差分を取得する - uses: tj-actions/changed-files@v45 if: ${{ env.IS_DEPENDABOT == 'true' }} id: changed-gradle-files with: files: | gradle/libs.versions.toml # version catalog で変更されていたライブラリを参照している SDK を探してラベルをつける - name: Identify updated libraries and set SDK label to PR if: ${{ steps.changed-gradle-files.outputs.any_changed == 'true' }} id: identify-updated-libraries run: | # version catalog で更新されたライブラリの version 定義名を特定(libraryA = "2.2.0" の libraryA の部分) UPDATED_LIBS_VERSION_NAMES=$(git diff HEAD^ HEAD --unified=0 gradle/libs.versions.toml | grep -oE '^\+[a-zA-Z0-9]+' | sed 's/+//') SDKS="" for VERSION in $UPDATED_LIBS_VERSION_NAMES; do # version を参照している libraries の名称を特定(androidx-libraryA-ui = { group = "androidx.libraryA", name = "libraryA-ui", version.ref = "libraryA" } から androidx.libraryA.ui を取得) UPDATED_LIBRARY_NAMES=$(grep "version\.ref = \"$VERSION\"" gradle/libs.versions.toml | grep -o "^[^ ]*" | sed "s/-/./g") for UPDATED_LIBRARY_NAME in $UPDATED_LIBRARY_NAMES; do # 各 SDK の build.gradle.kts ファイルから androidx.libraryA.ui を参照しているファイルを探して SDKS に名称を保存(andpad-sdk1/build.gradle.kts から sdk1 を保存) SDKS+=$(grep -rlE $UPDATED_LIBRARY_NAME **/build.gradle.kts | sed -r 's/andpad-([^\/]*)\/.*/\1/') SDKS+=$'\n' done done # SDK の重複を排除 SORTED_SDKS=$(echo "$SDKS" | sort | uniq) # SDK の名称のラベルを Pull Request につける for SDK in $SORTED_SDKS; do gh pr edit "$PR_NUMBER" --add-label "$SDK" done # もし SDK が該当せず Root の build.gradle.kts だけが更新されていた場合は "all" ラベルをつける if [ "$SDKS" == "" ]; then gh pr edit "$PR_NUMBER" --add-label "all" fi
これですべての Pull Request にラベルがつくようになり、「Pull Request 混在問題」が解消されました。
リリースノートのテンプレートを整備
最後に「リリースノートの情報不十分問題」を解消するためにリリースノートのテンプレートを整備しました。テンプレートは GitHub の自動生成機能を使います。
すでに Pull Request にはラベルが付いている状態なので、以下のようなテンプレートを設定して自動的に SDK ごとに分類されたリリースノートが作られるようにしました。
changelog: categories: - title: Features on All 🚀 labels: - all exclude: labels: - dependencies - title: Features on sdk1 labels: - sdk1 exclude: labels: - dependencies - title: Features on sdk2 labels: - sdk2 exclude: labels: - dependencies - title: Dependencies 📦 labels: - dependencies
リリース時には上記のテンプレートで自動生成されたリリースノートから、リリースしない SDK の項目を削除すれば OK です。すべての差分を確認していたころよりは楽になりました。
可能であれば Dependencies も各 SDK の欄に記載したかったのですが、androidx.core や compose などは複数の SDK で利用しているため複数のラベルがついており、上記テンプレートでは先に書いてある SDK の項目だけに差分が表示されてしまう問題がありました。そのため、最後に Dependencies としてまとめています。
リリースノートのテンプレートを導入することで誰でも同じ書式のリリースノートを作成できるようになり、「リリースノートの情報不十分問題」も改善されました。
今後やっていきたいこと
いくつかの対応をしましたが、まだ改善の余地はあると考えています。今後やっていきたいことの一例を記載します。
自動化を取り入れたい
まだ上記の取り組みをして様子を見ている段階なので、運用が安定してきたら以下のような自動化にも取り組んでいきたいです。
- リリースノートを公開したら、バージョンの名前が version catalog に定義されている sdk のバージョン名に反映された Pull Request が作成される
- リリースノートを公開したら、リリースされた sdk を利用しているアプリの開発者に通知する
SDK ごとのリリースノートをまとめたい
リリースノートの情報は整備されましたが、特定の SDK のリリースノートが探しづらい問題は残っています。GitHub のリリースノートの検索機能では不十分で、タグには検索機能が存在しません。
対応として、SDK ごとの Issue を用意しておいて、リリースノートが公開されたらそのリンクを SDK ごとに Issue に追記するなどが考えられます。他にも良い方法があれば試していきたいです。
リポジトリの垣根を超えてライブラリのバージョンを管理したい
SDK 開発をしていると、利用アプリと SDK でライブラリのバージョンを揃えてリリースしなければならないこともあります。一部の compose のバージョンでは互換性が無く、バージョンが異なることでアプリがクラッシュしてしまうケースもあります。
そのため、社内全体で共有する version catalog のようなものを用意して、各アプリと SDK からはその version catalog を参照すればリスクを下げられるのではないかと考えています。ただ、そうするとバージョン更新時にすべてのアプリと SDK の動作確認を待たなければならず、開発速度は落ちてしまうので悩ましいところです。
おわりに
この記事では社内 SDK の管理方法と課題、取り組みと今後やっていきたいことをまとめました。同じく社内で SDK を開発されている方の何かしらのお役に立てれば幸いです。
弊社では Android エンジニアを引き続き募集しております。興味を持っていただけましたら、ぜひカジュアル面談にご応募ください。