skip to content

Why do pluginManagement and dependencyResolutionManagement live in settings.gradle.kts rather than in a build script?

level: middleimportance: must knowfreq 60%

answer

  1. resolution policy is build-wide
  2. must precede project configuration
  3. version catalog 'libs' defined in settings
  4. FAIL_ON_PROJECT_REPOS single source
  5. plugin repos resolved before apply

basics

~10 s

Gradle must know how to resolve plugins and dependencies before configuring any project. Settings is evaluated first, build-wide, so resolution rules and version catalogs belong there.

solid answer

~50 s

Both blocks are *build-wide resolution policy*, and they must be established before any project is configured — which is exactly what the settings script does in the initialization phase. `pluginManagement { repositories { ... }; plugins { ... } }` tells Gradle where to fetch plugins and what versions to pin, so it can resolve the `plugins { }` blocks in every build script consistently. If each project picked its own plugin repos, the build would be non-deterministic. `dependencyResolutionManagement { repositories { ... }; versionCatalogs { ... } }` centralises *dependency* repositories and the version catalog (the `libs` accessor) for all projects. With `RepositoriesMode.FAIL_ON_PROJECT_REPOS`, project-level repositories are rejected, enforcing one source of truth. Putting these per-project would mean every build script repeats repo lists and risks divergence; centralising in settings gives one governed, deterministic resolution surface for the whole build. This is why they're `Settings` members, not `Project` members.

code

kotlin · 7 lines
kotlin
// settings.gradle.kts
pluginManagement { repositories { gradlePluginPortal() } }
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories { mavenCentral() }
    versionCatalogs { create("libs") { library("guava", "com.google.guava:guava:33.0.0-jre") } }
}

go deeper

for a junior

Recognise these blocks live in settings and centralise repos/plugin versions; don't need the modes.

for a middle

Explain the init-before-configuration ordering and that the version catalog and repos are centralised; name FAIL_ON_PROJECT_REPOS.

for a senior

Discuss determinism and DRY, the RepositoriesMode options, and how this prevents per-project repo drift.

for a principal

Frame settings-level resolution as supply-chain governance: one audited repo surface, centrally pinned plugin/library versions, enforced via repositoriesMode across all teams.

## Resolution is a build-wide concern Resolving a plugin or a dependency means deciding *which repository* and *which version*. In a multi-project build you want that decision made **once**, consistently, for every project — otherwise two subprojects could pull the same library from different repos or at different versions. Gradle therefore lifts both kinds of resolution policy into the `Settings` object, evaluated in the **initialization phase before any project's configuration phase**. ## pluginManagement ```kotlin // settings.gradle.kts pluginManagement { repositories { gradlePluginPortal() mavenCentral() } plugins { id("org.jetbrains.kotlin.jvm") version "2.0.0" } } ``` This defines where plugin artifacts come from and can pin plugin versions centrally. Build scripts then just write `plugins { kotlin("jvm") }` without a version. Plugins must be resolvable *before* configuration because applying a plugin happens as the build script is evaluated. ## dependencyResolutionManagement ```kotlin dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { mavenCentral() } versionCatalogs { create("libs") { library("guava", "com.google.guava:guava:33.0.0-jre") } } } ``` This centralises dependency repositories and the **version catalog** — the type-safe `libs.guava` accessor available in every build script. `RepositoriesMode.FAIL_ON_PROJECT_REPOS` forbids `repositories { }` in build scripts, guaranteeing a single source of truth. (`PREFER_PROJECT` / `PREFER_SETTINGS` offer softer policies.) ## Why not per-project If each `build.gradle.kts` declared its own repos, you'd duplicate them across N projects, risk one project resolving from an untrusted repo, and lose the version catalog's single definition point. Centralising in settings yields determinism, governance (supply-chain control), and DRY configuration. That governance angle is why these blocks are *settings* responsibilities, not project ones. ## Mental model Structure + resolution policy → settings (init phase). Per-project behaviour that *uses* that policy → build scripts (configuration phase).

  • What does RepositoriesMode.FAIL_ON_PROJECT_REPOS enforce?
    It makes the build fail if any project declares its own repositories { } block, forcing all dependency repos to be defined once in settings — a single, governed source of truth.
  • Where is a version catalog (the libs accessor) defined and why there?
    In dependencyResolutionManagement.versionCatalogs in settings, so the type-safe accessor is generated once and available identically to every build script.
  • Why must plugin repositories be known before the configuration phase?
    Applying a plugin happens while a build script is evaluated during configuration; Gradle needs the plugin's resolution rules from settings (init phase) before it can fetch and apply it.

saying these in an interview costs you the question

  • Putting repositories { } in every build script when the team has standardised on settings-level resolution.
  • Claiming the version catalog is declared per-project.

context