skip to content

When and how would you declare multiple named version catalogs in one build, and what are the trade-offs?

level: middleimportance: nice to knowfreq 28%

answer

  1. container → many create(name)
  2. name = accessor root prefix
  3. no merging across catalogs
  4. split on ownership/source boundary
  5. default to one libs

basics

~10 s

Call create("name") once per catalog inside versionCatalogs; each produces its own accessor root (e.g. libs.*, testLibs.*). Use it to separate concerns, but it adds cognitive overhead, so most builds keep a single libs.

solid answer

~50 s

`versionCatalogs` is a container, so you can `create` several catalogs in the same settings file, each with a distinct name that becomes its accessor prefix. For example one `libs` for production dependencies and a `testLibs` from a different TOML or a published catalog. Reasons to split: separating internal/shared catalogs from local ones, importing a published org catalog alongside a project-local one, or isolating a large plugin set. The cost is real: contributors must remember which prefix holds what, IDE auto-complete is spread across roots, and overlap between catalogs invites confusion. Gradle does not merge catalogs, so a dependency declared in two catalogs exists twice under different prefixes. The common guidance is to default to a single `libs` and introduce extra catalogs only when there's a concrete sharing or ownership boundary — e.g. a corporate catalog imported by `from("g:a:v")` plus a project `libs` for local-only additions.

code

kotlin · 11 lines
kotlin
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") { from(files("gradle/libs.versions.toml")) }
        create("corpLibs") { from("com.acme:corp-catalog:3.4") }
    }
}
// build.gradle.kts
dependencies {
    implementation(libs.guava)
    implementation(corpLibs.logging)
}

go deeper

for a junior

Know more than one catalog is possible and the name becomes the prefix.

for a middle

Declare several catalogs and articulate when the split is justified versus noise.

for a senior

Weigh discoverability and no-merge semantics; recommend single-catalog defaults with deliberate exceptions.

for a principal

Set an org convention for catalog topology — e.g. one published corp catalog plus a thin local catalog per repo.

## Multiple catalogs are allowed `dependencyResolutionManagement { versionCatalogs { ... } }` exposes a *container*. Each `create(name) { ... }` registers an independent catalog, and the **name is the accessor root** in build scripts: ```kotlin dependencyResolutionManagement { versionCatalogs { create("libs") { from(files("gradle/libs.versions.toml")) } create("corpLibs") { from("com.acme:corp-catalog:3.4") } create("testLibs") { from(files("gradle/test.versions.toml")) } } } ``` Now build scripts use `libs.guava`, `corpLibs.logging`, `testLibs.junit` — three separate namespaces. ## When it earns its keep - **Ownership boundary**: a centrally-published `corpLibs` (governed by a platform team) sits beside a project-local `libs` that teams edit freely. - **Source boundary**: one catalog comes from a file, another from a published artifact — they can't be a single `from`, so separate catalogs is the natural split. - **Scope isolation**: keeping a sprawling set of plugin/test entries out of the main namespace. ## The costs - **Discovery**: developers must know which prefix holds a given library; this is the biggest tax in practice. - **No merging**: Gradle never combines catalogs. The same coordinate declared in two catalogs is two separate aliases; there is no de-duplication or override across catalogs. - **Tooling**: accessors are generated per catalog, so auto-complete and refactors are scoped to each root. ## Guidance Default to a single `libs`. Reach for additional catalogs only when a real boundary (ownership, source, or scope) exists — most commonly a published corporate catalog imported via `from("g:a:v")` alongside a small project-local `libs`. Avoid splitting purely by category (ui/db/test) inside one repo; bundles and a single namespace usually serve better and keep the cognitive model simple.

  • If two catalogs declare the same library coordinate, what happens?
    Nothing special — Gradle does not merge catalogs. You simply have two aliases under different prefixes pointing at the same coordinate; there is no conflict, override, or de-duplication between catalogs.
  • What's a clean reason to use two catalogs rather than one?
    Different sources/ownership: a published, centrally-governed catalog imported with `from("g:a:v")` alongside a project-local `libs` from a TOML file — since one `from` can't combine a published artifact and a local file.

saying these in an interview costs you the question

  • Assuming Gradle merges or overrides entries across multiple catalogs.
  • Splitting catalogs by feature category (ui/db) inside one repo — usually adds overhead without a real boundary.

context