plugins/android-skills/skills/gradle-build-performance/SKILL.md
Use when Android/Gradle builds are slow — diagnosing bottlenecks with build scans, enabling configuration cache, migrating kapt to KSP, fixing cache misses, and optimizing CI/CD build times.
npx skillsauth add rcosteira79/android-skills gradle-build-performanceInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
./gradlew assembleDebug --scan# Generate a Build Scan (uploads to scans.gradle.com)
./gradlew assembleDebug --scan
# Local profile report (no upload)
./gradlew assembleDebug --profile
# Opens report in build/reports/profile/
In the Build Scan, go to Performance → Build timeline to identify which phase is slow.
| Phase | What Happens | Common Issues |
|-------|-------------|---------------|
| Initialization | settings.gradle.kts evaluated | Too many include() calls |
| Configuration | All build.gradle.kts files evaluated | Expensive plugins, eager task creation, I/O |
| Execution | Tasks run | Cache misses, non-incremental tasks, insufficient parallelism |
Skips the configuration phase on subsequent builds when inputs haven't changed (AGP 8.0+):
# gradle.properties
org.gradle.configuration-cache=true
org.gradle.configuration-cache.problems=warn # start with warn, move to fail once clean
Reuses task outputs from previous builds (including on CI):
# gradle.properties
org.gradle.caching=true
Builds independent modules simultaneously:
# gradle.properties
org.gradle.parallel=true
# gradle.properties
org.gradle.jvmargs=-Xmx4g -XX:+UseParallelGC
KSP is ~2× faster than kapt. Migrate when the library supports it (Hilt, Room, Moshi all support KSP):
// Before
kapt("com.google.dagger:hilt-compiler:2.51.1")
kapt("androidx.room:room-compiler:2.6.1")
// After
ksp("com.google.dagger:hilt-compiler:2.51.1")
ksp("androidx.room:room-compiler:2.6.1")
Also replace the kotlin-kapt plugin with com.google.devtools.ksp.
AGP 9 makes this mandatory. AGP 9 has built-in Kotlin support, and org.jetbrains.kotlin.kapt is incompatible with it. The path forward is KSP (requires KSP 2.3.1+ on AGP 9) or, for annotation processors with no KSP equivalent, the com.android.legacy-kapt plugin (same version as AGP) as a transitional fallback. See JetBrains' kotlin-tooling-agp9-migration skill for the broader AGP 9 migration mechanics.
Reduces R class size and recompilation scope (default in AGP 8.0+):
# gradle.properties
android.nonTransitiveRClass=true
Dynamic versions force resolution on every build:
// Bad — forces network check every build
implementation("com.example:lib:1.0.+")
// Good
implementation("com.example:lib:1.2.3")
Gradle checks repositories in order — put the most-used ones first:
// settings.gradle.kts
dependencyResolutionManagement {
repositories {
google() // Android artifacts
mavenCentral() // Everything else
// Third-party repos last
}
}
// Bad — eagerly configures and instantiates the task
tasks.create("generateVersion") { ... }
// Good — configured only if the task is in the execution graph
tasks.register("generateVersion") { ... }
File reads, network calls, and exec {} during configuration phase break the configuration cache and slow every build:
// Bad — reads file during configuration
val version = file("version.txt").readText()
// Good — defers read to execution
val version = providers.fileContents(layout.projectDirectory.file("version.txt")).asText
If you have a multi-repo setup, includeBuild is faster than publishing snapshots:
// settings.gradle.kts
includeBuild("../shared-library") {
dependencySubstitution {
substitute(module("com.example:shared")).using(project(":"))
}
}
subprojects { } and allprojects { } in the root build.gradle.kts eagerly evaluate all subprojects. Replace with Convention Plugins (see android-gradle-logic skill).
| Cause | Fix |
|-------|-----|
| Eager task creation | Use tasks.register() |
| File/network I/O in config | Defer to execution phase with providers |
| Many plugins applied unconditionally | Move to convention plugins; apply only where needed |
| subprojects {} / allprojects {} blocks | Replace with convention plugins |
| Cause | Fix |
|-------|-----|
| kapt annotation processing | Migrate to KSP |
| Build cache misses | Enable org.gradle.caching=true; check for non-deterministic task inputs |
| Sequential module builds | Enable org.gradle.parallel=true |
| Insufficient memory | Increase org.gradle.jvmargs heap |
| Cause | Fix |
|-------|-----|
| Dynamic versions (1.+) | Pin exact versions |
| Slow or unnecessary repositories | Reorder; remove unused repos |
| No local cache | Enable build cache |
gradle.properties Baseline# Performance
org.gradle.configuration-cache=true
org.gradle.caching=true
org.gradle.parallel=true
org.gradle.jvmargs=-Xmx4g -XX:+UseParallelGC
# Android
android.useAndroidX=true
android.nonTransitiveRClass=true
android.defaults.buildfeatures.buildconfig=false
android.defaults.buildfeatures.aidl=false
android.defaults.buildfeatures.renderscript=false
org.gradle.configuration-cache=true)org.gradle.caching=true)+ or -SNAPSHOT in production)register not create)development
Use when writing, fixing, or refactoring Android/KMP code in Kotlin — Android's three-tier test model, fake-first strategy, coroutine testing, and Compose UI testing, on a test-first (RED-GREEN-REFACTOR) foundation.
development
Use when debugging Android or KMP issues — Android-specific techniques covering Logcat, ADB, ANR traces, R8 stack trace decoding, memory leaks, Gradle build failures, and Compose recomposition bugs, on a root-cause-first foundation.
testing
Use when implementing paginated lists in Android or Compose with Paging 3 — PagingSource, Pager and PagingConfig setup, RemoteMediator for offline-first lists, LazyPagingItems and itemKey integration in LazyColumn, dynamic filters via flatMapLatest, and unit tests with TestPager and asSnapshot. Triggers include Paging 3, infinite list, infinite scroll, paginated list, LazyPagingItems, collectAsLazyPagingItems, and cachedIn.
development
Use when setting up or working with Koin in Android or KMP projects — module declarations with Classic DSL or KSP annotations, ViewModel injection in Compose, scopes, Nav 3 entry providers, application startup, and compile-time verification via `verify()`. Triggers on Koin, `single`, `factory`, `koinViewModel`, `koinInject`, `parametersOf`, `startKoin`, "KMP DI", "shared DI".