// Copyright (C) 2025 Gradle, Inc.
//
// Licensed under the Creative Commons Attribution-Noncommercial-ShareAlike 4.0 International License.;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://creativecommons.org/licenses/by-nc-sa/4.0/
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

[[best_practices_for_dependencies]]
= Best Practices for Dependencies

[[use_version_catalogs]]
== Use Version Catalogs to Centralize Dependency Versions

Version Catalogs provide a centralized, declarative way to manage dependency versions throughout a build.

=== Explanation

When you define your dependency versions in a single, shared version catalog, you reduce duplication and make upgrades easier.
Instead of changing dozens of `build.gradle(.kts)` files, you update the version in one place.
This simplifies maintenance, improves consistency, and reduces the risk of accidental version drift between modules.
Consistent version declarations across projects also make it easier to reason about behavior during testing—especially in modular builds where transitive upgrades can silently change runtime behavior in later stages of the build.

However, version catalogs only influence declared versions, not resolved versions.
Use them in combination with <<dependency_locking.adoc#sec:dependency-locking,dependency locking>> and <<resolution_rules.adoc#using-resolution-rules,version alignment>> to enforce consistency across builds.
To influence resolved versions, check out <<platforms.adoc#platforms,platforms>>.

=== Example

==== Don't Do This

Avoid declaring versions in `project.ext`, constants, or local variables:

====
include::sample[dir="snippets/best-practices/useVersionCatalogs-avoid/kotlin",files="build.gradle.kts[tags=avoid-this]"]
include::sample[dir="snippets/best-practices/useVersionCatalogs-avoid/groovy",files="build.gradle[tags=avoid-this]"]
====

Avoid misusing version catalogs for unrelated concerns:

* Don’t use them to store shared strings or non-library constants
* Don’t overload them with arbitrary logic or plugin-specific configuration

==== Do This Instead

Use a centralized `libs.versions.toml` file in your `gradle/` directory:

====
include::sample[dir="snippets/best-practices/useVersionCatalogs-do/common",files="gradle/libs.versions.toml[]"]
====

++++
<div style="text-align: right;">
  <a class="download-project-link"
     data-base-path="https://github.com/gradle/gradle/tree/master/platforms/documentation/docs/src/snippets/best-practices/useVersionCatalogs-do/"
     href="https://download-directory.github.io/?url=https://github.com/gradle/gradle/tree/master/platforms/documentation/docs/src/snippets/best-practices/useVersionCatalogs-do/kotlin">
    <img src="https://img.shields.io/badge/Download%20Project-GitHub-blue?logo=github&style=flat" alt="Download"/>
  </a>
</div>
++++

====
include::sample[dir="snippets/best-practices/useVersionCatalogs-do/kotlin",files="build.gradle.kts[tags=do-this]"]
include::sample[dir="snippets/best-practices/useVersionCatalogs-do/groovy",files="build.gradle[tags=do-this]"]
====

=== References

- <<version_catalogs.adoc#version-catalog,Version Catalogs>>

=== Tags

`<<tags_reference.adoc#tag:version-catalog,#version-catalog>>`

[[name_version_catalog_entries]]
== Name Version Catalog Entries Appropriately

Consistent and descriptive names in your version catalog enhance readability and maintainability across your build scripts.

=== Explanation

Version catalogs provide a centralized way to manage dependencies by mapping full dependency coordinates to concise, reusable aliases like `airlift-aircompressor`.
Adopting clear naming conventions for those aliases ensures that developers can easily identify and use dependencies throughout the project.

Aliases are typically made up of 1 to 3 segments.
For example `org.apache.commons:commons-lang3` could be represented as `commonsLang3`, `apache-commonsLang3`, or `commons-lang3`.

The following guidelines help in naming catalog entries effectively:

1. **Use dashes to separate segments**: Prefer hyphen/dashes (`-`) over underscores (`_`) to separate different parts of the entry name.
+
Example: For `org.apache.logging.log4j:log4j-api`, use `log4j-api`

