Within a single dependency declaration, how do you turn off transitive resolution or exclude a specific transitive module, and what's the difference between the two?
answer
- configuring block on the declaration
- isTransitive=false drops ALL transitives
- exclude(group, module) drops ONE
- exclude can take group-only or module-only
- you re-declare needs after isTransitive=false
basics
~10 sPass a configuring block to the declaration. isTransitive = false drops ALL transitive dependencies of that module; exclude(group = ..., module = ...) removes only a specific transitive module while keeping the rest.
solid answer
~50 sBoth options are set in the **trailing configuration block** of a single dependency declaration, operating on the resulting `ModuleDependency`/`ExternalModuleDependency`. **`isTransitive = false`** tells Gradle to bring in *only* that module's own artifact and **none** of its transitive dependencies — a blunt, all-or-nothing switch. **`exclude(group = "...", module = "...")`** is surgical: it drops one (or a group of) transitive module(s) from that dependency's graph while keeping every other transitive. You can pass `group` only, `module` only, or both. Use `exclude` when one transitive conflicts or is unwanted (e.g. excluding `commons-logging` in favor of a bridge); use `isTransitive = false` only when you truly intend to manage every transitive yourself — it's brittle because you must then re-declare anything the module actually needs. Both are scoped to this declaration; they don't change global resolution. (Build-wide exclusions and richer transitivity strategy belong to other mechanisms — here we mean the per-declaration form.)
code
kotlin · 10 linesdependencies {
implementation("org.springframework:spring-context:6.1.5") {
// surgical: remove just commons-logging
exclude(group = "commons-logging", module = "commons-logging")
}
implementation("com.example:heavy-sdk:2.0") {
// blunt: no transitives at all
isTransitive = false
}
}go deeper
Know that a dependency can have a block where you can turn off transitives or exclude something; exact API not expected.
State isTransitive = false drops all transitives and exclude(group, module) drops a named one, with a correct example.
Contrast scope/risk, give a real exclude use case (commons-logging bridge), and explain the runtime-breakage risk of isTransitive = false.
Weigh per-declaration knobs against build-wide strategies (constraints, capabilities, substitution), and the maintainability cost of scattered excludes across a large multi-module build.
## Where these options live A dependency declaration accepts a trailing **configuration block** (closure in Groovy, lambda in Kotlin). Inside it `this` is the created dependency — a `ModuleDependency` (or `ExternalModuleDependency`) — exposing `isTransitive` and `exclude(...)`: ```kotlin dependencies { implementation("org.hibernate:hibernate-core:6.4.4.Final") { // surgical removal of ONE transitive module exclude(group = "org.jboss.logging", module = "jboss-logging") } implementation("com.example:fat-lib:1.0") { // drop ALL transitives of fat-lib isTransitive = false } } ``` ## `isTransitive = false` — all or nothing By default a module brings its declared transitive dependencies. Setting `isTransitive = false` on the declaration tells Gradle to resolve **only that module's own artifact** and ignore its entire transitive graph. Consequences: - You get a smaller, fully-controlled set — but you are now responsible for declaring *every* transitive the library genuinely needs at runtime, or you'll hit `ClassNotFoundException`/`NoClassDefFoundError`. - It's a blunt instrument; prefer it only when you intentionally vendor/manage the whole tree yourself. ## `exclude(group, module)` — surgical `exclude` removes specific transitive modules from *this* dependency's subgraph, keeping the rest: - `exclude(group = "commons-logging", module = "commons-logging")` — drop exactly that module. - `exclude(group = "com.example")` — drop *all* modules from that group (module omitted). - `exclude(module = "unwanted-lib")` — drop that module regardless of group. Typical reasons: a transitive conflicts with another version, you're swapping in a bridge/replacement (e.g. `jcl-over-slf4j` for `commons-logging`), or a transitive is unused and bloats the classpath. ## Key difference | | `isTransitive = false` | `exclude(...)` | |--|------------------------|----------------| | Scope | the dependency's **entire** transitive graph | **selected** transitive module(s) | | Granularity | all-or-nothing | per-group / per-module | | Risk | high — you must re-add real needs | lower — only removes what you name | | Use when | you fully manage the tree | one transitive is unwanted/conflicting | ## Both are per-declaration These options affect only **this** dependency declaration's view of the graph; they don't impose a build-wide rule. If two declarations pull the same library, you'd configure each (or use a higher-level mechanism). Within the *notation* topic, the point is: the configuring block is part of how you *declare* a dependency, and `isTransitive`/`exclude` are the two knobs you set there. ## Speakable summary "`isTransitive = false` cuts off the whole subtree; `exclude` snips a single branch. I default to `exclude` for a known conflict and reserve `isTransitive = false` for when I deliberately own the entire transitive set."
- What can go wrong after setting `isTransitive = false`?The module's required transitive dependencies are no longer on the classpath, so you can hit ClassNotFoundException/NoClassDefFoundError at runtime unless you re-declare each real need yourself.
- How do you exclude every module from a group rather than one specific module?Pass only `group` to `exclude`, e.g. `exclude(group = "com.example")` — omitting `module` matches all modules in that group.
- Why is `exclude` usually preferred over `isTransitive = false`?It's surgical — it removes only the named conflicting/unwanted transitive and keeps everything else the library legitimately needs, so it's far less likely to break the runtime classpath.
isTransitive = false is unplugging a power strip — everything downstream goes dark. exclude is flipping one outlet off while the rest stay powered.
saying these in an interview costs you the question
- Saying `exclude` and `isTransitive = false` are interchangeable — one is surgical, the other all-or-nothing.
- Forgetting that `isTransitive = false` forces you to manually supply the module's real runtime needs.
- Claiming a per-declaration exclude is build-wide — it only affects that declaration's subgraph.