Why does gradle deprecate and remove things so often?

This is a little bit of a rant but I am curious why Gradle deprecates and removes things so often? This is not ideal for a build tool we need stability and backwards compatibility from a build tool.

I can understand if there is a fundamental design flaw that needs to be fixed but something like this?

The Report.enabled property has been deprecated. This is scheduled to be removed in Gradle 8.0. Please use the required property instead. See https://docs.gradle.org/7.1.1/dsl/org.gradle.api.reporting.Report.html#org.gradle.api.reporting.Report:enabled for more details.

What possible reason could there be to break everyone’s build just to change “enabled” to “required”?

I fight things like this on every release, just deprecations and removals that don’t seem to have any reason behind them whatsoever.

Another example, just got done upgrading from 6.8.x to 7.1.1 and I had to add this seemingly random DSL block to my builds so they worked under 7.1.x:

project.tasks.named("sourcesJar") {
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE

Why would something like this all of a sudden be necessary?

Does Gradle have any future plans to stabilize the API and to stop breaking shit for no particular reason?


Semantics are based on conventions. Conventions are defined by opinions supported by the “majority” of people (or the more powerful people). Working against conventions isn’t “bad” by itself but may cause unnecessary gray hair for fellow developers.

I don’t have an answer to your question, but personally I’m grateful for pointers such as

so that creating a tool to automatically update build scripts should be possible (and should be working in more than 50% of the cases).

As non-native English speaker I can’t help you with semantics explanations.
A certain programming language runtime I use used to be backwards-compatible for two decades and then changed quite a few fundamental things requiring quite a bit of additional configuration for making old things work on newer runtime versions.

I, too, don’t understand why EXCLUDE (or better yet: some sort of exclude-if-identical) shouldn’t be the default - could you find some more references on discussions about that?

To my knowledge the principles of semantic versioning are applied. If someone needs to stick to an old Major version backporting newer fixes etc. could be an (time-consuming) alternative. See my first paragraph - changing conventions based on evolved knowledge isn’t simple on Gradle’s side too I believe.

These are the easy problems that have mechanical fixes. The more worrying changes are when behavior is altered or even removed like in

There are always good reasons for them to do so, but it does feel like a constant fight to stay up to date.

I feel your pain. My job description these days could easily be summarised as “Running after the Gradle developers with a bucket”.

The answer to “Why has Report.enabled been replaced with Report.required?” probably lies in the JavaDocs:

boolean Report.isEnabled()

returns a Java primitive, whereas

Property<Boolean> Report.getRequired()

returns a lazy property. As a rule, we want to use lazy properties instead of Java primitives or POJOs because lazy properties will be evaluated once all Gradle build scripts have been parsed and Gradle’s “configuration” phase is complete. However changing an existing property from a boolean to a Property<Boolean> would be a breaking ABI change, and no-one wants that either.

The bottom line is that Gradle is slowly retro-fitting its lazy property API over its POJO-based one. And this is important, because otherwise the ordering of the statements inside the build.gradle scripts becomes very significant. I remember learning this lesson the hard way, back when I wrote my very first Gradle plugin: The only way to copy values set via my plugin’s DSL extension into a task my plugin had also created was to use a Project.afterEvaluate handler. However, other plugins can create afterEvaluate handlers too, and there is still no way to control the order that Gradle executes them all in.

I am far more forgiving of Maven’s pom.xml files these days. XML is verbose, but it also declarative rather than imperative. Whereas Gradle needs to bend over backwards to make its imperative Groovy/Kotlin scripts appear to be declarative.