skip to content

What do the allprojects{} and subprojects{} blocks do when placed in a root build.gradle.kts, and how do they differ?

level: juniorimportance: must knowfreq 70%

answer

  1. root Project helper methods
  2. allprojects = root + children
  3. subprojects = children only
  4. this = target project inside block
  5. eager configuration-phase iteration

basics

~10 s

Both apply configuration to many projects at once from the root build. allprojects{} includes the root plus every subproject; subprojects{} applies only to the child projects, excluding the root.

solid answer

~40 s

`allprojects {}` and `subprojects {}` are configuration blocks Gradle exposes on the root `Project`. Each takes an action that Gradle runs against a set of projects so you can cross-configure them in one place — typically applying a plugin, setting a `group`/`version`, or declaring common repositories and dependencies. The difference is which projects are in the set: `allprojects {}` iterates the root project **and** all of its descendants; `subprojects {}` iterates only the descendants and skips the root. You pick `subprojects {}` when the root is just an aggregator with no production code, and `allprojects {}` when even the root needs the setting (e.g. a shared `group`). Both are shorthand for calling `project.allprojects(action)` / `project.subprojects(action)`, which configure the targets eagerly during the configuration phase.

code

kotlin · 13 lines
kotlin
// root build.gradle.kts
allprojects {
    group = "com.example.shop"
    version = "2.3.1"
}

subprojects {
    apply(plugin = "java-library")
    repositories { mavenCentral() }
    dependencies {
        "testImplementation"("org.junit.jupiter:junit-jupiter:5.10.0")
    }
}

go deeper

for a junior

State the basic split: allprojects = root + all subprojects, subprojects = subprojects only, and that both let you configure many modules from the root.

for a middle

Explain that this is the target project, give a concrete use (apply java plugin + shared repos), and know the quoted-configuration gotcha.

for a senior

Frame them as eager helpers on the root Project, contrast with convention plugins, and note the coupling cost.

for a principal

Position cross-configuration as an anti-pattern at scale and discuss migration strategy to convention plugins for large monorepos.

## What they are In a Gradle multi-project build there is one **root project** (defined by `settings.gradle.kts`) and zero or more **subprojects** (declared with `include(":app", ":lib")`). Every project gets its own `Project` object and, usually, its own `build.gradle.kts`. The root `Project` exposes two helper methods that let you configure many projects from one file: - `allprojects(action)` — runs `action` against **this project and every project beneath it** (root + all subprojects). - `subprojects(action)` — runs `action` against **every project beneath this one** (subprojects only; the root is excluded). Written as DSL blocks in the root `build.gradle.kts`: ```kotlin allprojects { group = "com.example" version = "1.0.0" } subprojects { apply(plugin = "java") repositories { mavenCentral() } dependencies { "testImplementation"("org.junit.jupiter:junit-jupiter:5.10.0") } } ``` ## The key distinction The ONLY difference is the target set: | Block | Root project | Subprojects | |-------|-------------|-------------| | `allprojects {}` | yes | yes | | `subprojects {}` | no | yes | Use `subprojects {}` for things that only the *code* modules need (the `java` plugin, test dependencies) when the root is a pure aggregator. Use `allprojects {}` for things the root also needs — most commonly a shared `group` and `version` coordinate, or shared `repositories`. ## How it executes These are **not** declarative config that Gradle merges lazily. When the root build script runs during the configuration phase, `allprojects`/`subprojects` immediately schedule the action to run against each target project. Inside the block `this` is the *target* `Project`, so `apply(...)`, `dependencies {}`, `tasks` etc. all refer to that project, not the root. Because string-keyed methods like `dependencies` need the configuration to exist, you often see the quoted form `"implementation"(...)` in the root script — the `implementation` configuration is added by the `java` plugin you apply in the same block, so the typed accessor isn't generated for the root script. ## Why people reach for them They remove duplication: instead of repeating `repositories`, `group`, and common plugins in ten subproject scripts, you write it once. The trade-off is coupling — the root script now reaches into every child, which is exactly what convention plugins later replace.

  • Why might you write `"testImplementation"(...)` in quotes inside a subprojects block instead of the usual `testImplementation(...)`?
    The typed `testImplementation` accessor is generated only for scripts where the plugin that creates that configuration is applied directly. In the root script the configuration is added to the *target* project at runtime, so the root script has no generated accessor and you must reference it by its string name.
  • If you want a setting on every subproject except the root, which block do you use?
    `subprojects {}` — it excludes the root and applies only to descendants.

saying these in an interview costs you the question

  • Saying allprojects excludes the root — it includes the root.
  • Claiming these blocks apply lazily/declaratively; they iterate eagerly during configuration.

context