What does outputs.upToDateWhen { false } do, and what are the trade-offs of putting it in a build script?
answer
- predicate; any false ⇒ out of date
- { false } ⇒ always runs
- permanent vs one-shot CLI
- for non-snapshotable external state
- cache (cacheIf) is separate from up-to-date
basics
~10 sIt makes a task's up-to-date check always fail, so the task re-executes on every build instead of being skipped as UP-TO-DATE. It's a programmatic, permanent way to force rerun (unlike the one-shot CLI flags).
solid answer
~50 s`outputs.upToDateWhen { ... }` registers a predicate Gradle evaluates during up-to-date checking; if any registered predicate returns `false`, the task is considered out of date and runs. Passing `{ false }` therefore forces the task to **always** execute — it's the build-script equivalent of permanently applying `--rerun` to that one task. Common reasons: a task interacts with an external system whose state Gradle can't snapshot (network call, timestamp, current git state), so its 'correct' result can't be captured by file inputs/outputs. The trade-off is clear: that task can never be skipped, so it costs time on every build and breaks the incremental model for that node. Prefer it over CLI flags only when re-execution must be *unconditional*; otherwise declare proper inputs/outputs so normal up-to-date checking works. Note it disables up-to-date but a cacheable task could still be served `FROM-CACHE` unless caching is also disabled.
code
kotlin · 6 linestasks.named("fetchLiveData") {
// Always out of date: external source can't be snapshotted as inputs
outputs.upToDateWhen { false }
// If also cacheable, prevent FROM-CACHE restoring stale outputs:
outputs.cacheIf { false }
}go deeper
Know it forces the task to run on every build by failing the up-to-date check.
Explain the predicate semantics (any false ⇒ out of date) and the permanent-vs-CLI distinction.
Note up-to-date and cache are orthogonal; write a real predicate instead of { false } when possible; justify by non-snapshotable external state.
Weigh the build-time/cache cost of an always-out-of-date node in large graphs and govern its use; push toward correct input/output modeling.
## The up-to-date predicate API Every task has a `TaskOutputs` object (`task.outputs`) with an `upToDateWhen(Spec<? super Task>)` method. You register a predicate: ```kotlin tasks.named("generateReport") { outputs.upToDateWhen { false } } ``` During up-to-date checking, Gradle evaluates **all** registered predicates. If **any** returns `false`, the task is out of date and executes. `{ false }` is the degenerate case: it always reports out-of-date, so the task runs every single build. ## Why force it programmatically vs. a CLI flag The CLI flags (`--rerun`, `--rerun-tasks`) are **one-shot**: they force a rerun for a single invocation. `outputs.upToDateWhen { false }` is **permanent and declarative** — it lives in the build and applies to every build automatically. You reach for it when re-execution is a *requirement of the task*, not a debugging act. Classic example: a task that publishes to or queries an external system Gradle cannot snapshot, so file-based up-to-date checking can never be correct. ## A smarter predicate than `{ false }` You don't have to give up incrementality entirely. The predicate receives the task, so you can encode real out-of-date conditions: ```kotlin tasks.named<Exec>("deployIfChanged") { outputs.upToDateWhen { // only rerun when a marker is missing/stale !file("$buildDir/.deployed").exists() } } ``` ## Important interactions - **Up-to-date vs. cache are separate.** `upToDateWhen { false }` defeats the up-to-date skip but, for a `@CacheableTask`, Gradle could still restore outputs `FROM-CACHE`. To truly force work you may also need to disable caching for that task (e.g. `outputs.cacheIf { false }` or not marking it cacheable). - **onlyIf is still independent.** If `onlyIf` returns false the task is skipped regardless. - **Cost.** The task never benefits from incremental builds again; in large graphs an always-out-of-date task can bottleneck every build and reduce overall cache effectiveness. ## Rule of thumb Reach for `{ false }` only when a task is genuinely non-deterministic or externally driven. For everything else, **declare correct inputs and outputs** so Gradle's normal up-to-date checking does the right thing for free.
- If multiple upToDateWhen predicates are registered, when is the task up to date?Only when ALL registered predicates return true (and normal input/output checks pass). Any predicate returning false marks the task out of date.
- Does outputs.upToDateWhen { false } also stop a cacheable task from being served FROM-CACHE?No. Up-to-date and build cache are independent. A @CacheableTask may still restore outputs from cache; disable caching (e.g. outputs.cacheIf { false }) to force real work.
- When is { false } the wrong choice?When the task's results are actually a function of files/properties — declare proper inputs/outputs instead so it stays incremental rather than running every build.
saying these in an interview costs you the question
- Believing { false } also disables the build cache for the task (it doesn't).
- Using it as a default workaround instead of declaring inputs/outputs.
- Thinking it only affects one invocation like the CLI flags (it's permanent).