Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Faster Feedback: Cut Gradle Build and Pipeline...

Avatar for James Cullimore James Cullimore
September 25, 2025
180

Faster Feedback: Cut Gradle Build and Pipeline Times in Half

Waiting on builds and pipelines wastes time and kills flow. In this talk, I’ll show how I cut both Gradle build times and CI pipeline durations by 50% on a production Android app, all without introducing new tools or rewriting existing code.

We’ll start with Gradle: I’ll break down which gradle.properties changes actually moved the needle, how updating to newer Java and Kotlin versions improved performance, and how a comparison between Groovy and Kotlin DSL surfaced potential future wins. You’ll get a practical look at what worked, what didn’t, and how to measure impact in your own project.

Then we’ll move to the CI pipeline: I’ll walk through how I restructured steps to increase parallel execution and reduce total pipeline time, not by adding complexity, but by thinking strategically about the build graph. These weren’t sweeping changes, just focused tweaks with big results.

If you’re looking for real-world ways to reduce build and pipeline friction, this talk will give you a toolkit of practical optimizations you can apply immediately.

Key Takeaways / Learning Points:
- Which gradle.properties settings directly impact build performance and why.
- How upgrading Java and Kotlin versions can unlock performance improvements with minimal effort.
- A comparison of Kotlin vs Groovy DSL for build scripts — and how Groovy may still offer speed advantages in some cases.
- How to redesign your CI pipeline to better utilize parallelism and minimize sequential slowdowns.
- A strategy for measuring and iterating on build/pipeline performance using simple metrics and real impact.

Avatar for James Cullimore

James Cullimore

September 25, 2025
Tweet

