Gradle Basics Interview Questions

BUILD & TOOLING › Gradle

Walk me through what happens, step by step, running ./gradlew assembleDebug for the first time versus running it again with no changes. Where does the time go, and why is the second run faster?

What a strong answer covers: The first run has to evaluate every build script in the configuration phase, resolve the full dependency graph including downloading anything not cached, then execute the task graph with cold up-to-date checks. On the second run with no changes, the configuration cache can skip re-running the build scripts entirely if their inputs are unchanged, and the build cache or task-level up-to-date checks let most tasks short-circuit rather than redo real work, so time shifts almost entirely into cheap up-to-date verification.

You've got a large multi-module app and builds have gotten slow. Walk me through how you'd diagnose the bottleneck before reaching for a fix.

What a strong answer covers: Start with a build scan or --profile to see where time actually goes, per task and per module, rather than guessing; check whether the configuration phase itself is slow, which can happen even for a single-module build if too many modules get configured eagerly. Also look for tasks that aren't cache-hitting due to non-deterministic inputs, and check whether overly broad api dependencies are forcing unnecessary downstream recompilation; jumping straight to 'add more modules' or 'enable parallel' without profiling first is the common mistake.

Explain why using api instead of implementation for a dependency you don't strictly need to expose can quietly slow down incremental builds, even though the app still compiles fine either way.

What a strong answer covers: api puts that dependency on the compile classpath of every module that transitively depends on you, so Gradle has to consider all of those downstream modules potentially invalidated when that dependency's ABI changes, whereas implementation hides it and confines recompilation to your own module. Over-using api effectively removes the compile-avoidance boundary between modules, so a change in a leaf dependency can trigger a much larger recompilation cascade than the code actually warrants, even though nothing downstream directly referenced that type.

Back to Gradle Basics