Aligning Multi-Module Versions with Maven dependencyManagement
Stop version drift in multi-module Maven projects. Learn how to use dependencyManagement to centralize versions, import BOMs, and verify resolved dependencies with mvn dependency:tree.
05 Oct 2025, 13:14 UTC

Solving Version Drift in Multi-Module Projects
In multi-module Maven projects, declaring library versions in every child module leads to "version drift," where different modules accidentally use different versions of the same library. This often results in jar hell—runtime errors like NoSuchMethodError or ClassNotFoundException because the classpath contains conflicting versions of a dependency.
The solution is the <dependencyManagement> section in a parent POM. This section acts as a centralized lookup table. It does not add libraries to the classpath itself; instead, it defines the version, scope, and exclusions to be used if and when a child module chooses to declare that dependency.
Implementing Centralized Version Control
To implement this, define the authoritative version in the parent POM and omit the version tag in the child POM. This ensures that changing a version in one place updates every module in the project.
Parent POM Configuration
The parent POM must have a packaging type of pom. The <dependencyManagement> block wraps the dependencies that you want to govern.
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>project-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
Child Module Configuration
The child module references the parent and declares the dependency without a version tag. Maven will look up the version from the parent's management section.
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>project-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>project-child</artifactId>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId;
<!-- Version is omitted; inherited from parent -->
</dependency>
</dependencies>
</project>
Importing External Versions via BOM
A Bill of Materials (BOM) is a POM file used to define a curated set of compatible versions for a large framework. Instead of listing every individual library in your parent POM, you can import a BOM using the import scope. This is the standard approach for frameworks like Spring Boot.
Add this inside the <dependencyManagement> section of your parent POM:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.2.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Verification and Diagnostics
Because <dependencyManagement> is an implicit mechanism, you must verify that the correct versions are being resolved during the build process.
Checking the Dependency Tree
Run the following command from the terminal in the child module's directory (or use -pl <module-name> from the root) to see the resolved versions:
mvn dependency:tree
Expected Result: The output should show org.slf4j:slf4j-api:jar:2.0.9:compile. If a different version appears, a transitive dependency or a local override is taking precedence.
Analyzing the Effective POM
To see the final merged configuration after all inheritance and imports are processed, run:
mvn help:effective-pom
This command outputs the complete XML that Maven actually uses for the build. Search for the artifactId to confirm the version is correctly injected into the <dependencies> section.
Common Pitfalls and Limitations
- The "Empty Classpath" Mistake: A common error is adding a dependency to
<dependencyManagement>and assuming it is now available in the project. It is not. You must still declare the dependency in the<dependencies>section of the child module to actually include the JAR in the build. - Child Overrides: If a child module explicitly defines a
<version>tag, it overrides the parent's management. This can lead to silent version drift if developers add local versions for quick fixes. - BOM Requirement: When importing a BOM, you must specify
<type>pom</type>and<scope>import</scope>. Omitting these will cause Maven to attempt to download the BOM as a standard JAR, resulting in a build failure. - Deep Inheritance: In projects with multiple levels of parent POMs, the version resolved is the one closest to the child in the hierarchy. This can make tracing the source of a version difficult without
help:effective-pom.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.