2. **Derive the first segment from the project group**: Use a unique identifier from the project's group ID as the first segment. Do not include the top level domain in the segment (`com`, `org`, `net`, `dev`).
+
Example: For `com.fasterxml.jackson.core:jackson-databind`, use `jackson-databind` or `jackson-core-databind`

3. **Derive the second segment from the artifact ID**: Use a unique identifier from the artifact ID as the second segment.
+
Example: For `com.linecorp.armeria:armeria-grpc`, use `armeria-grpc`

4. **Avoid generic terms in the segments**: Exclude terms that are obvious or implied in the context of your project (`core`, `java`, `gradle`, `module`, `sdk`), especially if the term appears by itself.
+
Example: For `com.google.googlejavaformat:google-java-format`, use `google-java-format`, not `google-java` or `java`

5. **Omit redundant segments**: If the group and artifact IDs are the same, avoid repeating them.
+
Example: For `io.ktor:ktor-client-core`, use `ktor-client-core`, not `ktor-ktor-client-core`

6. **Convert internal dashes to camelCase**: If the artifact ID contains dashes, convert them to camelCase for better readability in code.
+
Example: `spring-boot-starter-web` becomes `springBootStarterWeb`

7. **Suffix plugin libraries with `-plugin`**: When referencing a plugin as a library (not in the `[plugins]` section), append `-plugin` to the name.
+
Example: For `org.owasp:dependency-check-gradle`, use `dependency-check-plugin`

=== Example

====
include::sample[dir="snippets/best-practices/versionCatalogNaming-do/common",files="gradle/libs.versions.toml[]"]
====

++++
<div style="text-align: right;">
  <a class="download-project-link"
     data-base-path="https://github.com/gradle/gradle/tree/master/platforms/documentation/docs/src/snippets/best-practices/versionCatalogNaming-do/"
     href="https://download-directory.github.io/?url=https://github.com/gradle/gradle/tree/master/platforms/documentation/docs/src/snippets/best-practices/versionCatalogNaming-do/kotlin">
    <img src="https://img.shields.io/badge/Download%20Project-GitHub-blue?logo=github&style=flat" alt="Download"/>
  </a>
</div>
++++

====
include::sample[dir="snippets/best-practices/versionCatalogNaming-do/kotlin",files="build.gradle.kts[tags=do-this]"]
include::sample[dir="snippets/best-practices/versionCatalogNaming-do/groovy",files="build.gradle[tags=do-this]"]
====

=== References

- link:https://blog.gradle.org/best-practices-naming-version-catalog-entries[Best Practices for Naming Gradle Version Catalog Entries]

=== Tags

`<<tags_reference.adoc#tag:version-catalog,#version-catalog>>`

[[set_up_repositories_in_settings]]
== Set up your Dependency Repositories in the Settings file

Declare your repositories for your plugins and dependencies in `settings.gradle.kts`.

=== Explanation

Using `settings.gradle.kts` file to declare repositories has several benefits:

 - *Avoids repetition*: Centralizing repository declarations eliminates the need to repeat them in each project's `build.gradle.kts`.
 - *Improves debuggability*: Ensures all projects resolve dependencies during resolution from the same repositories, in a consistent order.
 - *Matches the build model*: Repositories are not part of the project definition; they are part of global build logic, so settings is a more appropriate place for them.

NOTE: While https://github.com/gradle/gradle/issues/32443[`dependencyResolutionManagement.repositories` is an incubating API], it is the preferred way of declaring repositories.

=== Example

==== Don't Do This

You could set up repositories in individual `build.gradle.kts` files with:

====
include::sample[dir="snippets/best-practices/settingsRepositories-avoid/kotlin",files="build.gradle.kts[tags=avoid-this]"]
include::sample[dir="snippets/best-practices/settingsRepositories-avoid/groovy",files="build.gradle[tags=avoid-this]"]
====

==== Do This Instead

Instead, you should set them up in `settings.gradle.kts` like this:

++++
<div style="text-align: right;">
  <a class="download-project-link"
     data-base-path="https://github.com/gradle/gradle/tree/master/platforms/documentation/docs/src/snippets/best-practices/settingsRepositories-do/"
     href="https://download-directory.github.io/?url=https://github.com/gradle/gradle/tree/master/platforms/documentation/docs/src/snippets/best-practices/settingsRepositories-do/kotlin">
    <img src="https://img.shields.io/badge/Download%20Project-GitHub-blue?logo=github&style=flat" alt="Download"/>
  </a>
</div>
++++

====
include::sample[dir="snippets/best-practices/settingsRepositories-do/kotlin",files="settings.gradle.kts[tags=do-this]"]
include::sample[dir="snippets/best-practices/settingsRepositories-do/groovy",files="settings.gradle[tags=do-this]"]
====

=== Tags

`<<tags_reference.adoc#tag:structuring-builds,#structuring-builds>>`, `<<tags_reference.adoc#tag:repositories,#repositories>>`

[[dont_depend_on_kotlin_stdlib]]
== Don't Explicitly Depend on the Kotlin Standard Library

The Kotlin Gradle Plugin automatically adds a dependency on the Kotlin standard library (`stdlib`) to each source set, so there is no need to declare it explicitly.

=== Explanation

The version of the standard library added is the same as the version of the Kotlin Gradle Plugin applied to the project.
If your build does not require a specific or different version of the standard library, you should avoid adding it manually.

NOTE: Setting the `kotlin.stdlib.default.dependency` property to `false` prevents the Kotlin plugin from automatically adding the Kotlin standard library dependency to your project. This can be useful in specific scenarios, such as when you want to manage the Kotlin standard library dependency version manually.

=== Example

==== Don't Do This

====
include::sample[dir="snippets/best-practices/kotlinStdLib-avoid/kotlin",files="build.gradle.kts[tags=avoid-this]"]
include::sample[dir="snippets/best-practices/kotlinStdLib-avoid/groovy",files="build.gradle[tags=avoid-this]"]
====

<1> *`stdlib` is explicitly depended upon*: This project contains an implicit dependency on the Kotlin standard library, which is required to compile its source code.

==== Do This Instead

++++
<div style="text-align: right;">
  <a class="download-project-link"
     data-base-path="https://github.com/gradle/gradle/tree/master/platforms/documentation/docs/src/snippets/best-practices/kotlinStdLib-do/"
     href="https://download-directory.github.io/?url=https://github.com/gradle/gradle/tree/master/platforms/documentation/docs/src/snippets/best-practices/kotlinStdLib-do/kotlin">
    <img src="https://img.shields.io/badge/Download%20Project-GitHub-blue?logo=github&style=flat" alt="Download"/>
  </a>
</div>
++++

====
include::sample[dir="snippets/best-practices/kotlinStdLib-do/kotlin",files="build.gradle.kts[tags=do-this]"]
include::sample[dir="snippets/best-practices/kotlinStdLib-do/groovy",files="build.gradle[tags=do-this]"]
====

<1> *`stdlib` dependency is not included explicitly*: The standard library remains available for use, and source code requiring it can be compiled without any issues.

=== References

- https://docs.gradle.org/current/kotlin-dsl/gradle/org.gradle.kotlin.dsl/kotlin.html[the `kotlin()` function]

=== Tags

`<<tags_reference.adoc#tag:dependencies,#dependencies>>`

[[avoid_duplicate_dependencies]]
== Avoid Redundant Dependency Declarations

Avoid declaring the same dependency multiple times, especially when it is already available transitively or through another configuration.

=== Explanation

Duplicating dependencies in Gradle build scripts can lead to:

* *Increased maintenance*: Declaring a dependency in multiple places makes it harder to manage.
* *Unexpected behavior*: Declaring the same dependency in multiple configurations (e.g., `compileOnly` and `implementation`) can result in hard-to-diagnose classpath issues.

=== Example

==== Don't Do This

