skip to content

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