skip to content

Explain gradle.beforeProject and gradle.afterProject. How do they differ from a project calling its own afterEvaluate?

level: middleimportance: should knowfreq 45%

answer

  1. Gradle-object scope, all projects
  2. settings/init script declaration
  3. afterProject gets failure state
  4. beforeProject sets conventions
  5. afterEvaluate = single project

basics

~10 s

gradle.beforeProject and gradle.afterProject register callbacks that fire around the evaluation of EVERY project in the build, receiving the project as an argument. afterEvaluate is scoped to the single project that registers it.

solid answer

~50 s

Both `gradle.beforeProject { project -> }` and `gradle.afterProject { project -> }` are **build-wide** lifecycle hooks declared on the `Gradle` object (typically in `settings.gradle(.kts)` or an init script). `beforeProject` fires just before each project's build script is evaluated; `afterProject` fires just after — for **every** project in the build, with that `Project` passed in. By contrast, `project.afterEvaluate { }` is registered on one specific project and only fires for that project. So `gradle.afterProject` is effectively 'afterEvaluate for all projects', useful for cross-cutting setup applied from a central place (settings/init script) without touching each subproject's build file. `afterProject` also receives a failure parameter so you can react when a project failed to evaluate. These hooks run during the configuration phase and, like afterEvaluate, are subject to configuration-cache constraints — listeners that capture mutable build state are problematic.

code

kotlin · 7 lines
kotlin
// init.gradle.kts — applies to every project in every build
gradle.beforeProject { project ->
    project.repositories.mavenCentral()
}
gradle.afterProject { project ->
    println("Evaluated ${project.path}")
}

go deeper

for a junior

Know they are build-wide versions of the evaluation hooks that receive each project.

for a middle

Explain scope difference vs afterEvaluate and that they're declared in settings/init scripts; note afterProject's failure argument.

for a senior

Discuss when to centralize via gradle.*Project vs a convention plugin, and ordering relative to beforeProject/evaluate/afterProject.

for a principal

Weigh these against configuration-cache compatibility and maintainability; recommend convention plugins + lazy wiring over broad init-script hooks in shared org tooling.

## The two scopes of evaluation hooks Gradle exposes project-evaluation hooks at **two scopes**: - **Per-project**: `project.beforeEvaluate { }` and `project.afterEvaluate { }` — registered on one `Project`, fire only for that project. - **Build-wide**: `gradle.beforeProject { project -> }` and `gradle.afterProject { project, state -> }` — registered on the `Gradle` object, fire for **every** project, receiving the project (and for `afterProject`, a `ProjectState` describing success/failure). ## Where you declare the build-wide ones Because they apply to all projects, you declare them where you have the `Gradle` object before project scripts run — typically `settings.gradle.kts` or an **init script** (`~/.gradle/init.gradle.kts`). This lets a central place inject behavior into every project without editing each subproject build file. ```kotlin // settings.gradle.kts gradle.afterProject { project -> project.tasks.matching { it.name == "test" }.configureEach { // tweak every project's test task centrally } } ``` ## beforeProject vs afterProject - `beforeProject` runs **before** a project's build script is evaluated — handy to set conventions/defaults that the script can then override. - `afterProject` runs **after** evaluation — handy to react to whatever the script declared, or to detect evaluation failures via the `ProjectState`/failure argument. ## Relationship to afterEvaluate `gradle.afterProject` is conceptually `afterEvaluate` broadcast to every project. If you only need the behavior for one project, `project.afterEvaluate` is more targeted and clearer. If you need it everywhere from a single location (a convention plugin, settings script, or init script), the `gradle.*Project` hooks avoid repeating registration per project. ## Ordering & failure handling - For a given project, the sequence is roughly: `beforeProject` → evaluate build script → `afterProject` (and the project's own `afterEvaluate` callbacks fire as part of completing evaluation). - `afterProject` is told whether the project evaluated successfully, so you can centralize error reporting. ## Modern caveats These are eager, mutable-state-touching hooks. Under the **configuration cache**, listeners that capture and mutate live build state across projects are discouraged; prefer lazy wiring and convention plugins. Treat `gradle.*Project` as a powerful but legacy-leaning mechanism, mostly seen in init scripts and enterprise setups.

  • Where would you typically register gradle.afterProject?
    In settings.gradle(.kts) or an init script, since those have the Gradle object available before/while project scripts are evaluated and apply build-wide.
  • What extra information does afterProject give you compared to afterEvaluate?
    The project instance (since it fires for all projects) plus a ProjectState/failure indicator so you can detect whether that project failed to evaluate.

saying these in an interview costs you the question

  • Saying gradle.afterProject only fires for the root project — it fires for every project.
  • Confusing beforeProject (per evaluation, before script) with the initialization phase.
  • Claiming these are the recommended modern approach — they're legacy-leaning and clash with convention-plugin/lazy patterns.

context