How does the order of declared repositories affect dependency resolution in Gradle?
answer
- top-to-bottom, first hit wins
- miss = must get 404 before next repo
- dependency confusion / shadowing
- content filtering beats ordering
- resolved repo is cached
basics
~20 sGradle searches repositories in the order you declare them and stops at the first one that has the module's metadata. So order determines which source wins when a module exists in more than one repo.
solid answer
~50 sWhen Gradle resolves a module, it queries the declared repositories **top-to-bottom** and uses the **first** one that can supply the module's metadata; it does not keep looking for a "better" copy. So if the same coordinates exist in two repos, the one declared earlier wins. This matters for both correctness and performance: a slow or rarely-hit repo placed first adds latency to every miss, because Gradle must get a definitive "not found" before moving on. It's also a supply-chain concern — declaring an untrusted repo before a trusted one lets it shadow legitimate modules (dependency confusion). The modern remedy is not careful ordering but **repository content filtering** (`exclusiveContent`, `content { includeGroup(...) }`), which pins each group to a specific repo so lookups are deterministic regardless of order. Order also interacts with caching: once resolved, the chosen repo is recorded so subsequent builds don't re-walk the list for that module.
code
kotlin · 9 linesrepositories {
// Searched first — internal modules resolve here, fast.
maven {
url = uri("https://nexus.corp/repo")
content { includeGroup("com.corp") }
}
// Public fallback for everything else.
mavenCentral()
}go deeper
State that repos are searched in order and the first match is used.
Explain miss-cost and that first-hit-wins shadows duplicates; mention content filtering as the fix.
Tie ordering to dependency-confusion risk and to lookup performance on misses; recommend filtering over manual ordering.
Mandate content filtering / exclusive content as policy so resolution is deterministic and order-independent across the org.
## Order-sensitive lookup Gradle treats the `repositories {}` block as an **ordered list**. To resolve `group:name:version`, it asks each repository, in declaration order, "do you have metadata for this module?" The **first** repository that answers yes supplies the module; Gradle then downloads the artifact from there and stops. It does not compare versions or contents across repositories to pick the "best" one — first hit wins. ## Why order matters ### 1. Correctness / shadowing If the same coordinates exist in two repos with different contents, the earlier-declared repo wins. An attacker who can publish to a repo you list *before* your trusted one can serve a malicious artifact under a legitimate name — the classic **dependency-confusion** attack. ### 2. Performance A **miss** is expensive: Gradle must receive a definitive negative (HTTP 404, or absent metadata) from a repository before trying the next. A slow or large public repo declared first taxes every lookup that isn't satisfied there. General guidance: put the repository most likely to contain your modules first, and keep the list short. ## The better answer: content filtering Relying on order is fragile. Gradle lets you constrain *which* repo answers for *which* groups: ```kotlin repositories { mavenCentral() maven { url = uri("https://my.company/repo") content { includeGroup("com.mycompany") } // only ask this repo for our groups } exclusiveContent { forRepository { google() } filter { includeGroupByRegex("com\\.google\\..*") } } } ``` With filtering, lookups become **deterministic by content** rather than by accident of ordering, and Gradle can skip repos entirely for groups they can't serve — faster *and* safer. (The mechanics of content filtering are a topic of their own; here the point is that it supersedes manual ordering.) ## Caching interaction After a successful resolve, Gradle remembers which repository served a module, so later builds don't re-walk the whole list for it. A `--refresh-dependencies` run or cache eviction reopens the ordered search. ## Practical rules - Order trusted/internal repos deliberately; never list an untrusted repo before a trusted one. - Prefer content filtering to fix both determinism and speed. - Keep the repo list minimal — every entry is a potential lookup cost on a miss.
- Why is relying on repository order a security risk?If an untrusted repo is declared before a trusted one and can host the same coordinates, it shadows the real module — a dependency-confusion attack. Content filtering pins groups to repos to prevent this.
- Does Gradle compare versions across repositories to pick the newest?No. It takes the first repository that has the requested module's metadata; it does not scan all repos for a better/newer copy.
- How does a repository 'miss' affect build time?Gradle must receive a definitive not-found (e.g. 404) before trying the next repo, so a slow repo placed early adds latency to every unsatisfied lookup.
saying these in an interview costs you the question
- Saying Gradle picks the highest version across all repositories.
- Claiming order has no effect on resolution.
- Ignoring the dependency-confusion risk of repo ordering.