Gradle supports a rich model for declaring versions, which allows to combine different level of version information. The terms and their meaning are explained below, from the strongest to the weakest:

strictly

Any version not matched by this version notation will be excluded. This is the strongest version declaration. On a declared dependency, a strictly can downgrade a version. When on a transitive dependency, it will cause dependency resolution to fail if no version acceptable by this clause can be selected. See overriding dependency version for details. This term supports dynamic versions.

When defined, this overrides any previous require declaration and clears previous reject.

require

Implies that the selected version cannot be lower than what require accepts but could be higher through conflict resolution, even if higher has an exclusive higher bound. This is what a direct dependency translates to. This term supports dynamic versions.

When defined, this overrides any previous strictly declaration and clears previous reject.

prefer

This is a very soft version declaration. It applies only if there is no stronger non dynamic opinion on a version for the module. This term does not support dynamic versions.

Definition can complement strictly or require.

When defined, this overrides any previous prefer declaration and clears previous reject.

There is also an additional term outside of the level hierarchy:

reject

Declares that specific version(s) are not accepted for the module. This will cause dependency resolution to fail if the only versions selectable are also rejected. This term supports dynamic versions.

The following table illustrates a number of use cases and how to combine the different terms for rich version declaration:

Table 1. Rich version use cases
Which version(s) of this dependency are acceptable? strictly require prefer rejects Selection result

Tested with version 1.5, believe all future versions should work.

1.5

Any version starting from 1.5, equivalent of org:foo:1.5. An upgrade to 2.4 is accepted.

Tested with 1.5, soft constraint upgrades according to semantic versioning.

[1.0, 2.0[

1.5

Any version between 1.0 and 2.0, 1.5 if nobody else cares. An upgrade to 2.4 is accepted.
πŸ”’

Tested with 1.5, but follows semantic versioning.

[1.0, 2.0[

1.5

Any version between 1.0 and 2.0 (exclusive), 1.5 if nobody else cares.
Overwrites versions from transitive dependencies.
πŸ”’

Same as above, with 1.4 known broken.

[1.0, 2.0[

1.5

1.4

Any version between 1.0 and 2.0 (exclusive) except for 1.4, 1.5 if nobody else cares.
Overwrites versions from transitive dependencies.
πŸ”’

No opinion, works with 1.5.

1.5

1.5 if no other opinion, any otherwise.

No opinion, prefer latest release.

latest.release

The latest release at build time.
πŸ”’

On the edge, latest release, no downgrade.

latest.release

The latest release at build time.
πŸ”’

No other version than 1.5.

1.5

1.5, or failure if another strict or higher require constraint disagrees.
Overwrites versions from transitive dependencies.

1.5 or a patch version of it exclusively.

[1.5,1.6[

Latest 1.5.x patch release, or failure if another strict or higher require constraint disagrees.
Overwrites versions from transitive dependencies.
πŸ”’

Lines annotated with a lock (πŸ”’) indicate that leveraging dependency locking makes sense in this context. Another concept that relates with rich version declaration is the ability to publish resolved versions instead of declared ones.

Using strictly, especially for a library, must be a well thought process as it has an impact on downstream consumers. At the same time, used correctly, it will help consumers understand what combination of libraries do not work together in their context. See overriding dependency version for more information.

Rich version information will be preserved in the Gradle Module Metadata format. However conversion to Ivy or Maven metadata formats will be lossy. The highest level will be published, that is strictly or require over prefer. In addition, any reject will be ignored.

Rich version declaration is accessed through the version DSL method on a dependency or constraint declaration which gives access to MutableVersionConstraint.

build.gradle.kts
dependencies {
    implementation("org.slf4j:slf4j-api") {
        version {
            strictly("[1.7, 1.8[")
            prefer("1.7.25")
        }
    }

    constraints {
        add("implementation", "org.springframework:spring-core") {
            version {
                require("4.2.9.RELEASE")
                reject("4.3.16.RELEASE")
            }
        }
    }
}
build.gradle
dependencies {
    implementation('org.slf4j:slf4j-api') {
        version {
            strictly '[1.7, 1.8['
            prefer '1.7.25'
        }
    }

    constraints {
        implementation('org.springframework:spring-core') {
            version {
                require '4.2.9.RELEASE'
                reject '4.3.16.RELEASE'
            }
        }
    }
}