Maven resolves a version conflict by choosing the declaration nearest to your project, and Gradle by default chooses the highest requested version. When two libraries want different versions of a third, the two tools can pick different ones. Compare the resolved trees after the move and pin anything that must not change.
The two rules side by side
| Tool | Conflict rule |
|---|---|
| Maven | Nearest definition wins. At equal depth the first declaration wins |
| Gradle | Highest version wins by default |
The Maven documentation says it picks the nearest definition, uses the version of the closest dependency in the tree, and that you can guarantee a version by declaring it explicitly in your POM. The Gradle documentation says it considers all requested versions across the graph and by default selects the highest. The Gradle migration guide repeats the contrast: Maven uses a closest match algorithm while Gradle picks the newest.
Why this is a security question
A shift toward newer is often fine, and sometimes a fix. A shift toward older is the dangerous direction: Maven could have been using a patched version that sat near the root, while a Gradle graph could in principle land on another one. The honest answer is that you do not know until you compare. Both directions can also break behavior, so the check serves reliability as well.
Steps to find the shifts
- Print the resolved dependency tree from the Maven build and save it.
- Print the resolved dependency tree from the Gradle build for the matching configurations.
- Reduce both to group, artifact and version.
- Diff them and list every artifact whose version differs.
- For each one, ask why. Gradle can explain a selection with its dependency insight report, covered in why was this version selected.
- Decide per artifact: accept the new version, or pin the old one.
Pinning what must not move
The migration guide lists the Gradle mechanisms for controlling versions per project: dependency constraints, bills of materials and overriding transitive versions. Constraints are the usual tool for a security floor. See Gradle constraints to force a fixed transitive version.
Note the Gradle rule on strict declarations from the resolution docs: if a strictly version is declared lower than the highest requested, resolution fails. That failure is useful, since it surfaces a conflict instead of hiding it.
After the cutover
Turn on locking so the resolved set is recorded in gradle.lockfile and changes show up in code review. The Gradle docs describe generating the lock state with the write locks argument and say lockfiles should be checked in. Read Maven nearest definition wins for the Maven side in more depth.
Safeguard's dependency scanning and SBOM generation can show the vulnerable versions in each tree, and the free scan at gold.safeguard.sh/scan is a quick way to start.
Quick check
Which tool is safer by default?
Neither. Highest wins tends to pull in newer fixes, nearest wins tends to preserve what you declared. What matters is that you review the result.
How do I guarantee a version in Maven?
Declare it explicitly in your own POM, as the Maven documentation says.
Sources
- Maven, introduction to the dependency mechanism
- Gradle, graph resolution
- Gradle, migrating builds from Apache Maven
- Gradle, locking versions
Last reviewed: 19 October 2026.