Web Analytics

I've promised myself never to use gradle

The current article won’t be an exhaustive technical analysis of Gradle but more like a spontaneous rant.

Firstly, the time I am willing to allocate to learning a build tool will always be strictly limited. Secondly, I try to be fair, so the list of things I expect from a build tool is quite small:

Jokes aside, I’ve just described Maven.

Unfortunately, Gradle lost its way somewhere between the upgrade from version 4.x to 5.x, or between the upgrade from 5.x to 6.x, or between 6.x to 7.x, or between 7.x to 8.x. Or maybe Gradle was never the way to begin with. We were momentarily happy not having to write and read pom.xml files ever again, and we jumped ship too early. Our problem was never Maven; it was XML…

Gradle and the cognitive load

The moment you exit the realm of straightforward build files, you will become lost and incredibly lonely. This will happen because you never had the patience (by then!) to read the documentation in its entirety. And bear in mind, Gradle’s documentation is not something you can skim on the weekend or from your mobile phone while sitting on the bus. On the contrary, it’s a “hard” read, full of specific technical jargon you need to intimately familiarize yourself with.

Let me give you an example. The chapter “Learning the Basics -> Understanding the Build Lifecycle” starts with the following paragraph. This should theoretically be an easy read, given it’s an introductory article:

Quickly, without opening your CS reference book, tell me what a Directed Acyclic Graph is. It’s ok; you don’t have to open your CS reference book because the authors of the documentation were kind enough to link a Wikipedia article:

After digging through the documentation for a few days (or up to a week, if you want to understand how Multi-Project builds actually work), things will become much clearer as you experience a few Eureka moments. This period is critical. After this week, you will either hate or love Gradle. It only depends on your overall tolerance for complexity and over-engineered solutions.

In any case, kudos! You are now part of a select group of people who managed to get through the Gradle documentation. But it would be wise to hide this fact from your team. Otherwise, your colleagues will make you the guy responsible for the build file. This is a burden not always worth carrying.

My advice:

The disregard for backward compatibility

Java prides itself on being a conservative technology (for lack of a better word). The standard API rarely changes, and the old stuff keeps working, even if it falls from grace. People aren’t using Vector<T> anymore, but this doesn’t mean Vector<T> was removed from the Standard Library. To a lesser extent, the ecosystem of libraries, frameworks, and tools surrounding and supporting Java inherits this conservative approach. Developers make great efforts to maintain backward compatibility, even between major versions.

This is absolutely not the case with Gradle. Incrementing to a new major version is always painful (for non-trivial builds).

The API always changes. Sometimes for purely cosmetic reasons:

Or there are subtle changes that can affect existing behavior:

Because of these constant API changes, the 3rd-party plugins you depend upon won’t work anymore unless the original authors proactively update them. For this reason, the highly popular plugin Gradle Shadow comes in 3 “flavors”, one for each API change major Gradle version:

Maintaining multiple versions puts an unnecessary burden on open-source maintainers. For example, the famous Log4shell exploit was never fixed in older versions of the Gradle Shadow plugin (see issue), forcing users to either forcefully update their Gradle version (provoking even more backward compatibility havoc) or implement messy alternative workarounds.

On the one hand, I love that the Gradle team is forward-thinking, but the way things change from version to version is sometimes just too much. If you plan to use Gradle, you must permanently update it. If you get to the point of being a few major versions behind, you will make your life much harder than it should be.

My advice:

Gatekeeping Pandora’s Box

Make no mistake: when you choose to use Gradle, you will program your build file, not configure it.

The build.gradle file is a running program in disguise (a DSL). This means you can write actual business logic into a build file by creating your own functions and hooking them directly into the Directed Acyclic Graph we were previously speaking of. So even if it’s not explicitly required, it’s time for you to learn a little bit of Groovy or Kotlin, depending on the Gradle dialect you pick.

