What does it mean that Gradle's dependency resolution is 'variant-aware', and how does it differ from Maven's model?
answer
- component = bag of variants
- attributes = typed key/value
- Usage/Category/LibraryElements/TargetJvmVersion
- consumer requests, producer declares
- Maven: one artifact + scopes
basics
~20 sA component (like a library) publishes several variants — e.g. an API jar, a runtime jar, sources, javadoc. Gradle picks the right one by matching attributes (Usage, Category, etc.) to what the consumer asks for, instead of using one fixed jar like Maven.
solid answer
~40 sIn Maven a coordinate maps to a single artifact plus a flat `pom`-based scope model. Gradle instead treats a component as a set of **variants**, each described by **attributes** (typed key/value metadata such as `Usage=java-api` vs `java-runtime`, `Category`, `LibraryElements`, `TargetJvmVersion`). When you depend on a component, the resolution engine matches the **consumer's requested attributes** against the **producer's variant attributes** and selects exactly one variant for that graph edge. This lets a single dependency declaration resolve to different artifacts depending on context — compile classpath gets the API variant, runtime classpath gets the runtime variant, a test fixtures consumer gets the test-fixtures variant. Maven approximates this with separate classifiers and scopes but has no matching engine, so it cannot reason about JVM-version compatibility or pick an alternate variant automatically.
code
kotlin · 8 linesconfigurations.create("customCompile") {
isCanBeResolved = true
isCanBeConsumed = false
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, Usage.JAVA_API))
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
}
}go deeper
Know that a library can have several 'flavors' (API jar, runtime jar, sources) and Gradle picks one; name attributes loosely.
Define variants and the standard attributes (Usage, Category, LibraryElements, TargetJvmVersion) and contrast with Maven's single-artifact + scope model.
Explain the consumer-requests/producer-declares model and that selection runs per graph edge via the attributesSchema, plus the role of Gradle Module Metadata.
Frame variant-awareness as the mechanism enabling cross-project/cross-library compatibility guarantees (JVM targeting, platforms) and how it underpins org-wide dependency governance.
## What a variant is In Gradle, a published **component** (e.g. `com.example:lib:1.0`) is not a single jar. It is a bag of **variants**. A variant is a set of artifacts plus a set of **attributes** that describe what the variant is *for*. Examples of variants of one library: an `apiElements` variant (compile-time API), a `runtimeElements` variant (runtime classes + transitive deps), a `javadocElements` variant, a `sourcesElements` variant, and possibly a `testFixtures` variant. ## Attributes are typed metadata An **attribute** is a typed key with a value. Gradle ships standard ones: - `org.gradle.usage` (`Usage`): `java-api` vs `java-runtime` — is this for compiling against or running? - `org.gradle.category` (`Category`): `library`, `platform`, `documentation`, `enforced-platform`. - `org.gradle.libraryelements` (`LibraryElements`): `jar`, `classes`, `resources`, `aar`. - `org.gradle.jvm.version` (`TargetJvmVersion`): the minimum JVM the variant targets (e.g. 8, 17, 21). - `org.gradle.dependency.bundling`: `external` vs `shadowed`/`embedded`. Both sides participate: the **consumer** configuration (e.g. `compileClasspath`) carries *requested* attributes, and each **producer** variant carries *declared* attributes. ## How selection works During graph resolution, for each dependency edge Gradle runs **variant selection**: 1. Collect the consumer's requested attributes from the resolving configuration. 2. For the target component, list its variants and their attributes. 3. Use the **attributesSchema** to test each variant for **compatibility** (does it satisfy what the consumer wants?), then **disambiguation** (if several remain, which is best?). 4. Exactly one variant must win, otherwise resolution fails with an *ambiguous variant* or *no matching variant* error. The `attributesSchema` defines, per attribute, a **compatibility rule** (is value A acceptable when B is requested?) and a **disambiguation rule** (among acceptable candidates, prefer which?). For `TargetJvmVersion`, the built-in rule says a variant targeting JVM 8 *is compatible* with a consumer running on 17 (you can run older bytecode on a newer JVM), and disambiguation prefers the **highest** compatible version. That is why a multi-target library can ship a JVM-8 and a JVM-17 variant and the right one is chosen per consumer. ## Contrast with Maven Maven resolves a coordinate to one artifact (optionally a classifier) and models intent only through coarse **scopes** (compile/runtime/provided/test). There is no attribute-matching engine, so Maven cannot, for example, automatically pick a JVM-8 jar for a JVM-8 build and a JVM-17 jar for a JVM-17 build from the same coordinate. Gradle Module Metadata (the `.module` file) is what carries variant + attribute information so this works across published libraries. ```kotlin // A consumer configuration requests attributes; selection matches them to a variant val myConf = configurations.create("customCompile") { isCanBeResolved = true isCanBeConsumed = false attributes { attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, Usage.JAVA_API)) attribute( TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17 ) } } ```
- Which file format lets a published library expose multiple variants with attributes?Gradle Module Metadata — the `.module` JSON file published alongside the POM; it lists variants, their attributes, files, and dependencies.
- Why can the API and runtime classpaths resolve the same dependency to different artifacts?They are different consumer configurations with different `Usage` attributes (`java-api` vs `java-runtime`), so variant selection picks the `apiElements` vs `runtimeElements` variant respectively.
Maven hands you a single product off the shelf; Gradle is a vending machine that reads what you asked for (API vs runtime, which JVM) and dispenses the matching variant.
saying these in an interview costs you the question
- Claiming a Gradle component is always a single jar like in Maven.
- Saying attributes are just plain strings with no schema or compatibility rules.