skip to content

Custom outputs.upToDateWhen

The outputs.upToDateWhen and cacheIf predicates for custom staleness logic, including forcing a task to always execute. Interviewers use it to check that you can bend incrementality deliberately instead of by accident.

on this pageshow

questions

5

How do you make a Gradle task always run (never be considered up-to-date), and when would you do that?

level: juniorimportance: must knowfreq 62%

answer

  1. outputs.upToDateWhen { false }
  2. always out of date
  3. Spec<Task> predicate
  4. untrackable side effects
  5. escape hatch not default

basics

~20 s

Add outputs.upToDateWhen { false } to the task. The predicate always returns false, so Gradle never treats the task as up-to-date and runs it every build. Useful for tasks with side effects Gradle can't track.

solid answer

~40 s

Gradle skips a task when its declared inputs and outputs are unchanged. To force a task to always execute, register an up-to-date predicate that always returns false: ```kotlin tasks.named("deploy") { outputs.upToDateWhen { false } } ``` This tells Gradle the task is never up-to-date, so it runs on every invocation. You'd use it for tasks whose real effects Gradle cannot see — calling an external service, deploying, printing live status, or reading a value that changes outside the build (timestamps, remote state). Note it disables incremental skipping but the task can still be `cacheable` only if no `cacheIf` says otherwise; in practice you usually also want it not cached. Prefer declaring proper inputs/outputs when the effect *is* trackable — `upToDateWhen { false }` is the escape hatch, not the default.

code

kotlin · 5 lines
kotlin
tasks.register("deploy") {
    // Gradle can't see the remote server, so never skip this task
    outputs.upToDateWhen { false }
    doLast { println("deploying...") }
}

go deeper

for a junior

Recall the one-liner outputs.upToDateWhen { false } and that it forces the task to run every build.

for a middle

Explain WHY (untrackable side effects), that it's a Spec<Task> predicate, and that predicates are AND-ed.

for a senior

Contrast with declaring proper inputs/outputs, note it sacrifices incremental performance, and distinguish from cache behavior.

for a principal

Frame it as an escape hatch policy: prefer modeled inputs/outputs for cacheability across the team/CI; flag overuse as a build-performance smell.

## Up-to-date checking, briefly Gradle is an incremental build tool. Before running a task it computes a fingerprint of the task's **declared inputs** (files, properties) and **declared outputs**. If neither changed since the last successful run, the task is reported `UP-TO-DATE` and skipped. This is what makes repeated builds fast. ## The `upToDateWhen` escape hatch Sometimes a task has effects Gradle cannot model as files/properties — it deploys to a server, queries a remote API, or its result depends on the wall clock. For those, you override the decision with a predicate: ```kotlin tasks.register("reportStatus") { outputs.upToDateWhen { false } // always out of date -> always runs doLast { println("live status...") } } ``` `outputs.upToDateWhen { spec }` registers a `Spec<? super Task>`. Gradle considers the task up-to-date **only if** all registered predicates return `true` AND the input/output fingerprints match. Returning `false` short-circuits the whole thing, forcing execution. ## Important subtleties - It does **not** add `@Outputs`; a task with no outputs at all is also always out of date, but a predicate is the explicit, readable way. - Multiple `upToDateWhen` calls are **AND-ed** — any `false` wins. - The predicate receives the task instance, so you can make the decision conditional: `outputs.upToDateWhen { !project.hasProperty("force") }`. - This affects only the *up-to-date* decision. Build-cache reuse is governed separately by `outputs.cacheIf`/`@CacheableTask`. ## When NOT to use it If the effect *is* trackable, declare real inputs/outputs (`inputs.file`, `outputs.dir`) instead. Forcing a rerun throws away incremental performance and parallelism benefits, so reserve it for genuinely untrackable side effects.

  • Does `upToDateWhen { false }` also prevent build-cache reuse?
    Not directly. Up-to-date and cache are separate decisions. A task forced never-up-to-date will rerun, but if it's `@CacheableTask`/`cacheIf` it could still pull outputs from the cache. For a hard 'always do real work' task, also avoid caching it.
  • What if you register two `upToDateWhen` predicates?
    They're combined with logical AND. The task is up-to-date only if every predicate returns true (and fingerprints match); a single `false` forces a rerun.

saying these in an interview costs you the question

  • Claiming `upToDateWhen { false }` clears the build cache or deletes outputs — it only affects the up-to-date decision.
  • Using it as the default for ordinary file-producing tasks instead of declaring inputs/outputs.

context

open as a page

What is the difference between `outputs.cacheIf {}` and `outputs.upToDateWhen {}`?

level: middleimportance: must knowfreq 55%

basics

~10 s

upToDateWhen controls whether a task is skipped locally (incremental up-to-date check). cacheIf controls whether a task's outputs may be stored in / loaded from the build cache. They are independent decisions.

open as a page

Show how to write a conditional `outputs.upToDateWhen` predicate (not just `false`), and explain when the predicate is evaluated.

level: middleimportance: should knowfreq 38%

basics

~10 s

outputs.upToDateWhen { task -> someCondition } returns a boolean from a Spec<Task>. It's evaluated at execution time, during the up-to-date check, before the task's actions run.

open as a page

Compare `onlyIf {}` with `outputs.upToDateWhen {}`. When does each cause a task to be skipped, and how do they appear in the build output?

level: middleimportance: should knowfreq 33%

basics

~20 s

onlyIf { spec } decides whether the task runs at all — false means SKIPPED. outputs.upToDateWhen { spec } decides whether a task that would run is already up-to-date — true (with matching fingerprints) means UP-TO-DATE. Different decisions, different labels.

open as a page

A teammate added `outputs.upToDateWhen { false }` to a deploy task and the build is now slow and reruns it constantly even in CI cache scenarios. How do you reason about whether this is correct, and what alternatives exist?

level: seniorimportance: should knowfreq 30%

basics

~20 s

For a genuine deploy with untrackable remote side effects, upToDateWhen { false } is correct — deploys should always run. The fix isn't to remove it but to ensure the task is non-cacheable and isn't wired as a dependency of fast inner-loop tasks.

open as a page