====
include::sample[dir="snippets/best-practices/avoidDuplicateDependencies-avoid/kotlin",files="build.gradle.kts[]"]
include::sample[dir="snippets/best-practices/avoidDuplicateDependencies-avoid/groovy",files="build.gradle[]"]
====

<1> Redundant dependency in `implementation` scope.

==== Do This Instead

++++
<div style="text-align: right;">
  <a class="download-project-link"
     data-base-path="https://github.com/gradle/gradle/tree/master/platforms/documentation/docs/src/snippets/best-practices/avoidDuplicateDependencies-do/"
     href="https://download-directory.github.io/?url=https://github.com/gradle/gradle/tree/master/platforms/documentation/docs/src/snippets/best-practices/avoidDuplicateDependencies-do/kotlin">
    <img src="https://img.shields.io/badge/Download%20Project-GitHub-blue?logo=github&style=flat" alt="Download"/>
  </a>
</div>
++++

====
include::sample[dir="snippets/best-practices/avoidDuplicateDependencies-do/kotlin",files="build.gradle.kts[]"]
include::sample[dir="snippets/best-practices/avoidDuplicateDependencies-do/groovy",files="build.gradle[]"]
====

<1> Declare dependency once

=== Tags

`<<tags_reference.adoc#tag:dependencies,#dependencies>>`

[[single-gav-string]]
== Declare Dependencies using a single GAV (`group:artifact:version`) String

When declaring dependencies without a <<version_catalogs.adoc#version-catalog,version catalog>>, prefer using the single GAV string notation `implementation("org.example:library:1.0")`.
Avoid using the named argument notation.
The named argument notation has been deprecated and will no longer be supported starting in Gradle 10.

=== Explanation

All of these declarations will be treated equivalently when Gradle resolves dependencies.
However, the single-string form is more concise, easier to read, and is widely adopted in the broader JVM ecosystem.

This format is also recommended by link:https://central.sonatype.com/artifact/com.google.guava/guava[Maven Central] in its documentation and usage examples, making it the most familiar and consistent style for developers across tools.

=== Example

==== Don't Do This

====
include::sample[dir="snippets/best-practices/useGavString-avoid/kotlin",files="build.gradle.kts[tags=avoid-this]"]
include::sample[dir="snippets/best-practices/useGavString-avoid/groovy",files="build.gradle[tags=avoid-this]"]
====
<1> Avoid the named argument notation when declaring dependencies
<2> Other modifiers methods and constraints like `exclude` are not included in this recommendation and can use named argument notation as needed

==== Do This Instead

++++
<div style="text-align: right;">
  <a class="download-project-link"
     data-base-path="https://github.com/gradle/gradle/tree/master/platforms/documentation/docs/src/snippets/best-practices/useGavString-do/"
     href="https://download-directory.github.io/?url=https://github.com/gradle/gradle/tree/master/platforms/documentation/docs/src/snippets/best-practices/useGavString-do/kotlin">
    <img src="https://img.shields.io/badge/Download%20Project-GitHub-blue?logo=github&style=flat" alt="Download"/>
  </a>
</div>
++++

====
include::sample[dir="snippets/best-practices/useGavString-do/kotlin",files="build.gradle.kts[tags=do-this]"]
include::sample[dir="snippets/best-practices/useGavString-do/groovy",files="build.gradle[tags=do-this]"]
====
<1> Use the string notation instead when declaring dependencies
<2> Other modifiers methods and constraints like `exclude` are not included in this recommendation and can use named argument notation as needed

=== References

- <<declaring_dependencies_basics.adoc#declaring-dependencies-basics,Declaring Dependencies Basics>>

=== Tags

`<<tags_reference.adoc#tag:dependencies,#dependencies>>`

[[use_content_filtering]]
== Use Content Filtering with multiple Repositories

When using multiple repositories in a build, use <<filtering_repository_content.adoc#repository-content-filtering,repository content filtering>> to ensure that dependencies are resolved from an appropriate repository.

=== Explanation

