skip to content

Dependency Management

Declaring dependencies and controlling what the transitive graph resolves to: scopes, mediation, exclusions, dependencyManagement, and BOM imports. The densest area in Maven interviews, because version conflicts are the most common build failure.

on this pageshow

explore

questions

22

How do you inspect the full dependency graph of a Maven project, and what does dependency:tree show you?

level: juniorimportance: must knowfreq 80%

answer

  1. tree = resolved graph
  2. indent = who pulled it in
  3. -Dincludes filters branches
  4. -Dverbose shows omitted/conflicts
  5. group:artifact:type:version:scope

basics

~10 s

Run mvn dependency:tree. It prints every dependency your project pulls in, including transitive ones, as an indented tree showing which dependency brought in which, plus the scope and version of each.

solid answer

~40 s

`mvn dependency:tree` (the `tree` goal of the `maven-dependency-plugin`) renders the resolved dependency graph as an indented tree. Each node shows groupId:artifactId:type:version:scope, and indentation shows the parent that pulled it in transitively. It is the first tool you reach for to answer 'why is this jar on my classpath?' or 'which version actually won?'. You can scope output with `-Dincludes=groupId:artifactId` to show only the paths leading to a given artifact, and add `-Dverbose` to also see omitted/conflicting versions that mediation removed. It reflects the *resolved* graph after version mediation and scope rules, so it is the source of truth for what Maven actually puts on each classpath, not just what you declared in your pom.

code

bash · 1 line
bash
mvn dependency:tree -Dverbose -Dincludes=org.slf4j:slf4j-api

go deeper

for a junior

Knows to run mvn dependency:tree and read the indentation to find transitive deps.

for a middle

Uses -Dincludes and -Dverbose to isolate and debug a specific artifact and see conflicts.

for a senior

Treats the tree as authoritative resolved state and uses it to drive mediation/exclusion decisions.

for a principal

Standardizes graph-inspection workflows and teaches teams to debug classpath issues from the resolved graph rather than guessing from poms.

## What the dependency graph is Maven builds projects with declared dependencies, but each dependency can itself declare dependencies — these are **transitive dependencies**. The complete set forms a directed graph. Your classpath is determined by *resolving* that graph, applying version mediation and scope rules. ## dependency:tree `mvn dependency:tree` invokes the `tree` goal of the `maven-dependency-plugin`. It prints the resolved graph as an indented ASCII tree. The root is your project; children are direct dependencies; grandchildren are transitive ones. Each line has the coordinate form: `groupId:artifactId:type:version:scope` (e.g. `org.springframework:spring-core:jar:6.1.0:compile`). ## Key options - `-Dverbose` — also shows nodes that were *omitted* during resolution, annotated with reasons like `(omitted for conflict with 6.1.0)` or `(omitted for duplicate)`. This is how you see losing versions. - `-Dincludes=group:artifact` — filters to only the branches that lead to the matching artifact. Wildcards allowed, e.g. `-Dincludes=com.fasterxml.jackson.core:*`. - `-Dexcludes=...` — inverse filter. - `-DoutputType=dot` / `-DoutputFile=...` — emit a Graphviz/text file. ## Why it matters It answers the two most common dependency questions: *what is on my classpath* and *who pulled it in*. Because it shows the **resolved** graph (post-mediation, post-scope), it is authoritative — far more reliable than reading the pom by hand, which only shows what you declared, not what won. ```bash mvn dependency:tree -Dverbose -Dincludes=com.fasterxml.jackson.core:jackson-databind ``` This prints only the paths leading to jackson-databind and includes the conflicting versions that mediation discarded.

  • What does the -Dverbose flag add to the output?
    It shows dependency nodes that resolution omitted (e.g. losing versions in a conflict or duplicates), annotated with the reason, so you can see what mediation discarded.
  • Does dependency:tree show declared or resolved versions?
    Resolved — it reflects the graph after version mediation and scope filtering, which is why it's authoritative for what's actually on the classpath.

