skip to content

What are Gradle's extra (ext) properties, and how do you declare and read them in a build script?

level: juniorimportance: must knowfreq 55%

answer

  1. ExtensionAware -> ExtraPropertiesExtension
  2. ext {} (Groovy) vs by extra (Kotlin)
  3. dynamic, untyped Map<String,Object>
  4. typo fails at execution time
  5. project.ext / project.extra["k"]

basics

~20 s

Extra properties are arbitrary user-defined key/value pairs attached to a Gradle object. You set them with ext { key = value } (Groovy) or by extra (Kotlin) and read them back by name later in the script.

solid answer

~40 s

Every Gradle domain object (Project, Task, etc.) implements `ExtensionAware`, which exposes an `ExtraPropertiesExtension` (`ext`). Extra properties are dynamic, untyped key/value pairs you attach at runtime — Gradle has no compile-time knowledge of them. In Groovy you declare them via `ext { myVersion = '1.2' }` and read with `myVersion` or `project.myVersion`. In Kotlin you use a delegate: `val myVersion by extra("1.2")` to declare, and `val myVersion: String by extra` to read in another script. They're useful for sharing ad-hoc values (versions, flags) across a build. Their main weakness is the lack of type safety and the fact that a typo on read fails only at execution time.

code

kotlin · 10 lines
kotlin
// build.gradle.kts
val springVersion by extra("6.1.0")
val enableMetrics by extra(true)

dependencies {
    implementation("org.springframework:spring-core:$springVersion")
}

// map-style access also works
project.extra["buildStamp"] = System.currentTimeMillis()

go deeper

for a junior

Know that ext/extra is a key-value bag, how to set it in Groovy (ext {}) and Kotlin (by extra), and that you read it back by name.

for a middle

Explain the ExtensionAware/ExtraPropertiesExtension backing, the Kotlin delegate semantics, and that misspelled reads fail at execution time.

for a senior

Contrast ext with typed extensions and gradle.properties, and explain when ext is the right informal tool vs a smell.

for a principal

Discuss governance: discouraging ad-hoc ext bags across a large build in favour of version catalogs and convention plugins for maintainability.

## What extra properties are Gradle build scripts often need to stash arbitrary values that aren't part of any plugin's model — a shared version string, a feature flag, a computed path. Gradle supports this through **extra properties**. The mechanism rests on two interfaces: - **`ExtensionAware`** — implemented by `Project`, `Task`, `Gradle`, `Settings`, and most domain objects. It means the object carries an `ExtensionContainer`. - **`ExtraPropertiesExtension`** — a special extension, always present, registered under the name `ext`. It is essentially a `Map<String, Object>` you can write to and read from dynamically. So `project.ext` is the project's bag of dynamic properties. ## Declaring and reading — Groovy ```groovy ext { springVersion = '6.1.0' enableMetrics = true } // read anywhere later dependencies { implementation "org.springframework:spring-core:${springVersion}" } ``` Reading `springVersion` works because Groovy's dynamic dispatch falls through to `ext`. You can be explicit with `project.ext.springVersion` or `project.springVersion`. ## Declaring and reading — Kotlin Kotlin is statically typed, so there is no dynamic fall-through. Gradle provides a **property delegate** named `extra`: ```kotlin val springVersion by extra("6.1.0") // declare + initialise val enableMetrics by extra(true) // read in the same script just use the local val // read in ANOTHER script: val springVersion: String by extra // looks it up by the property name ``` The delegate stores under the property's own name and casts to the declared type on read. `project.extra["springVersion"]` is the map-style equivalent. ## Key characteristics - **Dynamic & untyped** — values are `Any?`. A misspelled key throws `ExtraPropertiesExtension$UnknownPropertyException` only when that line executes. - **Per-object** — `task.ext.foo` is distinct from `project.ext.foo`. - **Not configuration-cache friendly when captured eagerly** — prefer providers for values consumed by tasks. Extra properties are the quick, informal escape hatch; for anything structured, plugins expose typed extensions instead.

  • What happens if you read an extra property name that was never set?
    Gradle throws ExtraPropertiesExtension.UnknownPropertyException at execution time — there is no compile-time check, so a typo surfaces only when that line runs.
  • Are extra properties type-safe?
    No. They are stored as Any?/Object. In Kotlin the by extra delegate casts on read, but a wrong declared type throws ClassCastException at runtime; in Groovy there is no check at all.

saying these in an interview costs you the question

  • Claiming ext properties are compile-time type-checked.
  • Confusing ext (in-script dynamic) with gradle.properties (file-based).
  • Saying you must declare ext in settings.gradle — it lives on any ExtensionAware object.

context