skip to content

You are introducing the build cache to a team and CI. How would you roll out enabling it so it is safe, consistent, and verifiable across developers and pipelines?

level: seniorimportance: should knowfreq 35%

answer

  1. committed gradle.properties = consistency
  2. avoid personal ~/.gradle / env drift
  3. verify with --info FROM-CACHE / --scan
  4. --no-build-cache as documented escape hatch
  5. local first, then remote/CI

basics

~10 s

Commit org.gradle.caching=true to gradle.properties so every developer and CI agent picks it up from version control, then verify with --info/build scans that tasks report FROM-CACHE, keeping --no-build-cache available as a per-run escape hatch.

solid answer

~50 s

I would make enabling **declarative and committed**: put `org.gradle.caching=true` in the project's `gradle.properties` so it travels with the repo and is identical for every developer and every CI agent — no per-command flags, no drift between machines. I would *not* rely on personal `~/.gradle/gradle.properties` or environment toggles, which silently diverge. For verification I would run with `--info` or a build scan and confirm tasks report `FROM-CACHE`, proving the switch is actually effective and not just declared. I would keep `--no-build-cache` documented as the escape hatch for clean/debug runs. Rollout is incremental: enable the switch first (local cache only), confirm hit rates and correctness, and only then layer on a remote backend and CI push — those backend concerns are configured separately in the `buildCache {}` block. The key principle: enablement is a committed default, overrides are explicit and per-run.

code

bash · 8 lines
bash
# Commit the switch
echo 'org.gradle.caching=true' >> gradle.properties

# Verify effectiveness on a second clean run
./gradlew clean test --info | grep -i 'FROM-CACHE'

# Or get a detailed breakdown
./gradlew clean test --scan

go deeper

for a junior

Recall that committing org.gradle.caching=true makes it consistent for everyone.

for a middle

Explain committed-property vs. personal/env drift and how to verify hits with --info.

for a senior

Lay out the staged rollout (commit switch, verify hits, escape hatch, then backends) and justify decoupling enablement from backend config.

for a principal

Treat it as a build-platform policy: committed defaults, observability via scans, governance of overrides, and phased backend adoption across many repos.

## The goal: consistent, observable enablement Rolling out the build cache is mostly a *consistency* problem. Every developer and every CI agent should make the same enable/disable decision, and you should be able to *prove* it is on. ## Step 1 — make enablement declarative and committed Put the switch in the **project** `gradle.properties`, checked into version control: ```properties org.gradle.caching=true ``` Why this and not alternatives: - **Personal `~/.gradle/gradle.properties`** enables it for one user only — invisible drift; teammates and CI stay off. - **Environment variables / `GRADLE_OPTS` system properties** are easy to forget on a new agent and are not discoverable in the repo. - **Per-command `--build-cache`** depends on everyone remembering the flag. A committed property is reproducible, discoverable in code review, and identical on every machine — the same value that CI reads. ## Step 2 — verify it is actually effective Enabling is necessary but not sufficient; you must observe hits. Run with `--info` and watch task outcomes: ```bash ./gradlew clean test --info | grep -i from-cache ``` A cacheable task should report `FROM-CACHE` on the second clean run. A **build scan** (`--scan`) gives a richer per-task cacheability breakdown. If you see no hits, enablement is fine but the tasks may be non-cacheable or inputs may be unstable — diagnose separately. ## Step 3 — keep an explicit escape hatch Document `--no-build-cache` for the cases where someone wants a from-scratch, cache-free run (release determinism check, suspected stale entry). Because the flag overrides the committed property per run, the standing default is never compromised. ## Step 4 — layer backends incrementally Enable the switch with just the default local cache first. Once correctness and local hit rates look good, configure remote/CI backends in the `buildCache {}` block of `settings.gradle.kts`. Decoupling *enablement* from *backend configuration* keeps the rollout low-risk: you are only flipping one well-understood switch at a time. ## Principle Enablement = a single committed default that every machine shares; overrides = explicit, per-run, non-persistent. That combination gives you consistency plus flexibility.

  • How do you prove the build cache is actually working after enabling it?
    Run a task twice with a clean in between using --info and confirm it reports FROM-CACHE on the second run, or inspect a build scan's cacheability breakdown. A green FROM-CACHE outcome proves real hits, not just a declared switch.
  • Why not enable caching only via ~/.gradle/gradle.properties or an env var on CI?
    Those are not committed, so they drift between machines and are invisible in code review. A new developer or fresh CI agent would silently have caching off, defeating consistency.
  • Why enable the local cache before adding a remote one?
    It isolates risk: you validate that tasks are cacheable and produce correct outputs with the simple local backend before introducing network, auth, and push/pull concerns from a remote cache.

saying these in an interview costs you the question

  • Enabling caching only through personal or environment settings and assuming the team is covered.
  • Declaring the switch but never verifying actual FROM-CACHE hits.
  • Rolling out remote caching and the switch simultaneously, making failures hard to attribute.

context