If you declare both mavenLocal() and mavenCentral(), how does Gradle decide which one supplies a dependency, and why does order matter?
answer
- declared order, first match wins
- fixed version stops at first repo
- mavenLocal first = shadowing
- cacheChangingModulesFor for snapshots
- content filtering scopes mavenLocal
basics
~10 sGradle tries declared repositories in order until one provides the requested version. With mavenLocal() listed first, a matching local artifact is used before Central is ever queried — which can shadow the remote one.
solid answer
~50 sGradle resolves a given module by querying declared repositories **in declaration order** and taking the first that has the requested version's metadata. So if `mavenLocal()` comes before `mavenCentral()` and `~/.m2` already has `com.example:lib:1.0.0`, that local copy is used and Central is never consulted for it. Reorder them and Central wins. This is why a stale local publish can silently shadow the real artifact, and why `mavenLocal()` is dangerous early in the list. Note this is *not* about picking the newest version per repo — for a fixed requested version, Gradle stops at the first repo that supplies it. For dynamic/snapshot versions Gradle may consult multiple repos to find candidates, and `cacheChangingModulesFor` governs how often a `-SNAPSHOT` is re-checked. The pragmatic rule: put trusted remotes first and add `mavenLocal()` last (or only behind a dev flag) so it can supply *missing* coordinates without overriding present ones.
code
kotlin · 7 linesrepositories {
mavenCentral()
// Scope local to only your own group so it can't shadow third-party libs
mavenLocal {
content { includeGroupByRegex("com\\.example\\..*") }
}
}go deeper
Know repositories are tried in order and the first match is used.
Explain that mavenLocal-first can shadow remotes and ordering it last mitigates that.
Distinguish fixed vs dynamic vs changing resolution, cacheChangingModulesFor, and content filtering to scope mavenLocal.
Codify org-wide repository ordering/filtering conventions to prevent supply-chain and shadowing risks.
## Repository order is significant Gradle's `repositories {}` block is an **ordered** list. When resolving a module at a specific version, Gradle queries each repository in turn and uses the **first** one whose metadata satisfies the request; it does not merge or compare across repos to pick a 'best' source for a fixed version. ```kotlin repositories { mavenLocal() // queried first mavenCentral() // only consulted if local lacks the version } ``` With this order, an artifact sitting in `~/.m2` is used before Central is touched — the shadowing risk. Flip the order and the local copy is only a fallback for coordinates Central doesn't have. ## Fixed versions vs dynamic/changing - **Fixed version** (`1.0.0`): first repo that has it wins; resolution stops there. - **Dynamic version** (`1.+`, `latest.release`): Gradle may query multiple repos to enumerate candidate versions, then applies conflict resolution to pick the highest. - **Changing module** (`-SNAPSHOT`, or `isChanging = true`): the artifact may be refreshed periodically. `cacheChangingModulesFor` (default 24h) controls how stale a cached snapshot may be before Gradle re-checks the source repository. `mavenLocal()` snapshots are not timestamp-rotated, so they persist until overwritten. ## Practical guidance - Put **trusted remotes first**, `mavenLocal()` **last** (or gate it behind a property like `-PuseMavenLocal`). - Use `--refresh-dependencies` to force Gradle to ignore cached metadata and re-resolve, which helps when a stale local or snapshot artifact is suspected. - Prefer **repository content filtering** to make intent explicit: ```kotlin repositories { mavenLocal { content { includeGroup("com.example.myinternal") } // only my own coords from local } mavenCentral() } ``` That restricts `mavenLocal()` to specific groups, eliminating accidental shadowing of third-party libraries while still allowing fast local iteration on your own modules.
- How can you stop mavenLocal from accidentally supplying third-party libraries?Use repository content filtering — `mavenLocal { content { includeGroup("com.example") } }` — so only your own coordinates are eligible from the local repo.
- For a fixed version, does Gradle pick the newest across repos?No — it queries in order and uses the first repository that has that exact version; it does not compare versions across repos for a fixed request.
- How do you force a re-check of a possibly stale snapshot?Run with `--refresh-dependencies`, or rely on `cacheChangingModulesFor` expiry (default 24h) for changing modules.
saying these in an interview costs you the question
- Claiming Gradle always merges all repos and picks the newest version for a fixed coordinate.
- Putting mavenLocal first without realizing it shadows remotes.