skip to content

When would you use `configurations.all { exclude(...) }` instead of a per-dependency exclude, and what are the trade-offs?

level: middleimportance: should knowfreq 55%

answer

  1. configurations.all { exclude(...) }
  2. many paths -> one global rule
  3. blunt: affects future deps too
  4. log bridge swap (commons-logging)
  5. runtime ClassNotFound risk

basics

~10 s

Use a configuration-wide exclude when an unwanted artifact arrives through many dependency paths. It removes that coordinate from every dependency in the configuration at once, but it's a blunt, global rule.

solid answer

~40 s

A per-dependency exclude only prunes one dependency's subgraph, so when the same junk (e.g. `commons-logging`) is dragged in by several libraries you'd need to repeat the exclude everywhere. Instead apply it once at the configuration level: `configurations.all { exclude(group = "commons-logging", module = "commons-logging") }` removes that coordinate from *every* resolution. The trade-off is bluntness — it affects all current and future dependencies in those configurations, so if some component genuinely needs that artifact it will break, possibly with a `ClassNotFoundException` at runtime rather than a build error. You can scope it more tightly by targeting one configuration (`configurations.named("runtimeClasspath")`). Prefer it for cross-cutting log-bridge replacements (swapping `commons-logging` for `jcl-over-slf4j`), and document the reason since the cause is now far from any single declaration.

code

kotlin · 9 lines
kotlin
configurations.all {
    // commons-logging arrives through many paths; drop it everywhere...
    exclude(group = "commons-logging", module = "commons-logging")
}

dependencies {
    // ...and route JCL calls through SLF4J instead
    implementation("org.slf4j:jcl-over-slf4j:2.0.13")
}

go deeper

for a junior

Recognize the syntax and that it applies to all dependencies, not just one.

for a middle

Explain when to choose it (many paths bring the same junk) and the bluntness/runtime-failure trade-offs.

for a senior

Scope to specific configurations, prefer capability resolution/constraints for competing implementations, and document the reason.

for a principal

Define org policy: minimize global excludes, centralize log-bridge handling and version alignment in a convention plugin or platform so individual builds don't reinvent fragile rules.

## The problem it solves Per-dependency `exclude` is precise but local. If five libraries each transitively bring `commons-logging`, you must add the exclude to all five — easy to miss, and brittle as dependencies change. A **configuration-wide exclude** applies one rule across an entire configuration's resolution. ## How to declare it Gradle resolves dependencies through *configurations* (e.g. `implementation`, `runtimeClasspath`, `testRuntimeClasspath`). `configurations.all { ... }` iterates every configuration, including ones created later, so the exclude is truly global: ```kotlin configurations.all { exclude(group = "commons-logging", module = "commons-logging") } ``` To limit the blast radius, target a single configuration instead: ```kotlin configurations.named("runtimeClasspath") { exclude(group = "org.slf4j", module = "slf4j-simple") } ``` ## Trade-offs - **Reach:** it hits *all* dependencies in scope, present and future. That's exactly why it's used for log-bridge swaps, but it can over-prune. - **Distance from cause:** the rule lives in a global block, far from any one dependency. Six months later it's unclear *why* the artifact is missing — so document with a comment. - **Runtime failures:** removing something a component needs typically surfaces only at runtime (`ClassNotFoundException`), not as a resolution error, because Gradle happily resolves the pruned graph. ## Classic use case: logging bridges The canonical example is replacing `commons-logging` with the SLF4J bridge `jcl-over-slf4j`: globally exclude `commons-logging`, then add `jcl-over-slf4j` so the same API is satisfied by your chosen backend. The same pattern applies to `log4j` -> `log4j-over-slf4j`. ## Alternatives to consider first Before a broad exclude, weigh **dependency constraints**, a **platform/BOM** for version alignment, or **capabilities** (`resolutionStrategy.capabilitiesResolution`) which is the modern way to resolve mutually-exclusive implementations of the same capability — often cleaner than excludes.

  • How can you make a configuration-wide exclude less risky?
    Scope it to a specific configuration (e.g. `configurations.named("runtimeClasspath")`) instead of `all`, and prefer capability resolution or constraints when the goal is choosing between competing implementations rather than deleting something outright.
  • Why might a configuration-wide exclude cause a runtime error instead of a build error?
    Gradle resolves the pruned graph successfully — the class is simply absent. If some code path needs it, you only find out when that code runs and throws ClassNotFoundException/NoClassDefFoundError.

saying these in an interview costs you the question

  • Reaching for `configurations.all` excludes as the first tool for any conflict instead of constraints/capabilities.
  • Leaving global excludes undocumented so nobody knows why an artifact is missing.

context