Like a family tree where each ancestor can bring relatives you never invited — the tree shows the whole bloodline and who introduced whom.

saying these in an interview costs you the question

  • Saying the tree shows only direct dependencies (it shows transitive too)
  • Claiming it reflects what you declared rather than what was resolved
  • Confusing -Dincludes (a filter) with adding a dependency

context

open as a page

What are the dependency scopes in Maven, and what is each one used for?

level: juniorimportance: must knowfreq 85%

basics

~20 s

Maven has six scopes: compile (default, everywhere), provided (compile/test only, supplied at runtime by the container), runtime (not for compiling, needed to run), test (only for tests), system (like provided but you give a local jar path), and import (only for BOMs in dependencyManagement).

open as a page

What are transitive dependencies in Maven, and where do they come from?

level: juniorimportance: must knowfreq 75%

basics

~10 s

Transitive dependencies are the dependencies of your dependencies. If you declare library A and A needs B, Maven automatically downloads B too, so you don't have to list it yourself.

open as a page

What does the <dependencyManagement> section in a pom.xml do, and how is it different from a plain <dependencies> section?

level: juniorimportance: must knowfreq 75%

basics

~20 s

<dependencyManagement> declares versions (and other settings) centrally but does NOT add the dependency to the build. Child modules that list the same dependency without a version inherit the managed version. Plain <dependencies> actually pulls the jar onto the classpath.

open as a page

What is the difference between the provided and runtime scopes, and when would you choose each?

level: middleimportance: must knowfreq 70%

basics

~20 s

provided is on the compile and test classpaths but NOT packaged or shipped, because something else (a container/JDK) supplies it at runtime. runtime is the opposite: NOT on the compile classpath but available (and packaged) for running and testing.

open as a page

How do you stop a specific transitive dependency from being pulled in, and when would you do that?

level: middleimportance: must knowfreq 80%

basics

~20 s

Add an <exclusions> block inside the direct dependency that drags it in, naming the unwanted groupId and artifactId. That prunes it (and its sub-tree) from the graph. Use it for clashing logging libs or vulnerable transitives.

open as a page

When two paths in the dependency graph demand different versions of the same artifact, how does Maven decide which version to use?

level: middleimportance: must knowfreq 85%

basics

~10 s

Maven uses nearest-wins: the version closest to your project in the dependency tree (fewest hops) is chosen. If two are at the same depth, the one declared first in the POM wins.

open as a page

How do you import a BOM in Maven, and what does it accomplish?

level: middleimportance: must knowfreq 70%

basics

~20 s

A BOM (Bill of Materials) is a POM that only declares versions in its <dependencyManagement>. You import it by adding it inside your own <dependencyManagement> with <type>pom</type> and <scope>import</scope>. Then you use its libraries without specifying versions.

open as a page

What does a -SNAPSHOT version mean in Maven, and how are SNAPSHOTs resolved and updated?

level: middleimportance: must knowfreq 65%

basics

~20 s

A -SNAPSHOT version (e.g. 1.2.0-SNAPSHOT) marks an in-development, mutable version. On a remote repo each deploy is stored with a unique timestamp, and Maven re-checks for newer SNAPSHOTs periodically (by default daily). Release versions are immutable.

open as a page

A class works locally but throws NoSuchMethodError in another environment. How do you use the dependency plugin goals to diagnose and fix the likely version conflict?

level: seniorimportance: must knowfreq 55%

basics

~20 s

A NoSuchMethodError usually means two versions of a library are in play. Run mvn dependency:tree -Dverbose -Dincludes=group:artifact to see which versions exist and who pulled them in, then fix it with an exclusion, a direct declaration, or dependencyManagement.

open as a page

For each Maven scope, on which classpaths (compile, test, runtime) is the dependency available?

level: juniorimportance: should knowfreq 55%

basics

~10 s

compile: all three (compile, test, runtime). provided: compile and test only. runtime: test and runtime only. test: test only. system: compile and test only (like provided). import: none of them (it only manages versions).

open as a page

