Enforcing Dependency Convergence with Maven Enforcer: A Practical Guide
When a multi‑module Maven build pulls in conflicting versions of the same library, tests can fail silently. This post explains how to use the Enforcer plugin to enforce dependency convergence, keep a single source of truth, and avoid subtle test failures.
24 Feb 2026, 19:59 UTC

Problem: Hidden Version Conflicts in Multi‑Module Builds
In a typical enterprise codebase, several modules depend on the same library but pull different transitive versions. The result is a “dependency hell” where unit tests pass but integration tests fail, or worse, the application behaves unpredictably in production. The root cause is that Maven silently resolves the first version it finds in the dependency tree, leaving other modules unaware of the mismatch.
Why Convergence Matters
Dependency convergence guarantees that every artifact in the build graph resolves to a single, deterministic version. When convergence is enforced:
- Builds become reproducible – the same set of artifacts is used every time.
- Test failures caused by version drift are caught early in the CI pipeline.
- Security patches are applied consistently across modules.
- The
<dependencyManagement>section can serve as the single source of truth.
Enforcing Convergence with the Enforcer Plugin
The Maven Enforcer plugin offers a declarative rule called dependencyConvergence. It scans the dependency tree before the package phase and fails the build if more than one version of the same artifact is present.
Typical POM Configuration
<project>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>enforce-convergence</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<dependencyConvergence />
</rules>
<failFast>true</failFast>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<!-- other central versions -->
</dependencies>
</dependencyManagement>
</project>
Key points:
- Place the plugin in the
<build>section so it runs on every build. - The rule is declarative – no code changes are needed.
- Use
<failFast>true</failFast>to stop the build immediately when a conflict is detected. - Version numbers in
<dependencyManagement>act as the canonical source; any module that omits a version inherits this one.
Running the Check
Execute the build with the Enforcer enabled:
mvn clean install -Denforcer.failOnMissingRule=true
If a conflict exists, the console will show:
FAIL: Dependency Convergence
com.fasterxml.jackson.core:jackson-databind:2.12.5
com.fasterxml.jackson.core:jackson-databind:2.15.2
The build will exit with a non‑zero status, preventing a faulty artifact from being deployed.
Worked Example: Two Modules Pulling Different Jackson Versions
Assume module-a declares Jackson 2.12.5 and module-b declares 2.15.2. Without enforcement, Maven picks the first encountered version (often the one in the parent POM), but the other module still pulls its own version, leading to two copies on the classpath.
- In the root
pom.xml, add thedependencyManagemententry for Jackson 2.15.2. - In each module, either omit the Jackson dependency (inherit from
dependencyManagement) or explicitly declare it with the same version. - Run
mvn -pl module-a,module-b clean install. - The Enforcer plugin will scan the combined tree, detect the two versions, and fail the build.
- Fix the module that declares the older version or add a
dependencyManagementoverride.
After correction, the build succeeds, and the resulting JAR contains only Jackson 2.15.2.
Trade‑offs and Limitations
- Build Time Overhead: The convergence check traverses the entire dependency graph. In very large projects, this can add a few seconds to the build.
- False Positives: If a project intentionally uses multiple versions of the same library (rare, but possible), the rule will flag it. Use
excludesorincludesto fine‑tune. - Plugin Version Compatibility: Ensure the Enforcer plugin version matches your Maven core version. Newer plugin releases may drop support for older Maven 3.x releases.
- Non‑Deterministic Resolution: Maven’s version resolution algorithm can still pick a different version if the
dependencyManagemententry is missing. The rule only checks what Maven resolves, not what you intended.
Actionable Checklist
- Add the Enforcer plugin to the root
pom.xmlwith thedependencyConvergencerule. - Centralize all version numbers in
<dependencyManagement>. - Run
mvn clean installlocally and verify no convergence failures. - Add the Enforcer execution to your CI pipeline so that every build is checked.
- If a conflict arises, review the modules that declare the conflicting artifact and align them to the central version.
- Document the rule in the project README so new contributors know the build will fail on divergence.
By integrating this simple configuration, teams can catch subtle version drift early, reduce the risk of runtime failures, and maintain a single source of truth for dependency versions.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.