skip to content

What does the dependencies task show, and how do you read its output to spot an evicted (downgraded or upgraded) version?

level: juniorimportance: should knowfreq 50%

answer

  1. dependencies = full resolved tree per config
  2. scope with --configuration
  3. X -> Y means requested X, got Y
  4. (*) subtree omitted, (c) constraint
  5. arrows mark conflict resolutions

basics

~10 s

./gradlew dependencies prints the full resolved dependency tree per configuration. A line like 'lib:1.0 -> 2.0' shows that 1.0 was requested but the build resolved 2.0 — that request lost the conflict.

solid answer

~40 s

The `dependencies` task prints, for each configuration, the **resolved** dependency tree top-down. You usually scope it with `--configuration runtimeClasspath` to avoid a huge dump. Reading it: indentation is the tree depth; `+---`/`\---` are tree branches; `lib:1.0 -> 2.0` means *requested 1.0, resolved 2.0* (conflict resolution upgraded it); a trailing `(*)` means 'this subtree was already shown above, omitted here'; `(c)` marks a dependency constraint; `(n)` not resolved. The arrow notation is the key signal for conflicts — every `X -> Y` line is a module where highest-version-wins changed the version. For the *reason* behind a particular module's selection, switch to `dependencyInsight`. `dependencies` gives breadth (the whole graph); `dependencyInsight` gives depth (one module + why).

code

bash · 4 lines
bash
./gradlew dependencies --configuration runtimeClasspath
# +--- com.google.guava:guava:30.0-jre -> 31.0-jre   <- upgraded by conflict resolution
# +--- some.lib:foo:1.0
# |    \--- com.google.guava:guava:31.0-jre (*)        <- subtree already shown

go deeper

for a junior

Run the task with --configuration and recognise that arrows mean a version was changed by resolution.

for a middle

Decode (*), (c), (n) markers and know when to switch to dependencyInsight for reasons.

for a senior

Use it as the breadth-first scan before drilling, and read it across configurations for skew.

for a principal

Standardise these reports in CI/runbooks so resolution changes are reviewable.

## What the task does `./gradlew dependencies` reports the dependency graph for the project's configurations **after** resolution — i.e. it shows the versions Gradle actually selected, not just what you declared. Without arguments it prints every configuration, which is verbose; scope it: ```bash ./gradlew dependencies --configuration runtimeClasspath ``` ## Reading the tree - **Indentation + `+---` / `\---`**: the parent/child structure of the graph. Deeper indentation = transitive. - **`group:name:requested -> selected`**: a conflict was resolved. `com.google.guava:guava:30.0-jre -> 31.0-jre` means a node requested 30.0 but the build resolved 31.0 (highest-wins). Whenever you see an arrow, that module had multiple requested versions. - **`(*)`**: this module's subtree already appeared earlier in the report and is omitted to avoid repetition. - **`(c)`**: the entry is a **dependency constraint** rather than a real dependency edge. - **`(n)`**: declared but not resolved (e.g. a configuration not meant to be resolved). ## Spotting evictions Scan for `->` arrows. Each one is a place where the version you (or a transitive) asked for is **not** what shipped. Most arrows are harmless upgrades, but they're exactly where a surprising or breaking version can sneak in. After you find the arrow, run `dependencyInsight` on that module to see *who* requested each version and *why* the winner was chosen. ## Worked snippet ```text runtimeClasspath +--- com.google.guava:guava:30.0-jre -> 31.0-jre +--- some.lib:foo:1.0 | \--- com.google.guava:guava:31.0-jre (*) \--- other:bar:2.0 ``` Here your direct `guava:30.0-jre` was upgraded to `31.0-jre` because `foo` requested `31.0-jre`; the `(*)` shows guava's subtree was already printed. ## When to use which task - **`dependencies`**: 'show me everything' — overview, find arrows, see the shape. - **`dependencyInsight`**: 'explain this one module' — selection reasons + inverted requesters. Use them together: breadth first, then drill.

  • What does a trailing (*) mean in the dependencies output?
    That module's subtree was already printed earlier in the report, so Gradle omits it here to avoid repeating the whole branch.
  • If you need to know WHY a version was selected, which task do you use?
    dependencyInsight — the dependencies task shows the resolved tree and the X -> Y arrows but not the selection reasons; dependencyInsight adds the reason and the inverted requester list.

saying these in an interview costs you the question

  • Reading 'X -> Y' as 'X depends on Y' instead of 'X was resolved to Y'.
  • Running dependencies with no --configuration and getting lost in the dump.
  • Expecting selection reasons from dependencies (use dependencyInsight).

context