# Declaring Dependency Constraints

version 9.0.0

Dependency constraints function similarly to dependencies, with the key distinction that they do not introduce a dependency themselves. Instead, constraints define version requirements that influence the resolution process when a dependency is brought into the project by other means.

Although constraints are not strict versions by default, you can specify a strict version constraint if needed. Once the dependency is included, the version specified by the constraint participates in conflict resolution just as if it were declared as a direct dependency.

When developing a single-project library, constraints can be directly declared alongside direct dependencies. However, when developing multi-project libraries and applications, dependencies are best declared centrally in a platform:

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'
        }
    }

In general, dependencies are categorized as either direct or transitive:

- Direct dependencies are those explicitly specified within a component’s build or metadata.

- Transitive dependencies are not directly specified; they are pulled in automatically as dependencies of the direct dependencies.

A component may require both direct and transitive dependencies to compile or run.

## Declaring constraints alongside direct dependencies

Dependency constraints allow you to define the version or version range for a specific dependency, whenever that dependency is encountered during resolution.

This is the preferred method for managing the version of a component across multiple configurations or projects.

When Gradle resolves a module version, it considers all relevant factors, including rich versions, transitive dependencies, and dependency constraints for that module. The highest version that meets all the conditions is selected. If no such version exists, Gradle will fail with an error, detailing the conflicting declarations.

In such cases, you can adjust your dependency declarations, dependency constraints, or make necessary changes to transitive dependencies.

Like dependency declarations, dependency constraints are scoped by configurations, allowing you to selectively apply them to specific parts of a build.

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")`:

   - This line creates a constraint on the `api` configuration, asserting that if `commons-httpclient` is ever resolved by a resolvable configuration that extends the `api` configuration, its version must be `3.1` or higher.

   - If a transitive dependency (a dependency of a dependency) or another module in the project pulls in a different version of `commons-httpclient`, Gradle enforce the dependency to resolve to at least version `3.1`.

   - This constraint ensures that the library `commons-httpclient` will be at least version `3.1` across all configuration that extend the `api` configuration.

2. `runtime("org.postgresql:postgresql:42.2.5")`:

   - Similarly, this line applies a constraint on the `runtime` configuration, enforcing that `org.postgresql:postgresql` must resolve to at least version `42.2.5`.

   - Even if other dependencies or modules within the project try to bring in a different version of `postgresql`, Gradle will choose the higher of `42.2.5` and the other declared versions.

   - This ensures that any runtime dependencies on `postgresql` will resolve to at least version `42.2.5` across all resolvable configurations that extend the `runtime` configuration.

## Adding constraints on transitive dependencies

Issues with dependency management often arise from transitive dependencies. Developers sometimes mistakenly address these issues by adding direct dependencies instead of handling them properly with constraints.

Dependency constraints allow you to control the selection of transitive dependencies.

In the following example, the version constraint for `commons-codec:1.11` applies only when `commons-codec` is brought in as a transitive dependency since it is not directly declared as a dependency in the project. If `commons-codec` is not pulled in transitively, the constraint has no effect:

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'
            }
        }
    }

Dependency constraints can also define rich version constraints and support strict versions, allowing you to enforce a specific version even if it conflicts with a transitive dependency’s version (e.g., if a downgrade is necessary).

| 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.

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`.