What actually happens when your IDE runs a 'Gradle sync', and why can it take much longer than just opening files?
answer
- Tooling API builds models
- init + configuration, NOT execution
- evaluates every build script
- resolves dependency metadata
- config-time code runs on every sync
basics
~20 sSync 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 sAn 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 linestasks.register("hello") {
// configuration time: runs during sync (task is registered)
doLast {
// execution time: NEVER runs during a sync
println("Hello")
}
}go deeper
Know that sync runs Gradle (not static parsing), evaluates build scripts, and produces the IDE's project/dependency model without running tasks.
Map sync precisely to init+configuration phases; explain why config-time code runs on every sync and execution code never does.
Discuss Tooling API model objects (IdeaProject/GradleProject), dependency-metadata resolution cost, and how to keep configuration cheap so syncs stay fast.
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.