Maven Dependency Management vs Dependencies: Centralizing Versions to Stop Version Drift
When a Maven project grows into multiple modules, version drift can break builds. This blog explains how to use <code><dependencyManagement></code> to centralize dependency versions, override transitive dependencies, and keep your build clean and maintainable.
09 Dec 2025, 17:24 UTC

Problem: Version Drift in Multi‑Module Maven Builds
When a project grows into several Maven modules, each module often declares the same third‑party libraries. If every module hard‑codes a version tag, a small change in one module can silently introduce a breaking change in another. The result is a maintenance nightmare: you have to hunt down every <dependency> that references the same library and bump them all in lock‑step.
Even worse, transitive dependencies can pull in different versions of the same library from different paths in the dependency graph. Two modules may compile against commons‑lang3 3.9 while a shared library pulls in commons‑lang3 3.8, leading to class‑path clashes at runtime.
Thesis: Use <dependencyManagement> to Centralize Versions
The <dependencyManagement> section of a parent POM lets you define a single source of truth for the version and optional configuration of a dependency. Child modules can then reference the dependency without repeating the version, or even omit the dependency entirely if they only need a transitive one. This pattern eliminates version drift and gives you a clean way to override transitive versions.
How <dependencyManagement> Differs From <dependencies>
- Scope:
<dependencyManagement>only provides defaults. It does not add the jar to the classpath. The dependency still needs to appear in the module’s<dependencies>block. - Override: If a child module declares the same dependency with a different version, that version wins for that module only.
- Transitive Override: A dependency defined in
<dependencyManagement>can also override a transitive dependency that would otherwise bring in a conflicting version.
Concrete Example: Two‑Module Project
- Create the Parent POM
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>parent</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> <dependencyManagement> <dependencies> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> </dependency> </dependencies> </dependencyManagement> <modules> <module>module-a</module> <module>module-b</module> </modules> </project> - Module A – Declares the dependency without a version
<project ...> <parent> <groupId>com.example</groupId> <artifactId>parent</artifactId> <version>1.0.0-SNAPSHOT</version> </parent> <artifactId>module-a</artifactId> <dependencies> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <!-- No version tag – inherits from parent --> </dependency> </dependencies> </project> - Module B – Uses a different version, overriding the parent
<project ...> <parent> <groupId>com.example</groupId> <artifactId>parent</artifactId> <version>1.0.0-SNAPSHOT</version> </parent> <artifactId>module-b</artifactId> <dependencies> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.11.0</version> <!-- Explicitly override the parent definition --> </dependency> </dependencies> </project> - Verify with Maven
# Run from the parent directory mvn clean install # Inspect the dependency tree for module-a mvn -pl module-a dependency:tree # Expected output: org.apache.commons:commons-lang3:3.12.0 # Inspect the dependency tree for module-b mvn -pl module-b dependency:tree # Expected output: org.apache.commons:commons-lang3:3.11.0
In this setup, module‑a automatically inherits the 3.12.0 version from the parent, while module‑b intentionally uses an older 3.11.0. The parent POM remains the single source of truth for the majority of modules.
Transitive Dependency Override
Suppose a third‑party library com.example:lib-x pulls in commons-lang3 3.9 transitive. If you want every module to use 3.12.0, add a <dependencyManagement> entry for commons-lang3 and then declare the transitive dependency in the module’s <dependencies> block without a version. Maven will resolve the transitive dependency to the version defined in <dependencyManagement>, effectively overriding the library’s own version.
Trade‑offs and Limitations
- Coupling to a Single Parent: If many unrelated modules inherit from the same parent, a change to the parent can ripple across the entire codebase. Keep the parent focused on shared concerns (versions, plugins, properties) and avoid adding module‑specific configuration there.
- XML Placement Matters: The
<dependencyManagement>block must appear before the<modules>element. Placing it after can cause Maven to skip it during parsing, leading to unresolved dependencies. - Not a Classpath Addition: Defining a dependency in
<dependencyManagement>does not automatically bring it into the module’s classpath. The child must still list it in<dependencies>. Forgetting this step results in compile errors. - Large BOMs Can Be Overkill: BOMs that expose dozens of libraries can become hard to maintain. Use them sparingly and document the rationale for each entry.
Actionable Takeaway
When you start a new multi‑module project:
- Define a
<dependencyManagement>section in your parent POM for all shared third‑party libraries. - In child modules, omit the
versiontag or rely on the parent’s defaults. If a module needs a different version, declare it explicitly. - Use
mvn dependency:treeto confirm that the resolved versions match the parent’s definitions. - Consider creating a BOM POM if you want to expose a curated set of compatible library versions to downstream consumers.
By centralizing version control, you reduce the risk of subtle bugs, simplify upgrades, and keep your build files lean and maintainable.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.