How do allprojects { } and subprojects { } interact with configuration ordering, and why can configuration injection be deferred safely?
answer
- closure applied per-project at its evaluation
- deferred => order-safe
- trap: reading another project inside the block
- convention plugins preferred
- isolated projects forbids cross-config
basics
~10 sallprojects/subprojects in the root register configuration that Gradle applies to each project when that project is evaluated. It's deferred per project, so it doesn't depend on a fixed evaluation order.
solid answer
~40 s`allprojects { }` and `subprojects { }` are typically declared in the **root** build script. They don't run immediately against other projects; they register a configuration **closure** that Gradle applies to each matching project **when that project is evaluated**. So even though the root is evaluated first, the injected config takes effect lazily as each subproject's configuration phase runs — order among subprojects still being undefined doesn't break it. The pitfall is *eagerly* reaching into a specific other project inside these blocks (e.g. `subprojects { val v = project(":other").version }`), which reintroduces the ordering hazard. Modern guidance actually discourages cross-project configuration injection altogether (it conflicts with isolated projects); convention plugins applied per-project are preferred. But for ordering specifically: the closures themselves are deferred and order-safe.
code
kotlin · 7 lines// root build.gradle.kts
subprojects {
apply(plugin = "org.jetbrains.kotlin.jvm")
// safe: configures THIS project, applied lazily per project
tasks.withType<Test>().configureEach { useJUnitPlatform() }
// AVOID: version = project(":core").version // eager cross-project read
}go deeper
Know allprojects/subprojects apply shared config to projects; don't need the deferral nuance.
Explain that the closure is applied per project at its own evaluation, making it order-safe, and name the eager-read trap.
Tie it to isolated projects and recommend convention plugins over cross-project injection.
Set a migration path off allprojects/subprojects toward convention plugins for isolation-ready builds.
## What the blocks do ```kotlin // root build.gradle.kts allprojects { // root + every subproject repositories { mavenCentral() } } subprojects { // every subproject, excluding root apply(plugin = "java") } ``` These register a **configuration action**. Gradle invokes that action for each project **at the time the project is configured**, not when the root script runs. That deferral is what makes them order-safe: it doesn't matter which subproject Gradle evaluates next, because the closure is applied to whichever project is currently being evaluated. ## Why ordering doesn't bite (usually) Because each project's injected config runs during *its own* configuration, the undefined inter-project order is irrelevant for the injection itself. You aren't reading another project — you're configuring *this* one. ## The trap It becomes fragile the moment you read a *different* project inside the block: ```kotlin subprojects { // BAD: reads :core which may not be configured yet version = project(":core").version } ``` Now you're back to the eager-cross-project problem and undefined order. Fix with a provider, or define the shared value in `settings`/a `gradle.properties`/a convention plugin so no cross-project read is needed. ## Modern direction Gradle now recommends moving away from `allprojects`/`subprojects` cross-configuration toward **convention plugins** in `buildSrc` or an included build, applied explicitly per project. This is required for **isolated projects**, where each project configures in isolation and cannot reach into others. So while the closures are order-safe, the pattern itself is being phased out in favor of per-project convention plugins.
- Why are convention plugins preferred over subprojects { } today?They are explicit per-project, compose better, and are compatible with isolated projects, which parallelizes/isolates configuration and forbids the root reaching into other projects.
- Is it safe to apply a plugin inside subprojects { }?Functionally yes — it's applied when each project configures — but it's discouraged versus per-project convention plugins for isolation and clarity.
saying these in an interview costs you the question
- Saying subprojects { } runs immediately against all projects when the root evaluates.
- Reading another specific project inside allprojects/subprojects without realizing the ordering hazard.
- Claiming cross-project configuration injection is the recommended modern pattern.