skip to content

How would you use --warning-mode to make sure no new Gradle deprecations creep into a build between major-version upgrades?

level: seniorimportance: should knowfreq 38%

answer

  1. fail = ratchet pinned at zero deprecations
  2. clear with all first, then lock with fail
  3. org.gradle.warning.mode=fail in gradle.properties
  4. plugin bump can trip the gate — expected
  5. none hides debt, fail prevents it

basics

~10 s

Set --warning-mode=fail (or org.gradle.warning.mode=fail) in CI. The build then fails whenever any deprecation is logged, so a new deprecation from code or a plugin upgrade is caught immediately.

solid answer

~40 s

After clearing all existing deprecations with `--warning-mode all`, you turn `fail` into a **ratchet** so the deprecation count can never climb back above zero. In CI, run the build with `--warning-mode fail` (CLI) or pin `org.gradle.warning.mode=fail` in `gradle.properties` so every environment behaves the same. Now any change that introduces a deprecation — a new DSL usage you wrote, or a *plugin upgrade* that starts using a deprecated API — fails the pipeline at the point of introduction, when context is freshest and the diff is small. This keeps the build continuously upgrade-ready: when the next major Gradle ships, you have little or no backlog to clear. The trade-off is that a transitive plugin bump can suddenly break CI, so teams sometimes gate `fail` to the main pipeline while letting local builds use `summary`.

code

toml · 5 lines
toml
# gradle.properties — project-wide guardrail
org.gradle.warning.mode=fail

# Or apply only in CI, leaving local builds quiet:
#   ./gradlew check --warning-mode fail

go deeper

for a junior

Know that fail makes the build fail on any deprecation.

for a middle

Set it in gradle.properties or CI and understand it pins the count at zero after you clear existing ones.

for a senior

Articulate the ratchet pattern, the CI-only vs project-wide trade-off, and the plugin-bump consequence.

for a principal

Treat zero-deprecations as an org policy and CI standard so every repo stays continuously upgrade-ready; weigh the no-allowlist constraint and the operational cost of plugin-driven failures.

## The problem: deprecation debt accumulates silently With the default `summary` mode, deprecations are reported only as an end-of-build count, which everyone ignores. Over months the build accrues dozens of them, and the eventual major-version upgrade becomes a large, risky big-bang fix. The fix is to treat deprecation count like a quality gate. ## The ratchet pattern 1. **Reach zero once.** Run `./gradlew build --warning-mode all --stacktrace`, fix or upgrade every offender until the build is clean. 2. **Lock it with `fail`.** Configure CI to run with `--warning-mode fail`. Because `fail` aborts the build whenever *any* deprecation is logged, the count is pinned at zero — a ratchet that only allows clean states. 3. **Stay clean for free.** New code or a plugin upgrade that reintroduces a deprecation now fails the PR pipeline, so it's fixed in-context instead of discovered years later. ## Where to set it - **`gradle.properties`**: `org.gradle.warning.mode=fail` makes it the project default for everyone, including CI. Simple and consistent. - **CI-only flag**: pass `--warning-mode fail` only in the pipeline, leaving local builds on `summary`. This avoids surprising developers locally when a transitive plugin bump trips the gate, while still guarding the integration build. ```kotlin // gradle.properties // org.gradle.warning.mode=fail ``` ## Trade-offs and pitfalls - A dependency/plugin upgrade can introduce a deprecation you don't own; with `fail` that surfaces as a red CI build on the dependency-bump PR. That's the desired behaviour, but teams should expect it and budget for the small fix (or pin the plugin until a clean version exists). - `fail` is binary — it can't allow a known, accepted deprecation. If you need a temporary exception you must either fix it, suppress at the source, or relax the gate, since Gradle has no per-warning allowlist for the global mode. - Don't confuse `fail` with `none`: `none` hides debt, `fail` prevents it. Reaching for `none` to make CI green again defeats the purpose. ## Why this matters for upgrades The whole point is forward-compatibility: a build kept at zero deprecations on 8.x is, by construction, close to ready for 9.0. The ratchet converts a periodic painful migration into continuous small fixes.

  • A routine plugin upgrade PR suddenly fails CI under --warning-mode fail. Is the gate misconfigured?
    No — it's working as intended. The new plugin version uses a deprecated API; you fix it now (pin, patch, or upgrade further) while the change is isolated, instead of discovering it at the next major bump.
  • Can you allowlist one known-acceptable deprecation while keeping fail?
    Not via the global mode — it's all-or-nothing. You'd have to eliminate the call, suppress at the source plugin, or relax the gate for that build. There's no built-in per-warning exception list for org.gradle.warning.mode.
  • Why is fail better than just reading the summary count?
    `summary` relies on a human noticing and acting on a count that's easy to ignore; `fail` enforces it mechanically at the moment of introduction, keeping the build continuously upgrade-ready.

It's a ratchet wrench: all lets you tighten down to zero deprecations once, and fail is the pawl that won't let the count slip back the other way.

saying these in an interview costs you the question

  • Suggesting --warning-mode none to get CI green again instead of fixing the deprecation.
  • Believing fail supports a per-warning allowlist.
  • Only enabling fail after deprecations already accumulated, then being surprised the build is red everywhere.

context