skip to content

Configuration Phase

Evaluating every project's build script to construct the task model, and why all projects are configured even when you asked for one task. Interviewers ask because this explains why a large build feels slow before it compiles anything.

on this pageshow

questions

5

What happens during Gradle's configuration phase, and what is its output?

level: juniorimportance: must knowfreq 70%

answer

  1. every build.gradle.kts evaluated top to bottom
  2. builds the task model
  3. script body runs; task actions do NOT
  4. runs on every invocation
  5. register/configure tasks, apply plugins

basics

~20 s

Gradle 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 s

After 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 lines
kotlin
tasks.register("hello") {
    println("configuring hello")  // configuration phase
    doLast { println("running hello") }  // execution phase
}
println("script body")            // configuration phase

go deeper

for a junior

State the three phases in order and that configuration evaluates build scripts to build the task model. Show you know task actions run later.

for a middle

Distinguish script body / register block (configuration) from task action (execution) with a concrete example and explain the per-invocation cost.

for a senior

Connect default eager configuration to build performance and the need for configuration avoidance; reason about what work belongs where.

for a principal

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.

context

open as a page

How does choosing `tasks.register` over `tasks.create` affect the configuration phase?

level: middleimportance: must knowfreq 65%

basics

~20 s

tasks.create eagerly builds and configures the task during the configuration phase, even if it's never run. tasks.register defers that work until the task is actually needed, so unused tasks cost almost nothing at configuration time.

open as a page

How do you tell whether a piece of build-script code runs at configuration time or execution time, and why does it matter?

level: middleimportance: must knowfreq 60%

basics

~20 s

Code directly in the script body or in a task's configuration block runs at configuration time, on every build. Code inside doFirst/doLast/@TaskAction runs at execution time, only when the task runs. It matters because configuration-time work slows every command.

open as a page

Why does Gradle configure every project on every build by default, and what problems does that cause?

level: middleimportance: should knowfreq 50%

basics

~20 s

By default Gradle evaluates all participating projects' build scripts so it has a complete, consistent task model and can resolve cross-project dependencies. The cost is that even unrelated subprojects are configured on every command, slowing builds in large repos.

open as a page

A team reports that even `gradle help` is slow in their large monorepo. How would you diagnose and reduce configuration-phase time?

level: seniorimportance: should knowfreq 40%

basics

~10 s

Since help runs no real tasks, slowness is configuration-phase cost. Profile with --profile/build scans, find eager create, script-body I/O and .get() calls, switch to lazy register/Provider APIs, and enable the configuration cache.

open as a page