What does dependency:analyze do, and how do you act on its 'used undeclared' and 'unused declared' warnings?

level: middleimportance: should knowfreq 50%

basics

~10 s

mvn dependency:analyze byte-code-scans your classes to compare what you actually use against what you declare. It flags 'used but undeclared' (relied on transitively — should declare directly) and 'declared but unused' (candidates to remove).

open as a page

What problem does dependency:go-offline solve, and when would you use it in a build pipeline?

level: middleimportance: should knowfreq 45%

basics

~20 s

mvn dependency:go-offline pre-downloads every dependency and plugin your build needs into the local .m2 repository, so a later build can run with -o (offline) without hitting the network — ideal for Docker layer caching and air-gapped CI.

open as a page

When would you use dependency:purge-local-repository, and how does it relate to SNAPSHOT and corrupted-cache problems?

level: seniorimportance: should knowfreq 35%

basics

~10 s

mvn dependency:purge-local-repository deletes this project's dependencies from the local .m2 cache and re-resolves them. You use it to recover from a corrupted/partial download or to force a stale SNAPSHOT to be re-fetched fresh.

open as a page

What does the import scope do, and how is it different from every other scope?

level: seniorimportance: should knowfreq 40%

basics

~20 s

import is not a classpath scope. Used only on a pom-typed dependency inside <dependencyManagement>, it merges another POM's <dependencyManagement> (a BOM) into yours, so you inherit managed versions without adding any jar to a classpath.

open as a page

How does Maven decide the effective scope of a transitive dependency, and why does compile -> provided become none?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Maven narrows a transitive dependency's scope based on the scope of the direct dependency that pulled it in. A transitive provided (or system) dependency is dropped, so compile -> provided becomes none and you must redeclare it if you need it.

open as a page

A NoSuchMethodError appears at runtime that you suspect is a dependency version conflict. Walk me through how you'd diagnose and fix it.

level: seniorimportance: should knowfreq 65%

basics

~10 s

Run mvn dependency:tree (with -Dverbose) to find which versions of the artifact are in the graph and which one mediation chose. Then force the correct version via dependencyManagement or a direct declaration, and re-verify.

open as a page

What does <optional>true</optional> do on a dependency, and how does it differ from an exclusion?

level: seniorimportance: should knowfreq 55%

basics

~10 s

<optional>true</optional> marks a dependency you use internally but that should NOT propagate to projects that depend on you. It still applies to your own build; it just stops Maven passing it transitively to consumers.

open as a page

When centralizing versions, what is the difference between using Maven <properties> for versions versus <dependencyManagement>, and how do they interact in a multi-module project?

level: seniorimportance: should knowfreq 40%

basics

~20 s

A <properties> entry like <guava.version>33.2.1</guava.version> is just a reusable string you reference with ${guava.version}; you still write the version everywhere. <dependencyManagement> sets a managed default so child modules can omit the version entirely. Teams often combine both.

open as a page

How do Maven version ranges work (e.g. [1.0,2.0)), and why are pinned versions usually preferred?

level: seniorimportance: should knowfreq 45%

basics

~20 s

A version range like [1.0,2.0) lets Maven pick any matching version: square brackets are inclusive, parentheses exclusive. So that means >=1.0 and <2.0. Most teams instead pin an exact version (e.g. 1.4.2) for reproducible, predictable builds.

open as a page

What is dependency:copy-dependencies for, and how is it different from building a fat/shaded jar?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

dependency:copy-dependencies copies your project's resolved dependency jars into a folder (default target/dependency). Unlike a fat/shaded jar, it leaves them as separate files — useful for a Class-Path/-cp layout or for Docker images that cache deps separately from app code.

open as a page

What is the system scope and <systemPath>, and why is it discouraged?

level: middleimportance: nice to knowfreq 25%

basics

~20 s

system scope behaves like provided but Maven does not search any repository; you must give an absolute path to the jar via <systemPath>. It is discouraged because it ties the build to a local file, breaks portability and reproducibility, and the jar is never packaged.

open as a page