If your build declares more than one repository, you should declare content filters on these repositories to ensure you search for and obtain dependencies from the correct place.

Content filtering is necessary if you have a reason to restrict searching for a dependency to a particular repository, and can be a good idea even if acceptable dependency artifacts exist in multiple locations.

When possible, you should use the <<filtering_repository_content.adoc#sec:declaring-content-repositories,exclusiveContent>> feature to restrict dependencies to a particular known repository.

Content filtering has three main benefits:

1. **Performance**, since you only query repositories for dependencies that should actually exist within them
2. **Security**, by avoiding asking potentially every repository for every dependency (even ones they shouldn't contain), you improve resiliency to supply chain attacks by avoiding leaking information about your dependencies to other repositories, or even downloading potentially malicious artifacts
3. **Reliability**, by avoiding searching repositories that contain invalid or incorrect metadata for particular dependencies, which could result in obtaining incorrect transitive dependencies

Repositories will be searched for dependencies that pass their filters in the order they are declared.
Often the last repository is declared without any filters in order to serve as a default _fallback repository_ that is queried for any dependencies that don't pass the filters present on the other repositories.

WARNING: Carefully consider using content filtering with a fallback repository.
This can pose a security risk, so make sure you fully trust the fallback repository.
This setup can result in inadvertently (and silently) resolving dependencies from the fallback repository that were intended to come from filtered repositories if the dependencies were not available in those repositories.

=== Example

==== Don't Do This

Don't add multiple repositories without content filtering:

====
include::sample[dir="snippets/best-practices/contentFiltering-avoid/kotlin",files="settings.gradle.kts[tags=content-filtering-avoid]"]
include::sample[dir="snippets/best-practices/contentFiltering-avoid/groovy",files="settings.gradle[tags=content-filtering-avoid]"]
====

==== Do This Instead

Use content filtering to ensure that the proper repositories are searched first for the expected artifacts:

++++
<div style="text-align: right;">
  <a class="download-project-link"
     data-base-path="https://github.com/gradle/gradle/tree/master/platforms/documentation/docs/src/snippets/best-practices/contentFiltering-do/"
     href="https://download-directory.github.io/?url=https://github.com/gradle/gradle/tree/master/platforms/documentation/docs/src/snippets/best-practices/contentFiltering-do/kotlin">
    <img src="https://img.shields.io/badge/Download%20Project-GitHub-blue?logo=github&style=flat" alt="Download"/>
  </a>
</div>
++++

====
include::sample[dir="snippets/best-practices/contentFiltering-do/kotlin",files="settings.gradle.kts[tags=content-filtering-do]"]
include::sample[dir="snippets/best-practices/contentFiltering-do/groovy",files="settings.gradle[tags=content-filtering-do]"]
====

In many cases, it is better to use exclusive content filtering, as it ensures that dependencies _can only be found in the expected repository_.
If they are not present there, they will not be found at all.

++++
<div style="text-align: right;">
  <a class="download-project-link"
     data-base-path="https://github.com/gradle/gradle/tree/master/platforms/documentation/docs/src/snippets/best-practices/contentFilteringExclusive-do/"
     href="https://download-directory.github.io/?url=https://github.com/gradle/gradle/tree/master/platforms/documentation/docs/src/snippets/best-practices/contentFilteringExclusive-do/kotlin">
    <img src="https://img.shields.io/badge/Download%20Project-GitHub-blue?logo=github&style=flat" alt="Download"/>
  </a>
</div>
++++

====
include::sample[dir="snippets/best-practices/contentFilteringExclusive-do/kotlin",files="settings.gradle.kts[tags=content-filtering-do]"]
include::sample[dir="snippets/best-practices/contentFilteringExclusive-do/groovy",files="settings.gradle[tags=content-filtering-do]"]
====

=== References

- <<filtering_repository_content.adoc#repository-content-filtering,Filtering Repository Content>>

=== Tags

`<<tags_reference.adoc#tag:repositories,#repositories>>`, `<<tags_reference.adoc#tag:dependencies,#dependencies>>`

[[apply_exclusions_narrowly]]
== Apply Exclusions Narrowly

When excluding transitive dependencies, apply exclusions as narrowly as possible.

=== Explanation

Sometimes you may need to <<how_to_exclude_transitive_dependencies.adoc#how_to_exclude_transitive_dependencies,exclude transitive dependencies>> that cause conflicts or issues in your project.

Exclusions can negatively affect dependency resolution performance.
Applying exclusions as narrowly as possible minimizes this impact.
It also reduces the risks of inadvertently and silently excluding dependencies that are required elsewhere in your build, and of accidental runtime dependency clashes.

Gradle offers several ways to exclude transitive dependencies.
When excluding transitive dependencies, keep the scope as narrow as possible:

 - Attach exclusions to *specific dependencies* rather than applying them to an entire configuration.
 - Exclude a single `module` from a `group`, instead of excluding the entire `group`.
 - *Avoid* global exclusions using `configurations.all { ... }` or `configurations.configureEach { ... }`.

=== Example

==== Don't Do This

====
include::sample[dir="snippets/best-practices/applyExclusionsNarrowly-avoid/kotlin",files="build.gradle.kts[tags=avoid-this]"]
include::sample[dir="snippets/best-practices/applyExclusionsNarrowly-avoid/groovy",files="build.gradle[tags=avoid-this]"]
====
<1> The `commons-pool2` dependency transitively includes `cglib:cglib` and `org.ow2.asm:asm-util` as optional dependencies - we want to exclude both.
`hibernate-core` transitively optionally includes `cglib:cglib`, and also `javaassist:javassist` - we want to exclude both.
<2> This excludes *every* module provided by the `cglib` group from *every* dependency in the `implementation` configuration.
If other current or future dependencies in this project rely on different modules from `cglib`, those dependencies may fail to resolve, leading to compilation or runtime errors.
<3> This excludes `org.ow2.asm:asm-util` from *every* dependency in the `implementation` configuration.
If future dependencies rely on `org.ow2.asm:asm-util`, they may fail at compile or runtime because the module will be silently excluded.
<4> This excludes `javaassist:javassist` from *all* dependencies in *all* configurations, including those added by plugins or in the future, which carries the same risks as above, but on a larger scale.

==== Do This Instead

Exclude transitive dependencies as narrowly as possible, ideally on individual dependencies:

++++
<div style="text-align: right;">
  <a class="download-project-link"
     data-base-path="https://github.com/gradle/gradle/tree/master/platforms/documentation/docs/src/snippets/best-practices/applyExclusionsNarrowly-do/"
     href="https://download-directory.github.io/?url=https://github.com/gradle/gradle/tree/master/platforms/documentation/docs/src/snippets/best-practices/applyExclusionsNarrowly-do/kotlin">
    <img src="https://img.shields.io/badge/Download%20Project-GitHub-blue?logo=github&style=flat" alt="Download"/>
  </a>
</div>
++++

====
include::sample[dir="snippets/best-practices/applyExclusionsNarrowly-do/kotlin",files="build.gradle.kts[tags=do-this]"]
include::sample[dir="snippets/best-practices/applyExclusionsNarrowly-do/groovy",files="build.gradle[tags=do-this]"]
====
<1> Exclusions are applied only to the dependency that actually transitively includes them.
<2> All exclusions apply to a particular `module` instead of every module from a particular `group`.
<3> `javaassist:javassist` is only excluded from the `hibernate-core` dependency - the only dependency that transitively includes it.

Though it may seem repetitive to exclude the same transitive dependencies from multiple dependencies, this approach is safer, more performant, less likely to cause accidental runtime crashes, and makes it clearer which dependencies are affected by each exclusion.

=== References

- <<resolution_rules.adoc#sec:exclude-trans-deps,Excluding Transitive Dependencies>>

=== Tags

`<<tags_reference.adoc#tag:dependencies,#dependencies>>`
