Android project で Gradle ファイルをどう書くかについて、現在の自分がどのような考えを持っているかつらつら書いてみる。
きっかけ
随分前の話だけど、以下の投稿から続く Gradle の機能採用に関する判断の考え方を読んで、そういえば自分も以前は同じようなスタンスだったことを思い出した。
Gradle 関連とかウォッチするものの割と採用しないことが多いですね https://t.co/sOly2D7w5h pic.twitter.com/lOR8XNx4K3
— wada811 (@wada811) September 11, 2024
それで自分の投稿を探してみると2022年頃にはこんなこと言っていた:
Android アプリ開発やってて gradle script まわりもそれなりに機能を理解しながら書けるようになってほしいという気持ちがあるが、一方 composite build を使って共通機能を plugin で書くようになって AGP API が露出することがあると流石に皆に AGP API の知識まで求めるのは酷では...? となる
— nash (@nashcft) February 20, 2022
実際これで、色々巡って結局 kts でない build.gradle で apply from でいいじゃんの気持ちだし、なるべく DSL を使うだけの記述で済ませたいと今は考えていますhttps://t.co/dAYylh7BQo
— nash (@nashcft) February 20, 2022
今はこの頃からスタンスが変わっており、以前以下のような投稿をしていた:
2年くらい前は build.gradle なんて素朴に書いたら良いじゃんと考えていたが最近はきちんと整備されてないとやってられんという気持ちになっている
— nash (@nashcft) May 1, 2024
現在はこの投稿時からもまた少し変わった、というか当時は仕事で Gradle まわりをの整備を集中的に行っている最中だったこともあって気持ちが強くなっていたのが、時間が経って落ち着いたものになったというか、そんな感じ。
この考え方の変化を思い出してなんだか面白いなと感じたので、ついでに自分のも Gradle に対する考え方の現在のスナップショットを書き出してみるかと思い立ちこれを書くことにした。トピックは折角なのできっかけとなった投稿に倣って「Gradle ファイルを Groovy で書くか Kotlin で書くか」「共通設定をまとめるのに何を使うか」「依存ライブラリの管理方法」の3つにした。なお、この記事の内容は業務で開発するものなど「他人と共有する・他人に引き継ぐ」ことが想定される複数人で開発する Android project を対象としている。個人開発ではみなさん好きにやっていくのがよろしい。
Groovy か Kotlin か
現在は基本的には Kotlin DSL で記述するし、既に Groovy で書かれているものも書き換える方に倒すようにしている。理由は単純で、 project 内で使用する言語のバリエーションを減らしたいからである。2024年時点において Android アプリはほぼ Kotlin で書かれていることが期待できるので、じゃあ Gradle ファイルも Kotlin で書けた方が (Android アプリ開発においては) そこでしか使われない Groovy を覚えるより楽だし、Android アプリ開発初心者や Groovy/Gradle に慣れ親しんでない人は細かい文法の差異で混乱することがなくなって嬉しいよね、という発想。
ただ、絶対に Kotlin DSL にするというほど気持ちがあるわけではない。私自身は Gradle ファイルが Groovy で書かれていて困ることはないので、例えば Kotlin DSL が出る前から開発されているアプリでチームメンバーも古くからいる人たちがずっと残ってて、 Groovy で書かれていることに課題を感じていないみたいな状況だったら Kotlin DSL に書き換えようとはしないと思う。 Groovy 触ったことないって人が加入したらどうしようかって話を始める感じ。
そこまで気持ちが強くないのは書き換えによる体験の差がそこまで大きくないから、という印象にもとづいているように感じる。静的型付きの言語で書くようになったからと言ってコード補完が速くなるわけでもないし*1、私がこれまで触れてきたアプリの規模や構成だとビルド実行時間がわかりやすく短くなるわけでもなかった、といったような実利面の弱さをこれまでの経験から所感として持っている。なので自分にとっての書き換えのモチベーションは、繰り返しになるがあまりビルドスクリプトに触れる機会のない人にとっても負担が少なくなるようにアプリと同じ言語で書けるようにしようというメンテナンスへの配慮が殆どとなる。
共通な設定のまとめ方 (script plugins, pre-compiled plugins, binary plugins)
真面目に multi-module 化をして、数十 module にはなるだろうなという見立てがある場合には build-logic module を作成して binary plugins で共通設定を記述する、というか本当にちょっとしたプロダクトでない限り仕事で開発するようなアプリはそのようになるだろうと考えているので基本 binary plugins 派になったと言えるか。「本当にちょっとした」の自分の中での基準は分割したところで十個も行かないくらいで、このような規模の小さい project の場合は script plugins で済ませてしまうかもしれないし、そもそも記述をまとめず重複を受け入れてしまうかもしれない。
Project の規模以外の判断基準だと build-logic module の用意や Gradle plugin の書き方を覚える (覚えてもらう) ための手間とビルドパフォーマンスのトレードオフかなーという感じ。とはいえビルドパフォーマンスの方が大事でしょというのと今は nowinandroid や DroidKaigi 公式アプリみたいな参考実装があるという点から迷ったら binary plugins を書く方に倒しがち。
きっかけで書いたように元々全部 script plugins でいいじゃんと考えてたところから変化したのは、おもに以下の記事・スライドから影響を受けたため:
- Herding Elephants https://developer.squareup.com/blog/herding-elephants/
- Stampeding Elephants https://developer.squareup.com/blog/stampeding-elephants/
- Gradle Convention Plugins - Speaker Deck https://speakerdeck.com/jmatsu/gradle-convention-plugins
依存プラグインやライブラリの定義・管理方法
これのスタンスに関しては自分はかなり適当で、文字列リテラルで何度も同じものを書く羽目にならなければ別に何でも構わないと思っている。他は強いて言うなら Renovate や Dependabot みたいなので更新を自動化できることくらいだろうか。
ここのところは Gradle が公式で出している仕組みだからということで version catalog を入れるようにしているが、どちらかというと type-safe project accessors は絶対使うのでそれと構文を合わせるために使っているという面が強い気がする。
Version catalog と言えば以前は呼び出し側からのコードジャンプで生成コードに飛ばされるなど不便に感じることもあったが、最近は IDE のサポートも充実しており特使用感で気になることは無くなってきた。もしくは単に慣れたか。
考え方の変化について
変化については大雑把にはふたつ理由があって、ひとつはここ2年ほどビルドスクリプトまわりを自由にさせてもらえている*2ことでそれなりの知識と経験を得られたのが大きかったと思う。これによって自分の中でのコントロール可能な要素が広がり、作り込み度合いの段階ややりすぎ感の出てくる程度の肌感を得ることができた。また、上で挙げた repository や資料といった参考として紹介できるものの把握や評価ができたのも、こちら側に倒して良いかと考える後押しになった。
もうひとつは今いる組織の性質的に新しい仕組みや機能を導入する傾向が強く、保守的な対応の方が穏便だと考えていても割と先進的な選択をとりがちな状況に置かれているという事情がある。結局のところ保守的な対応方針を取るにはそれ相応に古くからの深い知識や長い付き合い (ただし漫然としたものではない) による匙加減の把握といったものが必要で、それらがあって初めて必要最低限の対応や素朴な構成の維持が可能になると考えている。そういった Gradle 熟練者のいない、つまりはこれから Gradle について学んでいくことになる組織では、割と無邪気に新機能を放り込まれたり、かなり大味な設定変更で異様に先進的な設定になったりすることがままある (あった)。それなら昔ながらのやり方に押し込むよりは先回りして現代的な構成をとっていた方が変更に対するコントロールがしやすく、かつ他チームメンバーのメンテや習得のモチベーションを損なわずにやっていけるだろうという気持ちになり、じゃあもうどんどん先行していくかとガチャガチャやっている内に考え方もそっち寄りになったように思う。
こんな感じで割と現代的な機能を採用するのにポジティブな考え方になってはいるが、上の3トピックに対するスタンスでも書いているように絶対そうするというほどの気持ちの強さがあるわけではないのは、そうするように説得したり、既存のものを書き換えたりするほどの特別なメリットがあるとは自分自身は感じていないからだろう。なので、きっかけにある TimeTree のようにすでに体制があって、それで上手くやってきているところでは自分も態々コストかけて書き換えたりはしないだろうなあとは思う。自分は今のところ諸々含めて「これからやっていく」という文脈の中にいるので、じゃあ .kts
で書く方が Gradle に慣れていない人にもなじみやすいだろう、これから multi-module 化を本格的にやっていくなら下準備として build-logic module で binary plugins として共通部分を括り出しておくと module 増やすの楽だし将来パフォーマンス面も苦しくなりにくいだろう、とか、そういう判断をして良いのでそうしているという感じ。
おわりに
Gradle まわりの扱いについて、現時点での考え方と、以前の素朴に書いていれば良いという考え方からどのように変わったのかについて書いた。自分は他の人がどういうことを考えているのか知れるのが割と嬉しいのできっかけの投稿や記事も含めてこういう文章は好きなのだが、今回の場合何か主張があって書いたわけではないので全体的に取り留めのないものになってしまった。まあこういった話題には固定的な「答え」のようなものは無く、それぞれの状況があるばかりなのでそれに応じて上手くやっていきましょう、皆さんはどうですか? という気持ちだったのかもしれない。
9月末ぐらいにぼんやりと書き始めてから中々まとまらず結局年末ギリギリになってしまったが何とか2024年内に書き終えられてよかった。