As a fun exercise, let’s write a build.gradle file that fails the build if the weather temperature in Bucharest is lower than 25 degrees Celsius. We need to write a new task called howIsTheWeatherInBucharest, connect to the https://openweathermap.org/ API through a REST call, perform the check, and fail the build if the day is too cold for programming.

// rest of the build file

task howIsTheWeatherInBucharest {
    doLast {
        // Quick and dirty code 
        def apiKey = '<...enter api code here...>'
        def req = new URL('[https://api.openweathermap.org/data/2.5/weather?q=Bucharest&units=metric&appid=](https://api.openweathermap.org/data/2.5/weather?q=Bucharest&units=metric&appid=)' + apiKey).openConnection()
        req.setRequestMethod("GET")
        req.setRequestProperty("Content-Type", "application/json; charset=UTF-8")
        req.setDoOutput(true)
        def is = req.getInputStream();
        def resp = new Scanner(is).useDelimiter("\\A").next();
        def json = new groovy.json.JsonSlurper().parseText(resp)
        def temp = Double.parseDouble(json.main.temp.toString())
        if (temp < 25.0) {
            throw new GradleException("Build failed, the weather in Bucharest is bad")
        }
    }
}
compileJava.dependsOn(howIsTheWeatherInBucharest)

Having the ability to write raw code is seductive, but it opens Pandora’s Box. The programmer’s reflex to throw in some custom functions to make things work will kick in, especially if the build requirements are complex. And to be honest, writing your build file with a programmer’s mindset is more natural than trying to circumvent the DSL.

But let’s take a step back and ask ourselves: is this really what we want from a build tool?! Writing quick and dirty code can spiral into writing more and more quick ’n’ dirty code. Other people in your extended team will add their personal quick ’n’ dirty code. Without the ability to properly debug the build process, and given the non-standard hacks people are willing to put into the build file, things can rapidly become less portable, extremely environment-dependent, or simply not idempotent. Should you always be online to build your project? Should you be inside a specific Private Network?

Do you remember when people were creating massive .sh shell scripts to build things? There’s a reason we stopped doing that.

Even if it’s easy to do, a Gradle build shouldn’t replace a proper CI/CD pipeline. Yet, I’ve worked on and seen projects where the build process was doing much more than just assembling a fat jar. It ran tests, integrated directly with SonarQube, created custom reports based on static code analysis results, performed infrastructure changes, etc. Why!? Simply because it was possible.

My advice:

Groovy, Kotlin, do you speak it?!

Gradle 5.0 came with a “game”-breaking change: devs were suddenly allowed to use an experimental Kotlin DSL to replace the historical Groovy DSL. In theory, this was terrific news, especially for the Android folks who were already flocking to Kotlin in droves. In reality, this heavily fragmented the community and brought even more confusion to the uninitiated.

For example, searching StackOverflow for Gradle issues suddenly became an arduous chore. First of all, the answers you will find on the Internet are, most of the time, horribly outdated. You cannot simply hack your way through with a solution targeting 5.0 if you are already on version 7.0. Chances are it won’t work. But now, you also need to be highly attentive to the dialect! You might find a working solution that uses Groovy, and you will have to manually translate it yourself to Kotlin.

Compared to the Groovy DSL, the Kotlin DSL seems to be much stricter and more opinionated. After all, Kotlin has a much stricter type system compared to Groovy. So if you are a Java developer planning to use Gradle with the Kotlin DSL, you have to be familiar with Groovy (just to be able to read the old reference materials), but you also need to learn enough Kotlin to be able to actually write your build file. A little bit of learning new things never killed anyone, but I am asking again: Why is it necessary to learn a completely new programming language just to master a build tool!?

My advice:

Abrupt final thoughts

I promised myself not to use Gradle anymore… but I still do it from time to time, especially for smaller, contained, personal projects. Old habits die hard.





Source Code & Contributions

Spot an error or have an improvement? Open a PR directly for this article .



<< Previous Post

|

Next Post >>