Transcript

  1. Why Faster Builds? • Faster feedback → quicker bug fixes

    and feature iteration. • 80% of local build time is idle waiting • Slow builds waste developer time (multiplied by many builds per day)
  2. The Hidden Cost • Each slow build wastes R&D bandwidth

    • Large teams perform thousands of builds/day, making this a multi-million-dollar productivity problem • Slow feedback delays releases and hurts morale.
  3. Update Tools • Update Android Studio / AGP / Gradle:

    Each release includes optimizations • Use latest Java (JDK 22+): Newer JVM often speeds up Gradle • Keep plugins up-to-date: Outdated Android/Java/Kotlin plugins can slow down builds
  4. gradle.properties Flags # Sets daemon memory, gc, and lint options

    for better stability and performance. org.gradle.jvmargs=-Xmx4g -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC -XX:MaxMetaspaceSize=512m -Dkotlin.daemon.jvm.options=-XX:MaxMetaspaceSize=1g -Dlint.nullness.ignore-deprecated=true # Reuses task outputs org.gradle.caching=true # Runs independent Gradle tasks in parallel org.gradle.parallel=true # Configures only necessary projects instead of all modules org.gradle.configureondemand=true # Shrink classpath size android.nonTransitiveRClass=true # Keep in mind, that some configurations are only beneficial in decoupled modules
  5. gradle.properties Flags # Enabled by default org.gradle.daemon=true # logs only

    essential info org.gradle.logging.level=info # For minimal output org.gradle.logging.level=quiet # Some performance tuning, e.g. cpu_cores/2=8 org.gradle.workers.max=8
  6. Gradle Myths • “Turning off org.gradle.parallel means no parallel work”

    ◦ Some tasks using Worker API still run in parallel. • “The Gradle Daemon does everything for me” ◦ There are multiple JVM processes (compiler daemons, test workers). • “Properties in gradle.properties flow through composite builds” ◦ Included builds don’t inherit root project’s properties automatically.
  7. Groovy vs Kotlin DSL • Groovy DSL is generally faster:

    Gradle 6.8 first-run: Groovy 33.5s vs Kotlin 76.2s • Kotlin DSL overhead: The Kotlin compiler adds initial delay • Use Kotlin DSL for better syntax/IDE support only if you need it, otherwise Groovy may yield faster config time.
  8. Optimize Repositories Order • Put main repos first: e.g. list

    mavenCentral() before others (like JitPack) to avoid extra network lookups • Minimize repository count: Use an aggregated/internal repo to limit remote calls • Gradle Plugin Portal: If you must include gradlePluginPortal(), list it last after mavenCentral()/google() to avoid extra searches
  9. Avoid Dynamic Versions • Fix version numbers: Avoid '+’ or

    '-SNAPSHOT' dependencies, as Gradle re-checks them often. • Cache dynamic versions longer: If you must use them, tune cacheDynamicVersionsFor / cacheChangingModulesFor so Gradle doesn’t hit the network on every build
  10. Use KSP Instead of KAPT • KSP is faster: The

    Kotlin Symbol Processing API is “significantly faster” than KAPT • Upgrade Room/other processors to KSP if possible.
  11. Limit Resources in Dev Builds • Filter unneeded resources: For

    debug/dev variants, restrict locales and densities (e.g. resourceConfigurations "en", "xxhdpi" ) • Reduces packaging time by skipping extra assets.
  12. Limit Resources in Dev Builds buildTypes { debug { packaging

    { resources { resourceConfigurations += listOf("en", "xxhdpi") } } } }
  13. Avoid Unnecessary and Unused Dependencies • Remove unused dependencies ◦

    https://github.com/autonomousapps/dependency-analysis-gradle-plugin • Limit unnecessary dependencies
  14. Performance & Debug SDKs • Performance/monitoring SDKs (Firebase Performance, LeakCanary,

    analytics) can increase build size & compile steps • Not always needed in dev/debug builds, activate selectively or use separate flavors • Ideal setup: QA or staging environment includes full monitoring; dev builds stay lean
  15. Hardware Matters, Stop Building on Fossils • Slow hardware =

    slow builds. Old CPUs, spinning HDDs, 4 GB RAM = bottlenecks • Time lost daily compounds: faster machines can pay for themselves • Do the math: ◦ saving 5 minutes per build × 10 builds/day = 50 minutes saved daily ◦ 20 days x 50 minutes = 16+ work hours per month ◦ €50 p/h x 16 hours = €800 of dev time per month
  16. Quick Wins on Local Builds Summary • Update tools (AGP,

    Gradle, JDK, Kotlin) and hardware • Enable Daemon, parallel, caching, config-on-demand • Compare Groovy vs Kotlin DSL • Use KSP (not KAPT) • Optimize dependency declarations & resources
  17. Why CI/CD Pipelines Also Matter? • CI builds cost time

    too, fast feedback on CI accelerates teams. • Even CI machines waste time (20% idle waiting). • Apply similar optimizations (and some CI-specific ones).
  18. Collapse Stages • Merge small stages: If a stage has

    only one job, combine it with adjacent stages to reduce overhead • Example: move a lone “pre-deploy” job into the same stage as deploy
  19. Parallelize Jobs • Run independent tasks in parallel: Use CI

    parallelism (e.g. parallel: N in GitLab) to split tests or lint jobs • Ensure tasks do not share state.
  20. Specify Build Variants and Modules • Too many build variants

    slow jobs • Specify variant per job (e.g. debug) • Exclude irrelevant modules (e.g. generated, phone/watch split)
  21. Slim Docker Images • Choose lightweight images: Use Alpine or

    slim variants in CI to reduce download and startup time • Smaller images = less time to pull and boot containers.
  22. Cache Dependencies & Artifacts • Cache package files: Store Gradle/Maven

    caches between builds (set cache:key, use pull/push) • Shared cache: Use shared cache across pipelines to speed all builds (push once, pull many). • Cache images/artifacts: Cache Docker layers or other build artifacts.
  23. Artifact Downloads • Only download what’s needed: By default, CI

    may pull all previous artifacts (slow!). Specify dependencies: so each job only downloads its needed artifacts.
  24. CI/CD Pipelines Summary • Parallelize independent jobs (tests, builds). •

    Merge trivial stages to reduce idle time. • Specify Build Variants and modules where necessary. • Cache dependencies/artifacts aggressively. • Use lightweight images and right-sized runners.
  25. Measure Baseline • Profile before changing: Use a build scan

    or profiler to record initial build times • Collect metrics: total time, slowest tasks, etc.
  26. Use Gradle Profiler • Automated profiling: The Gradle Profiler tool

    can run repeated builds and compare timings. • “Different things work for different projects”, don’t apply changes blindly • Use profiler to ensure each tweak actually helps.
  27. Track & Iterate • Baseline vs optimized: After each change,

    rerun profiler/scan and compare. • CI metrics: Monitor build time trends over commits (e.g. dashboard or badges). • Performance budget: Decide acceptable build-time limits for your project.
  28. Final Takeaways • Measure first: know your baseline (Build Scan/Profiler).

    • Start with easy wins: parallel, caching, tool updates. • Iterate & verify: use tools to see the real impact • Value developer time: even small speedups pay off big.