skip to content

Measurement And Profiling

Measuring a build instead of guessing at it: build scans, the profile report, and the flags that show where time actually goes. Interviewers ask because tuning without measurement is the most common wasted effort.

on this pageshow

explore

questions

26

What is a Gradle Build Scan, and how do you publish one from a build?

level: juniorimportance: must knowfreq 62%

answer

  1. --scan flag
  2. shareable web report of one build
  3. settings.gradle Develocity plugin
  4. accept terms once
  5. prints scan URL

basics

~10 s

A Build Scan is a shareable web report of a build's results. You publish one by adding --scan to any Gradle command; Gradle uploads the data and prints a scan URL.

solid answer

~40 s

A **Build Scan** is a persistent, shareable web record of what happened during a single Gradle build — timings, executed tasks, applied plugins, dependencies, console output, and environment. You publish one ad hoc by appending `--scan` to any invocation, e.g. `./gradlew build --scan`. The first time, Gradle prompts you to accept the terms of service; afterward it uploads the data to `scans.gradle.com` (or your Develocity server) and prints a unique URL. For consistent publishing without the flag, apply the `com.gradle.develocity` (formerly `com.gradle.build-scan`) plugin in `settings.gradle(.kts)` and configure it to publish always or on failure. Scans are the primary tool for diagnosing *why* a build was slow or *why* it behaved differently on another machine, because they capture context a local log cannot.

code

bash · 6 lines
bash
# Ad hoc — uploads to the public server, prompts for terms once
./gradlew build --scan

# Output ends with:
# Publishing build scan...
# https://gradle.com/s/abcdef123456

go deeper

for a junior

Know that --scan publishes a shareable web report and prints a URL, and that terms must be accepted once.

for a middle

Explain both publishing paths (flag vs. Develocity plugin in settings), what the scan captures, and why settings is the required location.

for a senior

Discuss publish-on-failure-only policies, pointing scans at a self-hosted Develocity server, and tagging scans for later filtering.

for a principal

Frame scans as the data backbone for org-wide build observability and the migration path from public scans to Develocity.

## What a Build Scan is A **Build Scan** is a deep, persistent, web-hosted snapshot of one Gradle (or Maven) build invocation. Where the console gives you a transient stream of text, a scan is a structured, navigable record you can open later or send to a teammate via a URL. It captures: the full task execution timeline, which tasks ran vs. were `UP-TO-DATE`/`FROM-CACHE`/`SKIPPED`, applied plugins, the resolved dependency graph, the environment (OS, JVM, Gradle version, JVM args), console and deprecation output, and any failures with stack traces. ## Two ways to publish **1. Ad hoc with `--scan`.** Append the flag to any command: ```bash ./gradlew build --scan ``` The first run prompts you to accept the terms of service for the public `scans.gradle.com` server (a one-time, machine-local acceptance). On success Gradle prints a line like `Publishing build scan... https://gradle.com/s/abc123`. **2. Via the Develocity plugin.** For a team you don't want to rely on people remembering the flag. Apply the plugin in **`settings.gradle.kts`** (it must be in settings, not a build script, because it needs to hook the whole build lifecycle): ```kotlin plugins { id("com.gradle.develocity") version "3.x" } develocity { buildScan { termsOfUseUrl = "https://gradle.com/terms-of-service" termsOfUseAgree = "yes" publishing.onlyIf { true } // or { !it.buildResult.failures.isEmpty() } to publish only on failure } } ``` With terms pre-accepted in config, CI can publish non-interactively. ## Why it matters A scan answers questions a local log cannot: *why was this build slower on CI than on my laptop?*, *which task dominated wall-clock time?*, *did the cache miss, and on what input?* Because the data is captured uniformly and shareable, scans turn "works on my machine" debates into a side-by-side URL comparison. The free public server stores scans; **Develocity** is the self-hosted/commercial product that adds history, trends, failure analytics, and a private build cache. ## Naming note The old plugin id was `com.gradle.build-scan` and later `com.gradle.enterprise`; current Gradle 8.x uses `com.gradle.develocity` with a `develocity { buildScan { ... } }` block. The `--scan` flag works without any plugin for the public server.

  • Where must the Develocity/build-scan plugin be applied, and why there?
    In `settings.gradle(.kts)`, not a project build script. The plugin instruments the entire build lifecycle (settings + all projects), which can only be hooked from the settings phase that runs before any project is configured.
  • Does `--scan` require the plugin to be applied?
    No. The `--scan` flag publishes to the public `scans.gradle.com` server even with no plugin. The plugin is what lets you configure consistent/automatic publishing, terms pre-acceptance, custom tags, and a Develocity server URL.

