[[resolution-strategy-safety-nets]]
= Preventing accidental dependency upgrades

In some situations, you might want to be in total control of the dependency graph.
In particular, you may want to make sure that:

- the versions declared in a build script actually correspond to the ones being resolved
- or make sure that dependency resolution is reproducible over time

Gradle provides ways to perform this by configuring the resolution strategy.

[[fail-version-conflict]]
== Failing on version conflict

There's a version conflict whenever Gradle finds the same module in two different versions in a dependency graph.
By default, Gradle performs _optimistic upgrades_, meaning that if version `1.1` and `1.3` are found in the graph, we resolve to the highest version, `1.3`.
However, it is easy to miss that some dependencies are upgraded because of a transitive dependency.
In the example above, if `1.1` was a version used in your build script and `1.3` a version brought transitively, you could use `1.3` without actually noticing.

To make sure that you are aware of such upgrades, Gradle provides a mode that can be activated in the resolution strategy of a configuration.
Imagine the following dependencies declaration:

.Direct dependency version not matching a transitive version
====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-resolutionStrategy/kotlin",files="build.gradle.kts[tags=dependencies]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-resolutionStrategy/groovy",files="build.gradle[tags=dependencies]"]
====

Then by default Gradle would upgrade `commons-lang3`, but it is possible to _fail_ the build:

.Fail on version conflict
====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-resolutionStrategy/kotlin",files="build.gradle.kts[tags=fail-on-version-conflict]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-resolutionStrategy/groovy",files="build.gradle[tags=fail-on-version-conflict]"]
====

[[reproducible-resolution]]
== Making sure resolution is reproducible

There are cases where dependency resolution can be _unstable_ over time.
That is to say that if you build at date D, building at date D+x may give a different resolution result.

This is possible in the following cases:

- dynamic dependency versions are used (version ranges, `latest.release`, `1.+`, ...)
- or _changing_ versions are used (SNAPSHOTs, fixed version with changing contents, ...)

The recommended way to deal with dynamic versions is to use <<dependency_locking.adoc#dependency-locking,dependency locking>>.
However, it is possible to prevent the use of dynamic versions altogether, which is an alternate strategy:

.Failing on dynamic versions
====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-resolutionStrategy/kotlin",files="build.gradle.kts[tags=fail-on-dynamic]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-resolutionStrategy/groovy",files="build.gradle[tags=fail-on-dynamic]"]
====

Likewise, it's possible to prevent the use of changing versions by activating this flag:

.Failing on changing versions
====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-resolutionStrategy/kotlin",files="build.gradle.kts[tags=fail-on-changing]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-resolutionStrategy/groovy",files="build.gradle[tags=fail-on-changing]"]
====

It's a good practice to fail on changing versions at release time.

Eventually, it's possible to combine both failing on dynamic versions and changing versions using a single call:

.Failing on non-reproducible resolution
====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-resolutionStrategy/kotlin",files="build.gradle.kts[tags=fail-on-unstable]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-resolutionStrategy/groovy",files="build.gradle[tags=fail-on-unstable]"]
====

[[resolution_consistency]]
== Getting consistent dependency resolution results

NOTE: Dependency resolution consistency is an incubating feature

It's a common misconception that there's a single dependency graph for an application.
In fact Gradle will, during a build, resolve a number of distinct dependency graphs, even within a single project.
For example, the graph of dependencies to use at compile time is different from the graph of dependencies to use at runtime.
In general, the graph of dependencies at runtime is a superset of the compile dependencies (there are exceptions to the rule, for example in case some dependencies are repackaged within the runtime binary).

Gradle resolves those dependency graphs independently.
This means, in the Java ecosystem for example, that the resolution of the "compile classpath" doesn't influence the resolution of the "runtime classpath".
Similarly, test dependencies could end up bumping the version of production dependencies, causing some surprising results when executing tests.

These surprising behaviors can be mitigated by enabling dependency resolution consistency.

[[sec::project_dependency_resolution_consistency]]
=== Enabling project-local dependency resolution consistency

For example, imagine that your Java library depends on the following libraries:

.First-level dependencies
====
include::sample[dir="snippets/dependencyManagement/customizingResolution-consistentResolution/kotlin",files="build.gradle.kts[tags=dependencies]"]
include::sample[dir="snippets/dependencyManagement/customizingResolution-consistentResolution/groovy",files="build.gradle[tags=dependencies]"]
====

Then resolving the `compileClasspath` configuration would resolve the `groovy` library to version `3.0.1` as expected.
However, resolving the `runtimeClasspath` configuration would instead return `groovy 3.0.2`.

The reason for this is that a transitive dependency of `vertx`, which is a `runtimeOnly` dependency, brings a higher version of `groovy`.
In general, this isn't a problem, but it also means that the version of the Groovy library that you are going to use at runtime is going to be different from the one that you used for compilation.

In order to avoid this situation, Gradle offers an API to explain that configurations should be resolved consistently.

[[sec:configuration_consistency]]
=== Declaring resolution consistency between configurations

In the example above, we can declare that we want, at runtime, the same versions of the common dependencies as compile time, by declaring that the "runtime classpath" _should be consistent with_ the "compile classpath":

.Declaring consistency between configurations
====
include::sample[dir="snippets/dependencyManagement/customizingResolution-consistentResolution/kotlin",files="build.gradle.kts[tags=explicit-configuration]"]
include::sample[dir="snippets/dependencyManagement/customizingResolution-consistentResolution/groovy",files="build.gradle[tags=explicit-configuration]"]
====

As a result, both the `runtimeClasspath` and `compileClasspath` will resolve Groovy 3.0.1.

The relationship is _directed_, which means that if the `runtimeClasspath` configuration has to be resolved, Gradle will _first_ resolve the `compileClasspath` and then "inject" the result of resolution as <<rich_versions.adoc#sec:strict-version,strict constraints>> into the `runtimeClasspath`.

If, for some reason, the versions of the two graphs cannot be "aligned", then resolution will fail with a call to action.

[[sec:java_consistency]]
=== Declaring consistent resolution in the Java ecosystem

The `runtimeClasspath` and `compileClasspath` example above are common in the Java ecosystem.
However, it's often not enough to declare consistency between those two configurations only.
For example, you most likely want the _test runtime classpath_ to be consistent with the
_runtime classpath_.

To make this easier, Gradle provides a way to configure consistent resolution for the Java ecosystem using the `java` extension:

.Declaring consistency in the Java ecosystem
====
include::sample[dir="snippets/dependencyManagement/customizingResolution-consistentResolution/kotlin",files="build.gradle.kts[tags=java-convention]"]
include::sample[dir="snippets/dependencyManagement/customizingResolution-consistentResolution/groovy",files="build.gradle[tags=java-convention]"]
====

Please refer to the link:{javadocPath}/org/gradle/api/plugins/JavaPluginExtension.html[Java Plugin Extension docs] for more configuration options.
