Diamond conflicts and vulnerable transitives are everyday problems; Gradle gives precise tools to fix them.
When the dependency graph is resolved, each module ends up with exactly one version per configuration. Gradle picks the highest requested version by default; this differs from Maven's nearest-wins rule. ./gradlew dependencyInsight --dependency <name> --configuration runtimeClasspath shows every path and why a version won.
Tools to shape the result:
- Platforms / BOMs:
implementation(platform("org.springframework.boot:spring-boot-dependencies:3.4.4"))imports a Maven BOM so all its modules get aligned versions.enforcedPlatformoverrides even higher requests. - Rich versions:
implementation("com.foo:bar") { version { strictly("[1.2, 2.0[") ; prefer("1.4") } }. - Constraints adjust transitive versions without adding a direct dependency (e.g. to fix a CVE).
- Exclusions:
implementation("x:y:1.0") { exclude(group = "commons-logging") }drops an unwanted transitive. - Substitution in
configurations.all { resolutionStrategy.dependencySubstitution { ... } }replaces a module with another or with a project. resolutionStrategy.failOnVersionConflict()makes conflicts explicit.- Capabilities detect mutually exclusive libraries (e.g. two logging bindings).
Dependency locking (dependencyLocking { lockAllConfigurations() } and ./gradlew dependencies --write-locks) records resolved versions in gradle.lockfile, so builds stay reproducible even with ranges. Dependency verification checks checksums and signatures of downloaded artifacts.
Separate configurations (compileClasspath, runtimeClasspath, testRuntimeClasspath) may resolve to different versions, which is expected.
Example
plugins { java }
repositories { mavenCentral() }
dependencies {
implementation(platform("org.springframework.boot:spring-boot-dependencies:3.4.4"))
implementation("org.springframework.boot:spring-boot-starter-web") // version from the BOM
implementation("org.apache.httpcomponents.client5:httpclient5:5.4.3") {
exclude(group = "commons-logging", module = "commons-logging")
}
implementation("com.fasterxml.jackson.core:jackson-databind") {
version { strictly("[2.18, 2.19[") }
}
constraints {
implementation("org.yaml:snakeyaml:2.4") { because("fix CVE in transitive version") }
}
}
configurations.all {
resolutionStrategy {
failOnVersionConflict()
cacheChangingModulesFor(0, "seconds")
}
}
dependencyLocking { lockAllConfigurations() } // $ ./gradlew dependencies --write-locks
// $ ./gradlew dependencyInsight --dependency snakeyaml --configuration runtimeClasspathRemember
- Gradle resolves conflicts to the highest version unless told otherwise
- platform(BOM) aligns versions across a family of modules
- Constraints, strict versions and exclusions adjust transitives
- Locking and verification make resolution reproducible and safe