skip to content

What is the difference between Gradle's configuration phase and execution phase, and why does the distinction matter when a build feels slow?

level: juniorimportance: must knowfreq 70%

answer

  1. init -> configuration -> execution
  2. configuration runs on every build (even help)
  3. configuration = build the task graph
  4. execution = run task actions, skippable/cacheable
  5. diagnose phase before optimizing

basics

~20 s

Configuration builds the task graph by evaluating build scripts; execution runs the selected tasks' actions. A build can be slow because the graph takes long to build (configuration) or because the tasks themselves take long (execution).

solid answer

~40 s

Gradle runs in phases: **initialization** (decide which projects participate), **configuration** (evaluate every relevant build script and create/configure the task objects, building the task graph), and **execution** (run the actions of the tasks you asked for plus their dependencies). The distinction matters because the two phases have completely different fixes. Slow configuration means script evaluation is expensive — e.g. eager task creation, resolving dependencies at configuration time, or heavy plugin logic — and it costs you on *every* invocation, even `gradle help`. Slow execution means the actual work (compilation, tests, packaging) is expensive and is helped by caching, incremental builds, or parallelism. Before optimizing you must measure which phase dominates, because shaving execution time does nothing if configuration is the bottleneck.

code

bash · 4 lines
bash
# If even a no-op task is slow, configuration is the suspect:
gradle help --profile
# open build/reports/profile/profile-<timestamp>.html and compare
# the 'Configuration' total vs the 'Task Execution' total

go deeper

for a junior

Name the phases and state that configuration builds the task graph while execution runs the tasks.

for a middle

Explain that configuration runs on every invocation and that the fix differs per phase, so measurement must come first.

for a senior

Connect each phase to concrete causes and tools (--profile, scans) and to the right optimization lever (avoidance vs caching/parallelism).

for a principal

Frame phase cost as a fleet-wide developer-productivity tax and reason about configuration cache and incremental adoption strategy across many modules.

## The three phases of a Gradle build Every Gradle invocation runs through three phases in order: 1. **Initialization** — Gradle reads `settings.gradle(.kts)` and decides which projects are part of the build, creating a `Project` instance for each. 2. **Configuration** — Gradle evaluates the build script of every project that participates and *configures* the tasks: it creates the task objects and runs the configuration code (the body of `tasks.register { ... }` for tasks that get realized, repository/dependency declarations, plugin `apply` logic). The output of this phase is the **task graph** (a directed acyclic graph of tasks and their dependencies). 3. **Execution** — Gradle takes the tasks you requested on the command line, computes their transitive dependencies, and runs each task's *actions* (the `doLast`/`doFirst` blocks and the `@TaskAction` method) in dependency order, skipping any that are `UP-TO-DATE` or `FROM-CACHE`. ## Why the split matters for performance The key insight: **configuration runs on essentially every build, regardless of which task you invoke.** Even `gradle help` or `gradle tasks` pays the full configuration cost (unless configuration avoidance / configuration cache is in play). So if configuration is slow, *every* developer command is slow — there is no escaping it with `--offline` or caching task outputs. Execution time, by contrast, is the cost of the real work and is highly *avoidable*: up-to-date checks skip tasks whose inputs didn't change, the build cache reuses outputs from previous runs or other machines, and `--parallel` runs independent tasks concurrently. The practical consequence: **diagnose before you optimize.** If your 40-second build spends 30 seconds configuring, adding the build cache buys you almost nothing. Conversely if it spends 35 seconds compiling, rewriting eager task creation won't help. ## Telltale signs of each - **Slow configuration:** `gradle help` is slow; the delay happens *before* any task lines print; build scans show a large "configuration" slice; common causes are eager `tasks.create`, dependency resolution in the configuration block, or expensive plugin/`buildSrc` logic. - **Slow execution:** `gradle help` is fast but real tasks are slow; the time is attributed to specific tasks; helped by caching, incremental compilation, and parallelism. ## How to tell them apart The two primary tools are the `--profile` HTML report (a per-phase, per-task breakdown written to `build/reports/profile/`) and **build scans** (`--scan`), which show a phase-by-phase timeline. Both clearly attribute time to configuration vs execution. (The lazy-API *fix* for slow configuration — `tasks.register` and configuration avoidance — is a separate topic; here the goal is identifying which phase is the problem.) ```text Initialization -> Configuration -> Execution (which (build the (run the projects) task graph) task actions) ```

  • Does running `gradle help` execute any of your real tasks?
    No — `help` runs almost no work, but it still pays the full initialization and configuration cost, which makes it a clean probe for configuration-phase slowness.
  • Which phase does the build cache speed up?
    Execution. The build cache reuses task *outputs*, so it only helps tasks that actually run during execution; it does nothing for configuration time.

Configuration is planning the route on a map for every trip you take; execution is actually driving. If planning the route takes 20 minutes every time, a faster car (caching) won't help your commute.

saying these in an interview costs you the question

  • Claiming the build cache or `--parallel` will fix slow configuration — they only affect execution.
  • Thinking configuration only runs for the project containing the requested task (it configures all participating projects unless avoidance/configuration-cache is active).

context