What is the difference between `components.withModule(...)` and `components.all(...)`, and when would you choose each?
answer
- withModule = one group:name
- all = every component
- all must be fast/pure, bail early
- class ref enables @CacheableRule + @Inject
- rules compose in registration order
basics
~10 swithModule("group:name", Rule) runs the rule only for that one module. all(Rule) runs it for every resolved module. Prefer withModule when you know the target; use all only for broad, cheap, generic patches.
solid answer
~40 sBoth register a `ComponentMetadataRule` in the `components` block, but they differ in scope. `withModule("com.example:lib", MyRule::class)` targets exactly one `group:name` — the rule's `execute` is only invoked for versions of that module. `all(MyRule::class)` invokes the rule for **every** component resolved in the build. Use `withModule` whenever you're fixing a known, specific dependency — it's precise, self-documenting, and minimizes overhead. Reserve `all` for genuinely cross-cutting concerns: e.g. inspecting every module to normalize a `status` attribute, or detecting a class of metadata problems across the graph. Because `all` fires for thousands of components, the rule must be fast, pure, and side-effect-free, and should bail out early for modules it doesn't care about. In both cases mark the rule `@CacheableRule` so results are cached per module version.
code
kotlin · 8 linesdependencies {
components {
// targeted: only this module
withModule("com.google.collections:google-collections", GuavaCapabilityRule::class)
// broad: inspect/patch every resolved module
all(NormalizeStatusRule::class)
}
}go deeper
Know withModule targets one module and all targets every module.
Justify the choice by precision and cost; know all must be fast/pure/cacheable and bail early.
Discuss class-ref vs inline-action tradeoffs (cacheability, injectable params) and rule composition/ordering.
Reason about graph-wide all rules as a maintainability/perf risk at org scale and prefer targeted rules shipped via shared plugins.
## Two registration entry points Inside `dependencies { components { ... } }` Gradle offers several registration methods; the two scope-defining ones are: - **`withModule(id, ruleClass)`** — `id` is a `group:name` coordinate (no version). The rule runs only for resolved versions of that module. - **`all(ruleClass)`** — the rule runs for *every* component that gets resolved anywhere in the build. Both have overloads taking a class reference (preferred, enables `@CacheableRule` + `@Inject` params) or an inline `Action<ComponentMetadataDetails>` (handy for quick one-offs, but not cacheable). ## Choosing **Use `withModule` when** you know the offending coordinate: "`com.example:logging-lib` forgets to declare slf4j", "`com.fasterxml.jackson.core:jackson-databind` should belong to a Jackson virtual platform". It documents intent and only runs where needed. **Use `all` when** the concern is cross-cutting and you can't enumerate targets up front — e.g. attaching a derived attribute to every module, or scanning for a metadata smell. Treat `all` like a hot loop: it executes for every node in the graph, so: - Return early for modules you don't touch (`if (id.group != "x") return`). - Do no I/O, no environment reads — keep it deterministic. - Mark it `@CacheableRule` so Gradle caches the (often no-op) result per module version. ```kotlin @CacheableRule abstract class NormalizeStatusRule : ComponentMetadataRule { override fun execute(context: ComponentMetadataContext) { // runs for EVERY module — keep it cheap context.details.statusScheme = listOf("integration", "milestone", "release") } } dependencies { components { all(NormalizeStatusRule::class) // broad withModule("com.example:lib", AddDepRule::class) // targeted } } ``` ## Ordering and composition Multiple rules can apply to the same module; they execute in registration order, and `all` rules and matching `withModule` rules all run. Keep them composable: each rule should make one focused change so the combined effect is predictable.
- Why can an `all` rule hurt build performance if written carelessly?It executes for every component in the graph (potentially thousands). If it does I/O, allocates heavily, or isn't `@CacheableRule`, that cost multiplies across modules and re-runs. Bail out early and keep it pure/cacheable.
- Can you pass the rule as an inline action instead of a class?Yes — `withModule("g:a") { withDependencies { ... } }` takes an `Action`. It's convenient but cannot be `@CacheableRule` or take `@Inject` params, so for anything non-trivial use a class reference.
saying these in an interview costs you the question
- Defaulting to `all` for a fix that targets a single known module — it's wasteful and obscures intent.
- Putting filesystem/network reads inside an `all` rule.