skip to content

Configuration Roles

Resolvable, consumable, and dependency-scope bucket roles, the flags that define them, and the extendsFrom hierarchy that links them. Interviewers ask to see whether you understand the model underneath the familiar configuration names.

on this pageshow

questions

5

You try to resolve `implementation` and get an error that it cannot be resolved. Why, and what should you resolve instead?

level: juniorimportance: must knowfreq 50%

answer

  1. implementation = bucket, not resolvable
  2. isCanBeResolved = false
  3. resolve compileClasspath / runtimeClasspath
  4. extendsFrom inherits the bucket
  5. declare vs resolve roles

basics

~10 s

implementation is a declaration bucket — isCanBeResolved is false — so it has no resolution result. Resolve compileClasspath or runtimeClasspath, which extend from it.

solid answer

~40 s

`implementation` is a **dependency-scope bucket**: both `isCanBeResolved` and `isCanBeConsumed` are false. Its only job is to *hold* declared dependencies, not to produce a file set, so asking for its resolved artifacts errors with 'Resolving configuration 'implementation' directly is not allowed'. The configurations you actually resolve are `compileClasspath` and `runtimeClasspath`, which are resolvable and `extendsFrom` the relevant buckets (`implementation`, `compileOnly`/`runtimeOnly`, `api`). So if you need the files, ask the right resolvable configuration — e.g. `configurations.runtimeClasspath.get().resolve()` or wire a task input to it. This protects you from accidentally resolving a half-defined set and from the classic mistake of treating one configuration as both the place you declare and the thing you resolve.

code

kotlin · 5 lines
kotlin
// WRONG: implementation is a bucket
// configurations.implementation.get().resolve()  // ERROR

// RIGHT: resolve the resolvable sibling
val runtimeFiles = configurations.runtimeClasspath.get().resolve()

go deeper

for a junior

Recognize that buckets can't be resolved and name compileClasspath/runtimeClasspath as the right targets.

for a middle

Explain the flag mechanics and the extendsFrom inheritance that feeds the classpath.

for a senior

Push toward lazy wiring (Provider as task input) instead of eager .resolve(); tie to configuration cache.

for a principal

Use this to justify role discipline in convention plugins so build authors never resolve buckets across the org.

## The error Gradle refuses `configurations.implementation.get().resolve()` (or `.files`) with a message like *'Resolving configuration 'implementation' directly is not allowed'*. This is by design. ## Why Every configuration has two booleans: - `isCanBeResolved` — may Gradle compute artifacts for it? - `isCanBeConsumed` — may other projects depend on it? A **bucket** (dependency-scope) like `implementation` has *both false*. It exists only so you can write `dependencies { implementation("...") }`. Resolving means running dependency resolution + variant selection to get concrete files — a bucket deliberately has no such result. ## What to resolve instead The Java plugin creates resolvable configurations that inherit the buckets: - `compileClasspath` — resolvable; `extendsFrom(implementation, compileOnly, api, ...)`. - `runtimeClasspath` — resolvable; `extendsFrom(implementation, runtimeOnly, api, ...)`. So declare in the bucket, resolve the classpath: ```kotlin val files = configurations.runtimeClasspath.get().resolve() // OK ``` ## Wiring into tasks (preferred) Rather than calling `.resolve()` eagerly, wire the resolvable configuration as a task input so resolution is lazy and cache-friendly: ```kotlin tasks.register("listRuntime") { val cp = configurations.runtimeClasspath doLast { cp.get().forEach { println(it.name) } } } ``` ## Takeaway 'Cannot be resolved' is not a bug — it tells you that you reached for the *declaration* role when you wanted the *resolution* role. Pick the resolvable sibling.

  • How does `runtimeClasspath` get the dependencies you declared in `implementation`?
    Through `extendsFrom`: `runtimeClasspath` extends `implementation` (and `runtimeOnly`, `api`, etc.), so declarations in the bucket flow up into the resolvable configuration.
  • Is there a downside to calling `.resolve()` directly in configuration scripts?
    Yes — it triggers eager resolution at configuration time, hurting performance and configuration cache. Prefer wiring the configuration (a Provider) as a lazy task input.

saying these in an interview costs you the question

  • Claiming the error is a Gradle bug or a missing dependency.
  • Trying to flip `isCanBeResolved = true` on `implementation` to 'fix' it instead of resolving the right configuration.

context

open as a page

What are the three roles a Gradle configuration can play, and what does each one do?

level: middleimportance: must knowfreq 55%

basics

~10 s

A configuration can be a dependency-scope (bucket) that collects declared dependencies, resolvable (you ask it to compute a classpath), or consumable (exposed as a variant to other projects).

open as a page

Explain `extendsFrom` and walk through how a declaration in `api` ends up on a downstream consumer's compile classpath.

level: middleimportance: should knowfreq 30%

basics

~10 s

extendsFrom makes one configuration inherit the dependencies declared in another. api is included in both apiElements (exposed) and compileClasspath, so its dependencies propagate to consumers' compile classpaths.

open as a page

How do you create configurations with explicit roles in modern Gradle, and why use the role-locking factories?

level: seniorimportance: should knowfreq 35%

basics

~10 s

Use the role-specific factories on the configurations container: dependencyScope("..."), resolvable("..."), and consumable("..."). They lock the role and the flags so the configuration can't be misused.

open as a page

What do `isCanBeResolved` and `isCanBeConsumed` mean, and what is wrong with a configuration where both are true?

level: seniorimportance: should knowfreq 28%

basics

~10 s

isCanBeResolved = Gradle can compute its artifacts for this build; isCanBeConsumed = other projects can depend on it. Both true is the deprecated legacy 'do-everything' role and should be split.

open as a page