skip to content

If a project property is set both in gradle.properties and via -P on the command line, which one wins, and how do you reason about it?

level: middleimportance: should knowfreq 45%

answer

  1. CLI -P overrides gradle.properties
  2. -D overrides systemProp. entry
  3. file = defaults, CLI = per-run override
  4. read APIs return the resolved value
  5. layered config: commit defaults, override per invocation

basics

~10 s

The command-line -P value wins. CLI flags override gradle.properties for the same property name. The same holds for -D versus systemProp. entries.

solid answer

~40 s

For a given property name, the **command line overrides the file**. If `gradle.properties` has `flavor=full` and you run `gradle build -Pflavor=lite`, the resolved value is `lite`. The same precedence applies to system properties: `-Dfoo=cli` beats `systemProp.foo=file`. The mental model: `gradle.properties` provides the **defaults/baseline**, and CLI flags are the **per-invocation override**. This is why teams check shared defaults into the project's `gradle.properties` and let developers or CI override individual values with `-P`/`-D` for a single run. (Precedence *among* the different gradle.properties files — project root vs `GRADLE_USER_HOME` vs `-D`-set ones — and the org.gradle.* daemon flags are a separate topic; here the key fact is simply that the CLI wins over the file for the same key.)

code

bash · 4 lines
bash
# gradle.properties has: flavor=full
# This single run overrides it to lite without editing the file
gradle build -Pflavor=lite
# resolved project property flavor == "lite"

go deeper

for a junior

Just state the rule: command-line -P/-D wins over gradle.properties for the same key.

for a middle

Frame it as layered config (committed defaults overridden per invocation) and give a worked example.

for a senior

Note that read APIs return only the resolved value and acknowledge the broader multi-file precedence lives elsewhere.

for a principal

Design a convention: commit safe defaults in gradle.properties, document which keys CI overrides, and avoid scattering values so resolution stays predictable.

## CLI beats file Gradle resolves a project or system property from several sources. For the same key, the **command-line flag has higher precedence than the `gradle.properties` file**. ### Worked example `gradle.properties` (committed default): ```properties flavor=full systemProp.api.timeout=30 ``` Invocation: ```bash gradle build -Pflavor=lite -Dapi.timeout=5 ``` Resolved values: `flavor=lite`, system property `api.timeout=5`. The CLI overrides won. ### Why this design It enables a clean layering: - **`gradle.properties`** = shared, version-controlled defaults that everyone gets. - **CLI `-P`/`-D`** = a one-off override for a specific build (a release run, a CI matrix cell, a developer debugging a flavor). This keeps the common case zero-config while allowing surgical, ephemeral overrides without editing files. ### Reading it back Whatever the source, the read APIs return the **resolved** (already-overridden) value: ```kotlin val flavor = providers.gradleProperty("flavor").get() // "lite" given the example above ``` You don't see *where* it came from — only the winning value. If you need to detect whether a CLI override was given specifically, you'd have to inspect the raw start parameters, which is rarely necessary. ### Scope note The precedence story has more layers — multiple `gradle.properties` locations (project root, `GRADLE_USER_HOME`), environment-variable and `-D`-style overrides of those files, and how `org.gradle.*` daemon flags resolve. Those finer rules belong to the properties-files topic. The single load-bearing fact for command-line invocation is: **a `-P`/`-D` flag overrides the same key in `gradle.properties`.**

  • What is the typical reason to keep defaults in gradle.properties but override with -P?
    gradle.properties holds shared committed defaults everyone gets; -P lets one invocation (a release build, a CI cell) override a single value without changing the file or affecting others.
  • Does the read API tell you whether the value came from the CLI or the file?
    No. The read APIs return the already-resolved winning value; detecting the source requires inspecting raw start parameters and is rarely needed.

saying these in an interview costs you the question

  • Claiming gradle.properties overrides the command line.
  • Asserting -P and systemProp. interact (they are different namespaces; -P/-D each override their own file form).

context