# Declaring Dependency Constraints

version 9.3.1-20260207121711+0000

Version declarations (including ranges) only affect the dependencies you declare directly. To control versions of transitive dependencies, use dependency constraints.

A dependency constraint sets version requirements for a module without adding that module as a dependency. When the module is pulled in, the constraint participates in version conflict resolution just like a declared dependency version:

- Constraints are not strict by default (they usually express "at least this version").

- You can make them strict or use rich versions (e.g., ranges, `prefer`, `reject`, `strictly`) when needed.

| Use constraints when you want to control versions centrally and avoid adding extra dependencies just to force a version.

You can declare constraints:

- Alongside dependencies in a single project (scoped to the same configuration buckets like `implementation`, `runtimeOnly`, `testImplementation`, etc.).

- Centrally in a platform (recommended for multi-project builds) using the `java-platform` plugin:

build.gradle.kts

    plugins {
        `java-platform`
    }

    dependencies {
        constraints {
            // Platform declares some versions of libraries used in subprojects
            api("commons-httpclient:commons-httpclient:3.1")
            api("org.apache.commons:commons-lang3:3.8.1")
        }
    }

build.gradle

    plugins {
        id 'java-platform'
    }

    dependencies {
        constraints {
            // Platform declares some versions of libraries used in subprojects
            api 'commons-httpclient:commons-httpclient:3.1'
            api 'org.apache.commons:commons-lang3:3.8.1'
        }
    }

You cannot declare constraints in version catalogs.

## Declaring constraints alongside direct dependencies

Constraints are scoped by bucket configurations (e.g., `implementation`, `runtimeOnly`). They apply whenever that dependency is encountered during resolution.

The `constraints {}` block is used within the `dependencies {}` block to declare these constraints:

build.gradle.kts

    plugins {
        `java-platform`
    }

    dependencies {
        constraints {
            api("commons-httpclient:commons-httpclient:3.1")
            runtime("org.postgresql:postgresql:42.2.5")
        }
    }

build.gradle

    plugins {
        id 'java-platform'
    }

    dependencies {
        constraints {
            api 'commons-httpclient:commons-httpclient:3.1'
            runtime 'org.postgresql:postgresql:42.2.5'
        }
    }

1. `api("commons-httpclient:commons-httpclient:3.1")`: ensures ≥ 3.1 for `api`.

2. `runtime("org.postgresql:postgresql:42.2.5")`: ensures ≥ 42.2.5 for `runtime`.

If multiple requirements exist, Gradle picks a version that satisfies all. If none exists, resolution fails with an error describing the conflict.

## Adding constraints on transitive dependencies

Use constraints to select transitive modules without introducing them as direct dependencies:

build.gradle.kts

    dependencies {
        implementation("org.apache.httpcomponents:httpclient")
        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')
        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')
            }
        }
    }

If `commons-codec` isn’t brought in transitively, the constraint is a no-op (it doesn’t add the module). If it is brought in, the constraint guides the version selection.

## Rich versions and strict versions for constraints

You can attach rich versions to constraints:

build.gradle.kts

    dependencies {
        constraints {
            implementation("com.google.guava:guava") {
                version {
                    strictly("33.1.0-jre")
                }
                because("avoid older versions with known issues")
            }
        }
    }

build.gradle

    dependencies {
        constraints {
            implementation("com.google.guava:guava") {
                version {
                    strictly("33.1.0-jre")
                }
                because("avoid older versions with known issues")
            }
        }
    }

## Transitivity of constraints

Dependency constraints are transitive.

If `library A` depends on `library B`, and `library B` declares a constraint on `module C`, that constraint will affect the version of `module C` that `library A` depends on.

For example, if `library A` depends on `module C version 2`, but `library B` declares a constraint on `module C version 3`, `library A` will resolve `version 3 of module C`.

## Publishing constraints

Dependency constraints are only published when using Gradle Module Metadata. This means they are fully supported only when both publishing and consuming modules with Gradle.

If modules are consumed with Maven or Ivy, the constraints may not be preserved.