In our previous lesson, we established that Gradle configurations are named "buckets" for dependencies, allowing you to manage different logical scopes like implementation or api. This abstraction is what saves you from manually managing multiple, complex classpaths.
Today, we will add another layer of precision to this model. You'll learn that not all configurations are created equal; they serve one of three distinct roles in the dependency management lifecycle. The goal of this lesson is to distinguish between declarable, resolvable, and consumable configurations. Mastering this taxonomy is key to understanding how dependencies flow from your build script to a compiled application, and from one project to another.
The Three Roles: A Conceptual Overview
At a high level, the three types of configurations define a dependency pipeline:
-
Declarable Configurations: These are the "buckets" you directly interact with in your
dependenciesblock. Their sole purpose is to receive dependency declarations.implementation,api, andcompileOnlyare primary examples. Think of these as the inputs to the system. -
Resolvable Configurations: These configurations are for internal use by the project. Gradle resolves them to get a concrete set of files (JARs, etc.) to form a classpath for a specific task, like compiling or running code.
compileClasspathandruntimeClasspathare classic examples. Think of these as the internal wiring of your build. -
Consumable Configurations: These configurations are used to expose or publish artifacts and their dependencies to other projects. When Project A depends on Project B, Project A resolves its dependencies against Project B's consumable configurations.
apiElementsandruntimeElementsare the standard examples. Think of these as the outputs or public interface of your project's dependencies.
This diagram from the Gradle documentation illustrates the flow between these configuration types within a typical Java project.

Formal Definitions and Standard Examples
To formalize these concepts, let's turn to the official Gradle documentation. It provides clear definitions and lists the standard configurations created by the Java plugin, which should be familiar to you.
Creating Dependency Configurations - Gradle User Manual
This document formally introduces the three configuration roles and provides concrete examples from the Java plugin.
First, read the introductory section Declarable, Resolvable, and Consumable to get the official definitions for each type. Next, skip down to the "Configurations added by Plugins" section and read the part about the Java Plugin. Pay close attention to how it categorizes familiar configurations like implementation (declarable), compileClasspath (resolvable), and apiElements (consumable).
The Mechanism: canBeDeclared, canBeResolved, canBeConsumed
As an engineer, you'll appreciate that these roles aren't just conceptual; they are enforced by a set of boolean flags on each configuration object. Understanding these flags reveals the underlying mechanics of how Gradle ensures each configuration is used correctly. By default, for legacy reasons, all flags are true, but modern plugins and build scripts should be explicit.
canBeDeclared = true: This is a declarable configuration (a bucket).canBeResolved = true: This is a resolvable configuration (for a classpath).canBeConsumed = true: This is a consumable configuration (for publishing).
A well-defined configuration should only have one of these roles. The documentation provides a concise table summarizing this.
Creating Dependency Configurations - Gradle User Manual
This section explains the flags that control the role of a configuration.
Please read the short section titled Configuration flags and roles. The table clearly lays out how the flags correspond to the roles we've discussed.
A Practical View: Modern APIs and Naming Conventions
The concepts of declarable, resolvable, and consumable configurations are so fundamental that modern Gradle APIs provide factory methods to create them directly, making the intent clear. The blog post below offers a practical perspective on this, connecting the concepts to the APIs you would use when writing a custom plugin.
The author also uses a helpful diagram that shows the inheritance relationship between the configurations, which complements the flow diagram we saw earlier.

Now, let's read the article to see how this plays out in code.
Configuration roles and the blogging-industrial complex
This article provides a great developer-centric view on configuration roles and introduces the modern, explicit APIs for creating them.
Read the first two main sections of the article. Start at What are Configurations, which recaps the three roles and contrasts the legacy creation method with the modern API. Then, continue to the next section and review the code example under the plugin implementation. This demonstrates how the dependencyScope(), resolvable(), and consumable() methods are used to create configurations for each role, following the naming convention seen in the diagram (sourceFiles, sourceFilesClasspath, sourceFilesElements).
As you can see, the API design and naming conventions (..., ...Classpath, ...Elements) are a direct reflection of this three-part model, making it a powerful mental framework for both using and creating Gradle plugins.
Tying It All Together
To bring all these pieces together, let's revisit a short segment from one of the videos you watched in the previous lesson. The presenter does an excellent job of summarizing these three roles while referencing the inheritance diagram.
Understanding Gradle #08 – Declaring Dependencies
This brief clip from the "Declaring Dependencies" video explicitly names and defines the three configuration types using the diagram we've just discussed.
Please re-watch the segment from 10:08 to 10:36. This will serve as a final reinforcement of the terminology: declarable (green), resolvable (blue), and consumable (pink/red).
Conclusion
In this lesson, we dissected the concept of configurations into three distinct roles, providing you with a structured model for understanding dependency flow in Gradle.
Here are the key takeaways:
- Declarable Configurations (e.g.,
implementation): Act as input "buckets" where you declare dependencies in your build script. - Resolvable Configurations (e.g.,
compileClasspath): Are for internal use, resolved by Gradle to build classpaths for tasks. - Consumable Configurations (e.g.,
apiElements): Act as outputs, exposing a project's artifacts and dependencies to external consumers. - This separation of roles is enforced by flags (
canBeDeclared,canBeResolved,canBeConsumed) and is reflected in modern Gradle APIs (dependencyScope(),resolvable(),consumable()).
In our next lesson, we will dive deeper into the relationships between configurations. We will focus on how the extendsFrom keyword is used to create inheritance hierarchies, allowing you to build complex dependency graphs in a clean and maintainable way.