What happens during Gradle's configuration phase, and what is its output?
answer
- every build.gradle.kts evaluated top to bottom
- builds the task model
- script body runs; task actions do NOT
- runs on every invocation
- register/configure tasks, apply plugins
basics
~20 sGradle evaluates every project's build script top to bottom, running the build-script code to register and configure tasks. The output is the task model — the set of tasks Gradle knows about and how they depend on each other.
solid answer
~40 sAfter initialization, Gradle enters the **configuration phase**. It evaluates each project's `build.gradle.kts` by executing the script as code from top to bottom. This applies plugins, declares dependencies and configurations, and registers/configures tasks — building the in-memory **task model** (the full set of `Task` objects and their dependency wiring). Importantly, configuration runs the build-script logic itself, not the task *actions*; a task's `doLast {}` block runs later, in the execution phase. The output of configuration is a complete, internally consistent task model from which Gradle can next build the task graph for the requested tasks. Because this code runs on *every* build invocation regardless of which task you asked for, expensive work placed at configuration time (file I/O, network calls, eager task creation) slows down *every* command — this is the motivation for configuration avoidance.
code
kotlin · 5 linestasks.register("hello") {
println("configuring hello") // configuration phase
doLast { println("running hello") } // execution phase
}
println("script body") // configuration phasego deeper
State the three phases in order and that configuration evaluates build scripts to build the task model. Show you know task actions run later.
Distinguish script body / register block (configuration) from task action (execution) with a concrete example and explain the per-invocation cost.
Connect default eager configuration to build performance and the need for configuration avoidance; reason about what work belongs where.
Frame configuration cost as a multi-project, org-scale concern: how monorepo build conventions and lazy APIs keep configuration time bounded as the project count grows.
## Where configuration sits A Gradle build runs in three ordered phases: 1. **Initialization** — Gradle decides which projects take part (reads `settings.gradle.kts`) and creates a `Project` instance for each. 2. **Configuration** — the focus here. 3. **Execution** — the selected tasks' actions actually run. ## What the configuration phase does Gradle takes each `Project` and **evaluates its build script** by executing the script body as ordinary Kotlin/Groovy code, top to bottom. During this evaluation: - `plugins { }` / `apply(plugin = ...)` applies plugins, which themselves register tasks and extensions. - `dependencies { }` and `configurations { }` declare the dependency model. - `tasks.register(...)` / `tasks.create(...)` add tasks to the project's `TaskContainer`. - Any configuration block on a task (`tasks.named("x") { ... }` or the closure passed to `register`) runs to set the task's inputs, outputs and properties. The result is the **task model**: the complete set of `Task` instances for all participating projects, with their properties set and their `dependsOn` / input-output wiring recorded. ## What does NOT run here A frequent point of confusion: the *body of the build script* runs at configuration time, but a task's **action** (`doFirst {}` / `doLast {}` / `@TaskAction` method) runs at **execution** time. So: ```kotlin tasks.register("hello") { println("A: configuring hello") // runs at CONFIGURATION time doLast { println("B: executing hello") // runs at EXECUTION time } } println("C: script body") // runs at CONFIGURATION time ``` Run `gradle help` (which does not execute `hello`) and you still see `A` and `C` — because configuration evaluates the script and the register block — but not `B`. ## Why every project is configured by default Unless configuration avoidance / lazy APIs are used, Gradle eagerly evaluates **all** projects' build scripts on **every** invocation, even ones unrelated to the task you asked for. That guarantees a complete task model but means configuration cost is paid on every build. This is exactly why you should avoid heavy work (eager task creation with `create`, file system scans, resolving configurations, network calls) at configuration time. ## The output, restated The deliverable of the configuration phase is a fully-built, validated task model. Gradle then moves on to construct the task graph (DAG) for the requested tasks and finally execute them.
- If you run `gradle help`, does the `doLast {}` of an unrelated task run?No. `doLast` is a task action that only runs at execution time if the task is in the executed graph. But the surrounding `register` block and the script body still run, because configuration evaluates every script.
- Why is putting `println` directly in the script body considered a configuration-time side effect?Because the script body is executed during the configuration phase on every build, so the println fires every time regardless of which task you requested — unlike code inside a task action.
saying these in an interview costs you the question
- Saying task actions (doLast/@TaskAction) run during configuration — they run during execution.
- Claiming only the requested task's project is configured by default — by default all projects are configured.
- Confusing configuration with execution as a single phase.