saying these in an interview costs you the question

  • Claiming a Build Scan is just the console log saved to a file — it is structured, navigable build data, not raw text.
  • Trying to apply the scan/Develocity plugin in a project `build.gradle` instead of `settings.gradle`.

context

open as a page

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%

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).

open as a page

What does the --dry-run flag do when you run a Gradle build, and when would you use it?

level: juniorimportance: must knowfreq 60%

basics

~10 s

--dry-run makes Gradle print the ordered list of tasks it WOULD run for your requested goal, without actually executing any of them. It's a safe way to preview the task graph.

open as a page

What is the Gradle --profile HTML report and how do you generate one?

level: juniorimportance: must knowfreq 55%

basics

~10 s

Pass --profile to a build (e.g. ./gradlew build --profile). Gradle writes a self-contained HTML report to build/reports/profile/profile-<timestamp>.html showing where build time was spent.

open as a page

You've published a Build Scan for a slow build. How do you use the Timeline tab to find the single slowest task and understand why the overall build took so long?

level: juniorimportance: must knowfreq 60%

basics

~10 s

Open the Build Scan's Timeline tab, sort tasks by duration (longest first), and read the top entry. It shows each task's wall-clock time so you can see which task dominated the build.

open as a page

Walk me through reading a Build Scan: what do the timeline, performance, and dependencies pages tell you?

level: middleimportance: must knowfreq 48%

basics

~20 s

The Timeline lists every task with its duration and outcome (executed, up-to-date, from-cache). The Performance page breaks total build time into phases. The Dependencies page shows the resolved dependency graph so you can spot conflicts and where versions came from.

open as a page

How do you use `--profile` to determine whether your build's bottleneck is the configuration phase or the execution phase?

level: middleimportance: must knowfreq 60%

basics

~10 s

Run the build with --profile. Gradle writes an HTML report to build/reports/profile/ that breaks time into Configuration and Task Execution sections, plus per-project and per-task timings, so you can see which phase dominates.

open as a page

Walk through the sections of the --profile HTML report and what each one tells you.

level: middleimportance: must knowfreq 48%

basics

~10 s

The report has a Summary plus Configuration (per-project config time), Dependency Resolution (per-configuration resolve time), and Task Execution (per-task time with outcome). You read Summary first, then drill into the dominant phase.

open as a page

On a Build Scan, what's the difference between UP-TO-DATE, FROM-CACHE, NO-SOURCE, and SUCCESS outcomes, and how does the scan quantify the savings from avoidance?

level: middleimportance: must knowfreq 50%

basics

~20 s

SUCCESS = the task ran. UP-TO-DATE = incremental check passed, skipped. FROM-CACHE = outputs restored from the build cache. NO-SOURCE = no inputs to act on. The scan tallies avoided tasks and estimates the time saved.

open as a page

Walk me through the Performance tab of a Build Scan. What breakdown does it give you and how do you use it to decide where to optimize?

level: middleimportance: must knowfreq 55%

basics

~20 s

The Performance tab splits total build time into phases — startup, settings/configuration, and task execution — and breaks execution into serial vs parallel time. You use it to see whether time went into configuration or execution before drilling in.

open as a page

A team enabled the remote build cache to speed up their slow Gradle build but saw almost no improvement. As the senior engineer, how do you reason about what went wrong and what you measure next?

level: seniorimportance: must knowfreq 35%

basics

~20 s

The build cache only speeds up execution; if the bottleneck is the configuration phase, caching does nothing. Measure with --profile/--scan to get the config-vs-execution split first; if configuration dominates, the fix is configuration avoidance, not caching.

open as a page

How do you accept the Build Scan terms of service so that scans publish non-interactively (e.g., on CI)?

level: middleimportance: should knowfreq 40%

basics

~20 s

On a developer machine --scan prompts once and stores acceptance locally. On CI there is no prompt, so you pre-accept in config by setting termsOfUseUrl and termsOfUseAgree = "yes" in the Develocity buildScan block, or pass them as system properties.

open as a page

Why is resolving dependencies during the configuration phase a performance problem, and how would you detect it?

