skip to content

IDE Gradle Sync Semantics and Pitfalls

What an IDE sync really runs — initialization and configuration through the Tooling API — and why configuration-time side effects make sync slow or flaky. Interviewers ask because delegate-build settings and re-sync triggers confuse entire teams.

on this pageshow

questions

5

What actually happens when your IDE runs a 'Gradle sync', and why can it take much longer than just opening files?

level: juniorimportance: must knowfreq 70%

answer

  1. Tooling API builds models
  2. init + configuration, NOT execution
  3. evaluates every build script
  4. resolves dependency metadata
  5. config-time code runs on every sync

basics

~20 s

Sync runs the build's initialization and configuration phases through Gradle's Tooling API to discover projects, tasks, dependencies and source sets, then feeds that model to the IDE. It does not run your tasks, but it does execute build-script code.

solid answer

~40 s

An IDE Gradle sync launches a Gradle process via the **Tooling API** and asks it to build project models (e.g. `IdeaProject`/`GradleProject`). To produce those models Gradle must run the **initialization** phase (settings.gradle, included builds, project structure) and the **configuration** phase (every `build.gradle(.kts)` of every project is evaluated). It resolves dependency metadata so the IDE knows the classpath. Crucially it does **not** run the **execution** phase — your actual tasks don't run. It feels slow because configuration evaluates all build scripts, may download dependency metadata, applies plugins, and runs any config-time logic you wrote. The result is the IDE's model: modules, source sets, dependencies, task list. Anything you compute at configuration time (reading files, network calls) runs on every sync, which is why config-time side effects hurt.

code

kotlin · 7 lines
kotlin
tasks.register("hello") {
    // configuration time: runs during sync (task is registered)
    doLast {
        // execution time: NEVER runs during a sync
        println("Hello")
    }
}

go deeper

for a junior

Know that sync runs Gradle (not static parsing), evaluates build scripts, and produces the IDE's project/dependency model without running tasks.

for a middle

Map sync precisely to init+configuration phases; explain why config-time code runs on every sync and execution code never does.

for a senior

Discuss Tooling API model objects (IdeaProject/GradleProject), dependency-metadata resolution cost, and how to keep configuration cheap so syncs stay fast.

for a principal

Reason about sync cost across a large multi-module build, configuration-on-demand, and standards that forbid config-time side effects to protect every developer's sync time.

## What 'sync' means When IntelliJ IDEA (or Eclipse Buildship) imports a Gradle project, it does **not** parse your `build.gradle` as text. Instead it starts a Gradle daemon and connects to it through the **Tooling API** — a programmatic API for driving Gradle from another process. The IDE requests *model objects* such as `IdeaProject`, `GradleProject`, or custom models, and Gradle returns a structured description of the build. ## Gradle's three phases (and which ones sync runs) Gradle always proceeds through three phases: 1. **Initialization** — Gradle evaluates `settings.gradle(.kts)`, determines which projects participate (`include`, `includeBuild`), and creates a `Project` object for each. 2. **Configuration** — every project's `build.gradle(.kts)` is executed top to bottom. Plugins are applied, extensions configured, and the **task graph is built** (tasks are *registered/created* but not run). Dependency configurations are wired. 3. **Execution** — the selected tasks actually run. A sync runs **initialization + configuration**, resolves enough dependency metadata to form the classpath, and then **stops** — it never enters execution. That is why a sync compiles nothing and runs no tests, yet still executes all of your build-script code. ## Why it can be slow - Every project's build script is evaluated, every plugin applied. - Dependency *metadata* (POMs, module descriptors) is resolved/downloaded to build the IDE classpath. - Any logic you put at configuration time — `exec {}`, reading files, querying git, eager task creation — runs **on every sync**. ## The model the IDE consumes The returned model contains: the module/project hierarchy, source sets (main/test) and their output dirs, the dependency list per source set, and the available tasks. IntelliJ maps these onto IDE modules so code navigation, classpath, and the Gradle tool window stay accurate. ```kotlin // This runs on EVERY sync because it's at configuration time: val gitSha = providers.exec { commandLine("git", "rev-parse", "HEAD") }.standardOutput.asText.get() // BAD if eager — slows every sync ``` Knowing sync = init+config (not execution) explains most IDE pain: stale models, slow imports, and config-time side effects all live in those first two phases.

  • Does a sync compile your code or run tests?
    No. Sync stops after configuration; compilation and tests are execution-phase work and are not performed during sync.
  • Why does code inside doLast {} not run during sync?
    doLast is an execution-phase action. Sync registers/configures the task but never executes it, so doLast bodies are skipped.

Sync is like a building inspector walking every room and drawing the floor plan (configuration) without ever switching the machines on (execution).

saying these in an interview costs you the question

  • Saying sync 'compiles the project' — it does not enter the execution phase.
  • Believing the IDE parses build.gradle statically instead of running Gradle via the Tooling API.

context

open as a page

In IntelliJ IDEA, what is the difference between delegating build/run to Gradle versus using the IDE's own build, and what are the trade-offs?

level: middleimportance: must knowfreq 60%

basics

~20 s

Delegate-to-Gradle runs builds and tests through Gradle tasks, so behavior matches CI and honors all Gradle config. The IDE's native builder compiles with IntelliJ's own compiler and is often faster but can diverge from Gradle (custom tasks, processing, resources).

open as a page

Why are configuration-time side effects especially harmful in a build that gets synced by an IDE, and how do you avoid them?

level: seniorimportance: must knowfreq 45%

basics

~20 s

Configuration runs on every sync and every build invocation, so config-time side effects (exec calls, file/network reads, eager task creation) run constantly, slow syncs, and break the configuration cache. Move work to execution via doLast/task actions and use lazy Providers and tasks.register.

open as a page

What changes require a re-sync in the IDE, and how do you recognize and recover from a stale Gradle model?

level: middleimportance: should knowfreq 50%

basics

~20 s

Re-sync after editing build scripts, settings.gradle, version catalogs, or anything that changes dependencies, plugins, modules, or source sets. A stale model shows as wrong/missing dependencies, unresolved symbols, or modules that don't match the build. Re-sync (or refresh Gradle project) to rebuild the model.

open as a page

What is the Gradle Tooling API, and what models does an IDE typically request from it during sync?

level: seniorimportance: should knowfreq 30%

basics

~20 s

The Tooling API is Gradle's programmatic API for driving a build from another process and getting structured models back (instead of parsing output). IDEs use it to fetch project models like GradleProject, IdeaProject/EclipseProject, build environment info, and to run tasks with progress events.

open as a page