This guide explains how to manage and upgrade versions of transitive dependencies in Gradle using dependency constraints.

Why Upgrade a Transitive Dependency?

There are many reasons to upgrade a version of a dependency:

  • Bug fixes resolve issues present in older versions.

  • Security patches address vulnerabilities to keep the project safe.

  • Performance improvements make builds faster and more efficient.

  • New features provide additional functionality and better compatibility.

Step 1: Setting Dependency Constraints on Transitive Dependencies

Dependency constraints allow you to define the version or version range of both direct dependencies and transitive dependencies.

Dependency constraints can also override transitive dependencies and enforce strict versions, even downgrading if necessary.

To enforce specific versions of transitive dependencies, use the constraints block inside dependencies {}:

build.gradle.kts
dependencies {
    implementation("org.apache.httpcomponents:httpclient") // No version specified
    constraints {
        implementation("org.apache.httpcomponents:httpclient:4.5.3") {
            because("previous versions have a bug impacting this application")
        }
        implementation("commons-codec:commons-codec:1.11") {
            because("version 1.9 pulled from httpclient has bugs affecting this application")
        }
    }
}
build.gradle
dependencies {
    implementation("org.apache.httpcomponents:httpclient") // No version specified
    constraints {
        implementation("org.apache.httpcomponents:httpclient:4.5.3") {
            because("previous versions have a bug impacting this application")
        }
        implementation("commons-codec:commons-codec:1.11") {
            because("version 1.9 pulled from httpclient has bugs affecting this application")
        }
    }
}
  • The implementation dependency omits the version.

  • The constraints block enforces specific versions for httpclient and commons-codec.

  • The constraint for commons-codec only applies if it is pulled transitively.

Running ./gradlew dependencies --configuration runtimeClasspath showcases the results:

dependencies.out
> Task :dependencies

------------------------------------------------------------
Root project 'how_to_upgrade_transitive_dependencies'
------------------------------------------------------------

compileClasspath - Compile classpath for source set 'main'.
+--- org.apache.httpcomponents:httpclient -> 4.5.3
|    +--- org.apache.httpcomponents:httpcore:4.4.6
|    +--- commons-logging:commons-logging:1.2
|    \--- commons-codec:commons-codec:1.9 -> 1.11
+--- org.apache.httpcomponents:httpclient:4.5.3 (c)
\--- commons-codec:commons-codec:1.11 (c)

Summary

Use dependency constraints when:

  • You want to override transitive dependencies with a specific version.

  • You need to enforce a strict version or range across multiple dependencies.

  • You want to prevent dependency conflicts without manually adding transitive dependencies.