skip to content

Dependency Graph Resolution & Cycles

How Gradle turns the requested task names into an ordered DAG, how circular dependencies are detected and reported, and how --dry-run previews the plan. Asked when an interviewer wants to see you debug a build rather than rerun it hopefully.

on this pageshow

questions

6

How can you preview the ordered list of tasks Gradle will execute for a given command without actually running them?

level: juniorimportance: must knowfreq 60%

answer

  1. --dry-run / -m flag
  2. prints ordered graph, SKIPPED
  3. configuration still runs
  4. execution skipped
  5. preview reachability + order

basics

~10 s

Run the build with the --dry-run (-m) flag, e.g. gradle build --dry-run. Gradle resolves and prints the full task execution order with each task marked SKIPPED, but executes nothing.

solid answer

~40 s

Use the `--dry-run` flag (short form `-m`): `gradle build --dry-run`. Gradle performs the configuration phase, resolves the full task graph for the requested tasks, and then prints every task in the exact order it would run, each annotated `SKIPPED`. No task actions run, so no outputs are produced. It is the cheapest way to verify *which* tasks a command pulls in and in *what order*, which is invaluable when debugging unexpected work, surprising dependencies, or whether a task you wired in is actually reachable. Note `--dry-run` still runs configuration, so configuration-time side effects and the dependency-graph build still happen; only task *execution* is skipped.

code

bash · 4 lines
bash
# Preview the task graph for `build` without executing anything
gradle build --dry-run
# short alias
gradle build -m

go deeper

for a junior

Know the flag name (--dry-run / -m) and that it prints the ordered task list without running tasks.

for a middle

Explain that configuration still runs and only execution is skipped; use it to debug ordering and reachability.

for a senior

Contrast it with configuration-avoidance and the configuration cache; explain configuration-time side effects still fire.

for a principal

Position dry-run within a debugging toolkit (alongside --console=plain, build scans, --info) and CI sanity checks for graph drift.

## What `--dry-run` does A Gradle invocation has two main phases: **configuration** (evaluate the build scripts, register and configure tasks, build the task graph) and **execution** (run the selected tasks' actions in dependency order). `--dry-run` (alias `-m`) runs configuration and graph resolution normally, then **prints the ordered task graph and skips execution** — every task is reported with the `SKIPPED` outcome. ## Why it is useful - **Verify ordering**: confirm the exact sequence Gradle derived from your `dependsOn`, finalizers, and ordering rules. - **Detect reachability**: check whether a task you wired in is actually pulled in by your entry point. - **Cheap inspection**: no compilation, no artifacts, no slow work — only configuration cost. ## What it does NOT skip Because configuration still runs, anything done eagerly at configuration time (e.g. work in a task's configuration closure, or `tasks.create` eager realization) still executes. Only the task *actions* (the `doFirst`/`doLast`/`@TaskAction` bodies) are skipped. If you need to avoid configuration cost too, that is the job of task-configuration avoidance (`tasks.register`) and the configuration cache — separate concerns from `--dry-run`. ## Example output ``` > gradle build -m :compileJava SKIPPED :processResources SKIPPED :classes SKIPPED :jar SKIPPED :assemble SKIPPED :test SKIPPED :check SKIPPED :build SKIPPED ``` The order shown is the resolved execution order, honoring all dependency and ordering constraints.

  • Does --dry-run skip the configuration phase?
    No. It runs configuration and resolves the full task graph; it only skips task action execution. Configuration-time side effects still happen.
  • What outcome label does each task get in dry-run output?
    Each task is printed as SKIPPED, since its actions are not executed.

saying these in an interview costs you the question

  • Claiming --dry-run skips configuration / makes the build instant
  • Saying it produces build outputs (it produces none)

context

open as a page

What happens when two tasks depend on each other, and how does Gradle report it?

level: middleimportance: must knowfreq 55%

basics

~10 s

Gradle cannot order a cycle, so it fails the build with a 'Circular dependency between the following tasks' error listing the tasks in the cycle. It detects this during graph resolution, before executing anything.

open as a page

When you run `gradle test`, how does Gradle decide the complete set and order of tasks it executes?

level: middleimportance: must knowfreq 70%

basics

~10 s

Gradle starts from the requested tasks, transitively pulls in all their dependencies into a directed acyclic graph (DAG), then topologically sorts that graph so every task runs after the tasks it depends on.

open as a page

When you request multiple tasks like `gradle clean build`, how does Gradle build one combined graph, and what does `--exclude-task` (-x) do to it?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Gradle merges all requested tasks into a single DAG, deduplicates shared dependencies (each task runs once), and topologically sorts the whole thing. -x <task> removes that task and any edge into it from the plan, so it and its now-unneeded dependencies are excluded.

open as a page

There are two different 'dependency graphs' in Gradle. What is the difference between the task graph and the dependency (artifact) resolution graph?

level: seniorimportance: should knowfreq 35%

basics

~20 s

The task graph is the DAG of tasks Gradle will execute (e.g. compile before test). The dependency/artifact graph is the resolved tree of external modules/jars for a configuration (e.g. via dependencies). They are separate concepts solving separate problems.

open as a page

How can build logic make decisions based on whether a particular task will run, and why must this be done at the right phase?

level: seniorimportance: nice to knowfreq 25%

basics

~10 s

Use gradle.taskGraph.whenReady { graph -> } (or graph.hasTask(...)). It fires after the full task graph is resolved but before execution, so logic can branch on whether, say, a release task is in the plan.

open as a page