What does the dependencyConvergence rule do, and how does it differ from requireUpperBoundDeps?
answer
- nearest-wins can downgrade
- convergence = one version everywhere
- upperBound = catch downgrades only
- fix via dependencyManagement pin
- report shows conflicting paths
basics
~20 sdependencyConvergence fails the build if a transitive dependency is pulled in at more than one version anywhere in the tree. requireUpperBoundDeps is gentler: it fails only when Maven resolves a version lower than the highest one requested.
solid answer
~40 s`dependencyConvergence` requires that every artifact resolves to a *single* version across the whole dependency graph; if two paths bring `guava:30` and `guava:32`, the build fails and the report shows both paths so you can add a `dependencyManagement` entry. It is strict and noisy on large trees. `requireUpperBoundDeps` is the pragmatic alternative: Maven's mediation uses *nearest-wins*, which can silently pick an *older* version than some path requires; this rule fails only when the resolved version is *below* the highest requested version — i.e. when you risk a downgrade and possible `NoSuchMethodError`. In short: convergence demands one version everywhere; upperBound only catches dangerous downgrades. Many teams enable `requireUpperBoundDeps` for safety and reserve full `dependencyConvergence` for libraries where strict hygiene matters.
code
xml · 4 lines<rules>
<dependencyConvergence/>
<requireUpperBoundDeps/>
</rules>go deeper
Knows both rules guard against version conflicts in transitive dependencies.
Can read the convergence report and pin a version via dependencyManagement.
Explains nearest-wins downgrades and chooses upperBound vs full convergence per project.
Sets a graded dependency-hygiene policy across the org and weighs convergence noise against build maintainability.
## Background: how Maven picks a version A transitive dependency can be requested at several versions via different paths. Maven resolves conflicts with **nearest-wins (dependency mediation)**: the version declared *closest to your project* in the tree wins, regardless of whether it is newer or older. This can silently **downgrade** a library below what another path needs, causing runtime `NoSuchMethodError`/`ClassNotFoundException`. ## dependencyConvergence Fails the build if any artifact appears at **more than one version** anywhere in the resolved graph — it demands total convergence to a single version per artifact. - Pros: maximal hygiene; surfaces every conflict. - Cons: very noisy on big projects; forces you to pin many versions via `dependencyManagement`. - The failure report prints each conflicting version with its full dependency path, which tells you exactly where to add management. ## requireUpperBoundDeps Fails only when the **resolved** version is **lower** than the highest version requested by any path. In other words it catches *downgrades* — the dangerous case — and tolerates the harmless situation where the nearest (winning) version is already the highest. - Pros: low noise, targets real risk. - Cons: does not force a single version; multiple-but-safe versions still 'pass'. ## Side-by-side - Two paths want `lib:1.0` and `lib:2.0`, nearest is `2.0` → convergence FAILS (two versions exist), upperBound PASSES (resolved 2.0 is the highest). - Two paths want `lib:1.0` and `lib:2.0`, nearest is `1.0` → both FAIL (convergence: two versions; upperBound: resolved 1.0 < requested 2.0 = downgrade). ## Fixing failures Add an explicit, authoritative version in `<dependencyManagement>`: ```xml <dependencyManagement> <dependencies> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>33.0.0-jre</version> </dependency> </dependencies> </dependencyManagement> ``` This pins the version regardless of where guava is pulled in, satisfying both rules. ## Practical guidance Many teams start with `requireUpperBoundDeps` (catches the worst class of bug with little noise) and adopt full `dependencyConvergence` selectively for published libraries.
- Why can requireUpperBoundDeps fail even when you never declared the lower version yourself?Because nearest-wins mediation can pick a transitively-declared older version that is closer in the tree than the path requesting the newer one, producing a silent downgrade.
- How do you fix a convergence/upperBound failure?Add an explicit version in dependencyManagement so every path resolves to the same pinned version, or use an exclusion to drop the unwanted path.
- Which rule is noisier and why?dependencyConvergence — it flags any artifact present at more than one version, which is common in large trees, whereas upperBound only flags actual downgrades.
saying these in an interview costs you the question
- Claiming Maven always picks the newest version — it uses nearest-wins, which can pick an older one.
- Treating dependencyConvergence and requireUpperBoundDeps as equivalent.
- Fixing conflicts by editing transitive POMs instead of using dependencyManagement/exclusions.