What is @DisableCachingByDefault, when would you apply it to a task type, and how does it interact with @CacheableTask and cacheIf?
answer
- explicit not-cacheable + reason
- opposite of @CacheableTask
- Copy/Jar pack cost > work
- sets default; cacheIf overrides
- silences validation nag
basics
~10 s@DisableCachingByDefault marks a task type as intentionally not cacheable and records a reason, so it isn't a candidate for caching and validation won't nag about it. It's the explicit opposite of @CacheableTask.
solid answer
~40 s`@DisableCachingByDefault(because = "...")` is the annotation you put on a task **type** to say 'this task should not be cached by default, and that's deliberate.' It's used when caching wouldn't help or wouldn't be safe — e.g. tasks whose work is cheaper than packing/unpacking (`Copy`, `Jar`), tasks with non-file or non-reproducible outputs, or trivial lifecycle tasks. It suppresses validation warnings that would otherwise ask 'why isn't this cacheable?' and documents intent via the mandatory `because`. It's mutually exclusive with `@CacheableTask`. Note it sets the *default*: a specific instance can still be opted in with `outputs.cacheIf { ... }`, and conversely a `@CacheableTask` instance can be opted out with `doNotCacheIf`. So the annotations set type-level policy; `cacheIf`/`doNotCacheIf` are the per-instance overrides.
code
kotlin · 6 lines@DisableCachingByDefault(because = "Not worth caching: copy is cheaper than pack/unpack")
abstract class StageFiles : DefaultTask() {
@get:InputFiles abstract val from: ConfigurableFileCollection
@get:OutputDirectory abstract val into: DirectoryProperty
@TaskAction fun run() { /* copy */ }
}go deeper
Know it's the explicit 'not cacheable, on purpose' annotation, opposite of @CacheableTask.
Explain when to use it (pack cost > work, non-reproducible outputs) and the mandatory reason.
Reason about type default vs per-instance cacheIf/doNotCacheIf and clearing validation findings.
Set a convention for documenting cacheability across a plugin suite so every task type is intentionally cacheable or explicitly disabled.
## The three cacheability states of a task type A task *type* sits in one of three states: 1. **`@CacheableTask`** — opts the type into caching; you assert inputs/outputs are correct and reproducible. 2. **`@DisableCachingByDefault(because = "…")`** — explicitly *not* cacheable by default, with a documented reason. 3. **Neither** — the legacy/implicit non-cacheable state. Functionally not cached, but Gradle's validation may warn because intent is unstated. `@DisableCachingByDefault` exists to turn that third, ambiguous state into an explicit, self-documenting one. The `because` argument is required and shows up in diagnostics, so anyone reading the code knows *why* caching is off rather than wondering if it was an oversight. ## When to use it - **Pack/unpack costs exceed the work.** `Copy`, `Sync`, `Jar`, `Zip` — re-running is often as cheap as fetching and unpacking from the cache, so caching adds overhead. - **Non-reproducible or non-file outputs.** Tasks that print to the console, hit the network, or write timestamps can't be safely cached. - **Trivial/lifecycle tasks.** `Delete`, aggregator tasks with no real outputs. - **Authoring abstract base types** you don't want subclasses to inherit caching from implicitly. ## Interaction with per-instance overrides The type annotation sets the **default**; per-instance `outputs.cacheIf {}` / `doNotCacheIf {}` refine it: ```kotlin @DisableCachingByDefault(because = "Output is not reproducible across runs") abstract class DeployManifest : DefaultTask() { /* ... */ } // rare: force-enable a specific instance you know is safe tasks.named<DeployManifest>("manifestForCi") { outputs.cacheIf("inputs are pinned in CI") { true } } ``` Conversely, a `@CacheableTask` instance can be excluded with `doNotCacheIf("reason") { predicate }`. The resolution rule: an instance is cached only if the type/`cacheIf` says yes **and** no `doNotCacheIf` predicate says no. ## Relationship to validation Without the annotation, `validatePlugins` may flag a non-cacheable task as 'not cacheable without reason.' Adding `@DisableCachingByDefault` (or `@CacheableTask` with proper input annotations) resolves the finding. It's the recommended way to keep plugin validation clean while documenting cacheability decisions.
- Can a @DisableCachingByDefault task ever be cached?Yes — the annotation only sets the default. A specific instance can opt in via outputs.cacheIf { ... } if you know that instance is safe and worthwhile.
- Why is the 'because' parameter mandatory?To document intent. It appears in diagnostics so reviewers know caching was deliberately disabled with a rationale, rather than left off by accident.
- Why are Copy and Jar not cacheable by default?Their work is essentially I/O; storing/unpacking outputs from the cache is typically as expensive as just performing the copy/archive, so caching adds overhead without payoff.
saying these in an interview costs you the question
- Saying @DisableCachingByDefault makes caching impossible for every instance (cacheIf can still opt in).
- Applying @CacheableTask and @DisableCachingByDefault together — they're mutually exclusive.
- Marking expensive deterministic tasks as DisableCaching out of habit, losing real cache wins.