Stopping Version Drift with Maven BOM Imports
Centralize library versions across a multi-module Maven build by importing a BOM in the parent POM, then verify the result with dependency:tree.
15 Aug 2025, 17:42 UTC

In a large multi-module Maven project, version drift is a silent killer. It starts when Module A uses Jackson 2.15.2 and Module B uses 2.17.0. Suddenly, you're hunting down NoSuchMethodError at runtime because the classpath loaded a different version of a transitive library than one of your modules expected.
The solution is not to manually copy-paste versions into every POM, but to use a Bill of Materials (BOM) within a <dependencyManagement> section. This allows you to centralize versioning in one place, ensuring every module in your project stays in sync without repeating version strings.
The power of dependencyManagement
To understand BOMs, you must distinguish between <dependencies> and <dependencyManagement>. The management section does not actually add libraries to your classpath. Instead, it acts as a lookup table. If a child module declares a dependency that is listed in the management section, Maven will automatically apply the version defined there. If the child declares a dependency not in the list, it must provide a version itself.
Implementing a BOM
A BOM is a special POM file that contains only a <dependencyManagement> section. Many popular frameworks, like Spring Boot or Jackson, provide a BOM specifically to ensure all their components work together. To use one, you use the <scope>import</scope> strategy. Here is how you configure your parent POM to import the Spring Boot BOM:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.2.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Once this is set in the parent, your child modules can declare Spring starters without any version tags. This guarantees they all use the exact same version of the framework:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!-- version resolved from the imported BOM --> </dependency>
Verifying the configuration
Centralizing versions is only useful if it is actually working. BOM imports need Maven 3.0 or later for reliable support, so start by running mvn -v anywhere and checking the Maven version line. Then confirm the merge with two commands:
- Check the resolved tree: from a module directory (or the reactor root with
-pl :module-name), runmvn dependency:tree. Look for the libraries in question and confirm they resolve to the versions the BOM defines. - Inspect the merged POM: run
mvn help:effective-pomin a child module. This prints the full XML after Maven has merged the parent and the BOM. Search for<dependencyManagement>to confirm the imported entries are present.
Trade-offs and limitations
While BOMs solve version drift, they can mask transitive version conflicts. A BOM pins the version of every artifact it manages, including ones that arrive transitively, so a library you expected to pull a newer version may quietly get the pinned one. If you manually declare a version in a child module that conflicts with the BOM, the child's local declaration wins, potentially re-introducing the drift you just removed. Also note that import scope is only meaningful inside <dependencyManagement>; placing it in <dependencies> does nothing. Finally, not every library provides a BOM. If you rely on many niche internal libraries, you may need to create your own internal BOM project — a POM dedicated solely to <dependencyManagement> — to manage your internal ecosystem.
To clean up your project, identify your most frequently used libraries, move their versions into a parent BOM's <dependencyManagement> section, and strip the <version> tags from your child modules. Future upgrades then become a single-line change in one file, and a quick mvn dependency:tree pass confirms nothing drifted along the way.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.