skip to content

How do you declare a variable in a build.gradle Groovy script with def, and how does its scope differ from a project property?

level: middleimportance: should knowfreq 40%

answer

  1. def = untyped script-local
  2. ext.x = shared extra property
  3. subprojects see ext, not def
  4. foo='x' without def/ext -> unknown property error
  5. version catalog preferred for versions

basics

~20 s

def x = '1.0' declares a local, dynamically-typed variable visible only within that script. To share a value across the project or subprojects you set it as an ext/project property (e.g. ext.appVersion = '1.0'), which is reachable elsewhere.

solid answer

~40 s

`def` is Groovy's untyped local declaration: `def libVersion = '33.0.0-jre'`. A `def` variable is a **script-local** — it lives only in the build script that declares it and isn't visible to subprojects, other scripts, or task closures evaluated elsewhere. For values you need to share you use **extra properties** via the `ext` namespace: `ext.appVersion = '1.0'`, then read it as `appVersion` or `project.appVersion`. Extra properties attach to the `Project` (or any `ExtensionAware` object) and propagate to subprojects through `allprojects`/`subprojects`. A subtle trap: assigning `foo = 'x'` without `def` and without a prior `ext.foo` declaration is an error (you can't dynamically create a top-level property), whereas `def foo = 'x'` is fine but local. So: `def` for throwaway local helpers, `ext` for cross-script/cross-module sharing.

code

groovy · 10 lines
groovy
def libVersion = '33.0.0-jre'   // local to this script only

ext {
    appVersion = '1.0.0'        // shared extra property
}

dependencies {
    implementation "com.google.guava:guava:${libVersion}"
}
version = appVersion            // ext property readable here / in subprojects

go deeper

for a junior

Know def x = ... is a local variable and that ext is used to share values.

for a middle

Contrast script-local def vs project-attached ext properties and the unknown-property error.

for a senior

Explain propagation via allprojects/subprojects and recommend version catalogs over ext for versions.

for a principal

Govern where shared config lives (catalog, convention plugins, gradle.properties) to keep large builds consistent and discoverable.

## def — dynamic local declaration In Groovy, `def` declares a variable without a static type; the JVM type is inferred at runtime: ```groovy def libVersion = '33.0.0-jre' def deps = ['a', 'b'] ``` In a build script a `def` variable is a **local variable of the script object**. Its scope is the lexical block where it's declared. Critically, it is **not** added to the project model: subprojects, applied script plugins, and lazily-evaluated task actions in other contexts cannot see it. (Referencing a script-local from a `doLast` closure in the *same* script is fine, because the closure captures the surrounding scope.) ## Extra (ext) properties — the shared namespace When you need a value visible beyond the current script you use **extra properties**, exposed through the `ext` namespace on any `ExtensionAware` object — most often the `Project`: ```groovy ext { appVersion = '1.0.0' javaVersion = 21 } // read anywhere on the project: version = appVersion ``` Extra properties live on the project's `ExtraPropertiesExtension`. In a multi-module build you commonly set them in the root and read them in subprojects: ```groovy allprojects { ext.orgGroup = 'com.acme' } // subproject build.gradle: group = orgGroup ``` ## The 'undeclared property' trap Writing `foo = 'x'` with no `def` and no existing property tries to **set** a property named foo on the project; if it doesn't exist Gradle throws `Could not set unknown property 'foo'`. You must first declare it via `ext.foo` (or use `def` for a local). Reading an unknown property similarly throws `MissingPropertyException`. ## Choosing between them - `def` — a private helper inside one script: a computed path, a temporary list, a loop variable. - `ext` — configuration that downstream scripts/subprojects must read. - Modern practice favors a **version catalog** (`libs.versions.toml`) over `ext` for dependency versions, but `ext` is still common for ad-hoc cross-module values. ## Why it matters Mixing these up causes 'works in this file but not the subproject' confusion. Knowing `def` is local and `ext` is the shared, model-attached namespace lets you place values at the right scope and avoid the unknown-property errors.

  • Why might a def variable work in the root build.gradle but be invisible to a subproject?
    def declares a script-local; it isn't attached to the project model. Subprojects only see values placed on the project, e.g. via ext properties or allprojects/subprojects blocks.
  • What happens if you write appVersion = '1.0' without def and without ext.appVersion first?
    Gradle tries to set an existing property named appVersion; if none exists it throws 'Could not set unknown property'. Declare it with ext first, or use def for a local.

saying these in an interview costs you the question

  • Saying a def variable is visible to subprojects.
  • Claiming you can assign any new top-level property without declaring it via ext.

context