skip to content

Explicit dependsOn

Declaring dependsOn with names, task providers, or closures, and how those edges assemble the execution DAG alongside aggregate lifecycle tasks. Asked as the baseline before the interviewer moves on to implicit wiring.

on this pageshow

questions

5

What does the dependsOn declaration do for a Gradle task, and what is its effect on the build?

level: juniorimportance: must knowfreq 80%

answer

  1. must run before + must be included
  2. builds the execution DAG
  3. name / Task / TaskProvider / closure
  4. lifecycle tasks = empty + dependsOn edges
  5. topological sort

basics

~10 s

dependsOn declares that another task must run before this one. Gradle adds the named task to the build's task graph and executes it first whenever this task is requested.

solid answer

~40 s

`dependsOn` declares an explicit ordering relationship: when task A `dependsOn` task B, requesting A guarantees B runs first (and B is included in the graph even if not asked for directly). You can pass task names as strings (`dependsOn("compileJava")`), a `Task`/`TaskProvider` reference, or a collection/closure that resolves to tasks. Gradle uses these edges to build the execution DAG, then topologically sorts it so every dependency precedes its dependent. Crucially, `dependsOn` means "must run before" AND "must be included" — it is the core mechanism for chaining work. Lifecycle tasks like `build` are nothing more than empty tasks aggregating many `dependsOn` edges (e.g. `build` depends on `assemble` and `check`).

code

kotlin · 6 lines
kotlin
tasks.register("release") {
    group = "distribution"
    dependsOn("build")            // by name
    dependsOn(tasks.named("javadoc")) // lazy TaskProvider
}
// running :release runs build (and everything build depends on) first

go deeper

for a junior

State the two effects: the dependency runs first and is included in the build. Give a simple by-name example.

for a middle

Mention the DAG / topological sort and the various accepted argument types (name, Task, TaskProvider, collection).

for a senior

Connect dependsOn to lifecycle aggregate tasks (build/check/assemble) and contrast it with output-wiring; note laziness of TaskProvider.

for a principal

Frame dependsOn as the coarse-grained edge in the build's dependency model and discuss when to prefer it vs richer wiring across a multi-module build.

## What `dependsOn` is `dependsOn` is the primary way to declare an **explicit dependency** between Gradle tasks. Saying task `A.dependsOn(B)` asserts two things: 1. **Inclusion** — if `A` is in the task graph, `B` is pulled in too, even if nobody requested `B` directly. 2. **Ordering** — `B` must complete before `A` starts. ## How it shapes the execution DAG Gradle separates the build into two phases. During **configuration**, all `dependsOn` edges are recorded. Before execution, Gradle assembles a **directed acyclic graph (DAG)** of every requested task plus everything reachable through dependency edges, then performs a **topological sort** so each task runs only after all its dependencies. With parallel execution (`--parallel` / worker API), independent branches can run concurrently, but a `dependsOn` edge is always honored. ## Ways to declare it ```kotlin tasks.register("deploy") { dependsOn("build") // by name (String) dependsOn(tasks.named("test")) // by TaskProvider — lazy, preferred dependsOn(myJarTask) // by Task/TaskProvider reference dependsOn("a", "b", "c") // multiple at once } ``` You can also pass a `Provider`, a `Callable`/closure, or a `Collection` that resolves to tasks. Passing a `TaskProvider` (from `tasks.named(...)` / `tasks.register(...)`) is preferred because it stays **lazy** and doesn't force the dependency task to be configured eagerly. ## Lifecycle / aggregate tasks Many built-in tasks carry **no action of their own** — they exist purely to group other tasks via `dependsOn`. The Java plugin's `build` task `dependsOn` `assemble` and `check`; `check` `dependsOn` `test`; `assemble` `dependsOn` `jar`. Running `./gradlew build` therefore pulls in the whole chain. You create your own aggregate tasks the same way: register an empty task and add `dependsOn` edges to the real work. ## What `dependsOn` does NOT do It does not pass data between tasks, and it does not wire task *outputs* to *inputs* (that is inferred-dependency territory). It is a coarse "run this first" edge.

  • If you request only task A and A dependsOn B, does B run?
    Yes. dependsOn pulls B into the graph and runs it before A, even though you never asked for B directly.
  • Does dependsOn pass any data or files from B to A?
    No. It only enforces ordering and inclusion. Sharing data requires wiring B's outputs to A's inputs (inferred dependencies), not dependsOn.

Like a recipe step that says 'preheat the oven before baking' — the bake step won't start until the preheat finishes, and asking to bake automatically schedules the preheat.

saying these in an interview costs you the question

  • Saying dependsOn transfers outputs/data between tasks — it only orders and includes.
  • Claiming the dependency task runs only if explicitly requested — it is pulled in automatically.

context

open as a page

What are the different ways to specify the argument to dependsOn, and why is passing a TaskProvider preferred over a task name string?

level: middleimportance: must knowfreq 65%

basics

~20 s

You can pass a task name string, a Task or TaskProvider reference, a Provider, a Collection, or a closure. A TaskProvider is preferred because it is lazy — it doesn't force the other task to be created/configured eagerly.

open as a page

How do you declare multiple dependencies on a single task, and how would you build a custom lifecycle aggregate task using dependsOn?

level: middleimportance: should knowfreq 50%

basics

~20 s

Pass several arguments to one dependsOn call, call dependsOn multiple times, or pass a collection — dependencies accumulate. A custom aggregate is an empty task that dependsOn the real tasks, grouping them under one command.

open as a page

Walk through how Gradle turns dependsOn edges into the actual execution graph, including the phases involved and how the requested tasks expand into the full plan.

level: seniorimportance: should knowfreq 45%

basics

~10 s

After configuration, Gradle takes the requested tasks, transitively follows dependsOn edges to gather every needed task, builds a directed acyclic graph, topologically sorts it, then executes — running parallel branches concurrently when enabled.

open as a page

When is reaching for dependsOn the wrong tool, and what problems does over-relying on explicit dependsOn cause in a build?

level: seniorimportance: nice to knowfreq 35%

basics

~10 s

dependsOn only orders and includes tasks; it doesn't wire outputs to inputs. Over-using it for data flow leads to missing dependencies, broken incremental builds and cache misses, and a brittle hand-maintained graph.

open as a page