Kotlin build performance
Skill almasumdev/awesome-kotlin-android-agent-skills/.github/skills/performance/kotlin-build-performance
Expert guidance on making Kotlin Android builds fast — configuration cache, build cache, KSP vs KAPT, module graph, Gradle daemon tuning. Use this when builds are slow or CI times blow up.From its SKILL.md
npx -y skills add almasumdev/awesome-kotlin-android-agent-skills --skill kotlin-build-performanceAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
5.6 KB, ~1.4k tokens by cl100k_base, as published. Nobody here has run it
Kotlin Android Build Performance
Instructions
A slow build starves the feedback loop. Target: clean build under 60 s on dev laptops, incremental builds under 5 s.
1. Enable Gradle Caches (gradle.properties)
org.gradle.jvmargs=-Xmx4g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configuration-cache=true
org.gradle.configuration-cache.parallel=true
org.gradle.configureondemand=true
# Kotlin
kotlin.incremental=true
kotlin.code.style=official
# AGP
android.useAndroidX=true
android.nonTransitiveRClass=true
android.nonFinalResIds=true
android.defaults.buildfeatures.buildconfig=false
android.defaults.buildfeatures.resvalues=false
android.defaults.buildfeatures.shaders=false
Verify with:
./gradlew :app:assembleDebug --configuration-cache --build-cache
The second run should log "Reusing configuration cache" and most tasks FROM-CACHE.
2. KSP Instead of KAPT
KAPT stubs Java to run annotation processors — it's the single biggest Kotlin-build tax. KSP understands Kotlin directly and is 2–4× faster.
plugins { alias(libs.plugins.ksp) }
dependencies {
implementation(libs.hilt.android); ksp(libs.hilt.compiler) // Hilt 2.48+
implementation(libs.room.runtime); ksp(libs.room.compiler)
implementation(libs.moshi); ksp(libs.moshi.codegen) // if on Moshi
}
Remove kotlin("kapt") and kapt(...) dependencies. If a processor has no KSP support, isolate it in its own small module so KAPT doesn't drag down the whole graph.
3. Module Graph Hygiene
A bad module graph (one fat :app depending on everything) defeats caching. Aim for a diamond-free DAG:
:app
├─ :feature:articles
├─ :feature:profile
└─ :feature:settings
:feature:* → :core:designsystem, :core:ui, :core:data
:core:data → :core:network, :core:database, :core:domain
:core:* → :core:common
Rules:
- A feature depends on
:core:*, never on another feature. - Gradle builds sibling leaves in parallel; flat is fast.
- Run
./gradlew projectReportand./gradlew projectsto inspect. Use thecom.dropbox.focusorproject-reportplugin to visualize.
4. Convention Plugins
Put shared Android config in build-logic/ as precompiled script plugins:
// build-logic/convention/src/main/kotlin/app.android.library.gradle.kts
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
}
android {
compileSdk = 35
defaultConfig { minSdk = 24 }
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
kotlin {
jvmToolchain(17)
compilerOptions.freeCompilerArgs.add("-Xjvm-default=all")
}
Feature modules shrink to:
plugins { id("app.android.library"); id("app.android.hilt") }
This keeps configuration fast because Gradle loads one compiled plugin, not dozens of duplicated blocks.
5. Dependency Hygiene
- Always use
implementation; reach forapionly for types exposed in another module's public API. nonTransitiveRClass=true— each module gets only its own R class. Faster incremental compile, smaller generated code.- Avoid snapshot/plus versions (
1.2.+); they defeat the cache. - Lock dependency versions via the version catalog. Keep catalog churn low.
6. Compose Compiler Tuning
composeCompiler {
// enable strong skipping (Kotlin 2.0 + Compose compiler 1.5.14+)
enableStrongSkippingMode = true
// Add reports & metrics only on demand to avoid IO cost in CI
reportsDestination = layout.buildDirectory.dir("compose-reports")
}
7. Remote Build Cache
// settings.gradle.kts
buildCache {
local { isEnabled = true; directory = File(rootDir, ".gradle/build-cache") }
remote<HttpBuildCache> {
url = uri("https://your-cache.example.com/cache/")
isPush = System.getenv("CI") == "true"
credentials {
username = System.getenv("GRADLE_CACHE_USER")
password = System.getenv("GRADLE_CACHE_PASS")
}
}
}
Use a self-hosted Gradle Enterprise cache or develocity — the ROI is significant for teams > 3 developers.
8. Measuring
./gradlew :app:assembleDebug --scan
Publish a build scan and look at:
- Task breakdown — longest tasks.
- Avoided tasks — should be high on incremental.
- Configuration time — should be well under 1 s with config cache.
Use ./gradlew --profile for local HTML reports when a scan isn't wanted.
9. CI Specifics
- Use the official
actions/setup-javawith Temurin 17 +gradle/actions/setup-gradle@v4. - Enable dependency cache + configuration cache artifacts.
- Split CI into parallel jobs:
assemble,test,lint,detekt,ksp tasks. - Warm the remote cache nightly on
mainto reduce PR build times.
Checklist
-
configuration-cacheandbuild-cacheare on; second run reuses both. - No
kaptplugins remain (except where KSP isn't supported, isolated to one module). - Feature modules consume precompiled convention plugins, not duplicated
android { }blocks. -
nonTransitiveRClass = trueenabled. - No
+-style dependency versions; all versions live inlibs.versions.toml. - Compose strong skipping mode is enabled.
- A remote build cache is configured for CI.
- Regular
--scanreviews inform further optimization.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.