level: middleimportance: should knowfreq 45%

basics

~20 s

Configuration runs on every build, so resolving a dependency configuration there (e.g. iterating configurations.x.files in a build-script block) forces network/metadata work each time. Detect it via --profile's Dependency Resolution section or a build scan, which attribute that time to configuration.

open as a page

A teammate says they'll use --dry-run to confirm a build is fast and side-effect-free. Why is that the wrong tool, and what would you reach for instead?

level: middleimportance: should knowfreq 30%

basics

~20 s

--dry-run only previews the task graph; it still runs configuration and skips all execution, so it can't measure execution speed and won't reveal execution-time side effects. Use --profile (or task timing) to measure where time goes.

open as a page

What does the --profile flag do, and what kind of build slowness is it good at surfacing?

level: middleimportance: should knowfreq 45%

basics

~10 s

--profile makes Gradle write a timestamped HTML report under build/reports/profile/ that breaks the build down by phase — startup, settings/configuration, and task execution — so you can see where wall-clock time went.

open as a page

How do you make sure the numbers in a --profile report are trustworthy and reproducible?

level: middleimportance: should knowfreq 30%

basics

~10 s

Warm the daemon first, run the scenario more than once, control caches (clean vs incremental deliberately), keep the machine quiet, and compare like-for-like runs. The first cold run includes compilation/downloads and isn't representative.

open as a page

When would you publish to the public scan server versus a self-hosted Develocity instance, and how do you control publishing?

level: seniorimportance: should knowfreq 30%

basics

~20 s

The public server is fine for one-off, non-sensitive debugging. For a team you point scans at a self-hosted Develocity server (server = "https://...") so data stays private and is retained. You control publishing with publishing.onlyIf { ... } and --scan/--no-scan.

open as a page

When diagnosing a slow build, how does a build scan help you attribute time between configuration and execution more precisely than the console output alone?

level: seniorimportance: should knowfreq 40%

basics

~20 s

A build scan (--scan) records a full timeline and a Performance section that splits wall time into initialization, configuration, and execution, names the slowest projects/tasks, and flags issues like configuration-time resolution — detail the plain console log never shows.

open as a page

What does the -Dorg.gradle.internal.tasks.stats system property give you, and how does it differ from --profile?

level: seniorimportance: should knowfreq 25%

basics

~20 s

Setting -Dorg.gradle.internal.tasks.stats=true makes Gradle print, at the end of the build, an aggregate breakdown of task execution by task type and by outcome (executed, up-to-date, from-cache, skipped) — a text summary, not an HTML report.

open as a page

The profile report shows most time in the Configuration section. How do you interpret and act on that?

level: seniorimportance: should knowfreq 38%

basics

~10 s

High Configuration time means Gradle is spending too long running build scripts and configuring tasks every build. Reduce eager work, use lazy task registration, and enable the configuration cache so configuration is reused.

open as a page

When would you choose the --profile report over a Build Scan, and what are its limitations?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Use --profile when you need a quick, offline, no-setup measurement — e.g. air-gapped CI or no Gradle account. It's coarser than a Build Scan: wall-clock phase breakdown only, single build, no per-task cache-hit insights or sharing.

open as a page

A task you expected to be FROM-CACHE shows up as SUCCESS (a cache miss) in the Build Scan. How do you use the scan to diagnose why it missed?

level: seniorimportance: should knowfreq 45%

basics

~10 s

Open the task in the scan, check its outcome and cacheability, and compare its cache key/inputs against a previous build's scan. A changed input hash or a non-portable absolute path usually explains the miss.

open as a page

Using a Build Scan, how would you identify a serial bottleneck — a task or chain that prevents the build from parallelizing — and what does the critical path tell you?

level: seniorimportance: should knowfreq 40%

basics

~20 s

On the Timeline, look for a long stretch where one task runs while other worker lanes sit idle — that's a serial bottleneck. The critical path is the longest dependency chain; shortening it shortens the whole build.

open as a page

How would you set up a lightweight, account-free build-timing measurement step in CI using these flags, and what would you capture from it?

level: seniorimportance: nice to knowfreq 18%

basics

~10 s

Add a diagnostic build invocation with --profile to archive the HTML report as a CI artifact, and optionally -Dorg.gradle.internal.tasks.stats=true to log aggregated task-type/outcome stats. Capture the report and the log for trend review.

open as a page