I work on a large codebase that uses Gradle. We're occasionally held up as an example: "see? This corp uses Gradle for something this large, so it must be good." We have a regularly scheduled meeting with Gradle where we have in the past influenced their feature prioritization. We also have a former Gradle engineer on staff.
Gradle is such garbage. I can't count the number of times our ex-Gradler has said "you're not going to like the answer..."
Everything is action-at-a-distance and implicit. You can't answer basic questions like:
- Why does this task exist?
- Why does this task depend on this other task?
- Why did this file get included or excluded?
- How did I get 4 dependencies on 4 different versions of the same library?
You can't answer this stuff because some random include file in some random place reached into a task that had already been created and modified it. And there's no way to find out where this happened. I don't understand why you would want a build system that has build tasks that are mutable.
And yes, it would be nice if people wouldn't do action-at-a-distance, or would not do weird stuff to tasks after they've been created, but that's not really possible. We've actually gone through the trouble of writing a linter that makes sure that devs aren't doing anything too crazy in their .gradle files. But of course some files have to do crazy stuff, so there's an escape hatch, so everyone just uses that.
> Everything is action-at-a-distance and implicit.
And this was the idiomatic way to build "enterprise" software a few years ago, right? Inversion of control, automatic dependency injection, service discovery, configuration over code, builder patterns, ORMs, DSLs, and on and on. The idea seemed to be that, if we just keep adding abstraction and complexity to our tools, soon enough we would be able to build an entire systems with one just-so line of code.
I'm not justifying it, but Gradle really was a product of the times. I used it for years but never understood it, and always hated it only slightly less than Maven.
> this was the idiomatic way to build "enterprise" software a few years ago, right? Inversion of control, automatic dependency injection, service discovery, configuration over code, builder patterns, ORMs, DSLs, and on and on.
All of that remains true for enterprise today as well.
And for the record, Gradle doesn't really check any of the marks you just listed, only DSL if you are squinting a little.
Gradle is basically a application to run generic groovy code. There are literally no limits to what you can do, as you've got a full JVM to play with.
Right, but running code to create configuration for other code, which is how I remember the build system working, is the same kind of inception-like abstraction that I was referring to.
As to that "no limits" thing: I see that as part of the problem and complexity of using Gradle. Everything is abstract and open. But at the point where you can do literally anything, maybe we'd be better off with a build library that we could just call from whatever language we're using? Why do we need another language to do "literally" anything?
I'd agree that a lot of the SMB/large enterprise software devleopment practices make trivial domain requirements into extremely complicated distributed monoliths colloquially called "microsrevices", but gradle is a pretty bad example for this kind of behaviour.
gradle was created as a kneejerk reaction to maven forcing build configuration to be set in stone and in xml. Its inspired by dynamic scripting languages, which is as far removed from these companies as you can get.
I remember it being created shortly after groovy became massively overhyped, and I think the only tools still using it are gradle and Jenkins...
To be fair (to myself :) I was just saying that Gradle was a product of its time. It might not have all the problems of "enterprise" software from that period, but I certainly felt like it was overly general; almost as if it was an experiment in inverting configuration and code. I did actually prefer Gradle over Maven but in truth I found both to be pretty impenetrable.
"Captured by the tools" . . I am stealing that one!
Whew, yeah, I stepped away from Gradle once I got the keys for our company's Jenkins instance. Jenkins was . . its own . . bag of rats . . but at least it was somewhat predictable.
"Captured by the tools". You've described big label tech comms software from the last fifteen - nay, twenty - years. A . . frickin ECOSYSTEM . . an entire industry that outspends content creation several times over . . a vast panoply of ten thousand page specifications and eye-wateringly-expensive tooling . . all for making . . DOCUMENTS. For making documents. WHY DOES THIS EXIST.
Actually, there's a reason. But it's not a functional reason or a technical reason; it's economics, culture, a whole maze of perverse incentives. Which is why it's so frustrating. I'm working on a presentation for exactly this, and it's been really cathartic.
Why should my build tool require more study than the language I’m working in? Why do I need to learn a wholly language to build?
In the same vein, why should the ORM be more complex than the database?
Why should the application server be orders of magnitude more complex than my code?
I mean - I do understand the reasons as given - I just don’t agree that the solutions are worth the sacrifices. I should not need a two week course to be good at some ancillary tool.
This morning I started thinking about this thread and what I use today. I don’t even notice the build and dependency system in Go. It’s just there, and it works. Easy things are easy, hard things are possible, and performance is important.
That's just a mutable task with more steps. If I create a task and then later can reconfigure that task, that's a mutable task. Arguing that it happens during the configuration phase is just semantics. It's still mutable and action-at-a-distance.
Is there another popular build system that allows this?
> Also, people should just learn to write plugins - that’s the proper way to add functionality.
I'm not sure I agree. It puts your weird behavior inside an opaque binary. If your plugin needs any sort of configuration whatsoever then you've got a ton of boilerplate to write to make the DSL "just work". The original article actually calls this out:
> The whole idea of using community-developed plugins becomes a minefield because the chance of them applying all of the concepts completely correctly is essentially 0%, meaning you might be okay as long as you’re only doing basic stuff, but as soon as you stray off the beaten path stuff starts breaking and you have almost no chance to fix it properly yourself.
I don't see any reason why I'd be better at writing a plugin than other people in the community.
Have you written a gradle plugin? It’s like a dark art. The gradle internals are a poorly documented, complicated magical mess. I wrote several gradle plugins used by 1000+ devs and I’m about the only one who understood how gradle worked in my whole org - and that was after spilling some serious blood. One of the most poorly overdesigned OSS tools I’ve ever worked on.
I also did a lot of maven dev and it was way easier to work with.
(TBF this was about 5 years ago so maybe it has improved.)
> (TBF this was about 5 years ago so maybe it has improved.)
No, it's gotten worse. They've deprecated most of what you know from 5 years ago and now have different ways of doing the same thing. Not better, except it's probably more "performance friendly" as that's been their priority lately, to make Gradle perform as well as the competitors... mostly, it's just different and even more black arts now as you're forced to use Providers, inject dependencies and so on.
Don’t know why this is downvoted when it’s the truth.
Build scripts are there to just configure stuff.
Gradle is literally combination of tasks, plug-ins and artifacts.
Gradle suffers the same issue I had with Ruby codebases some 10-12 years ago: people want to be clever, the tooling is very powerful, people use the powerful features to create idiosyncratic tricks: auto-loaders, reconfigurations, and a multitude of action-at-a-distance clever tricks which are inscrutable after it gets large enough.
The ergonomics of Gradle allows to create monstrosities that are extremely hard to reason about later. I've lost countless hours in previous jobs using Gradle where someone at some point of the project tried to engineer the build system as an application, an application that is hard to read, reason about and actually know what it is doing.
Build scripts should be declarative, simple to read and follow. Not a plethora of imports and reconfigurations happening under the hood creating a massive cognitive load to simply understand how the fuck a JAR is built.
It's a pity that this comment is somewhat buried, because it is so true.
Loads of people complain about Maven, because writing custom logic in it is hard. If you do want to add some custom conditional steps into your build, you have to somehow squeeze it in your pom file using plugins and xml configuration.
But the thing is, because adding custom logic is so hard, Maven builds are pretty easy to understand. And as in all other area's of software development, easy to understand means easy to change and easy to maintain.
This is the approach we take with our linter. And it sucks. Because sometimes you really do want to have a code approach. Our remote cache rules have a couple twists (developers can read from remote or read/write to local, presubmit server doesn't use cache at all, and CI does read/write to remote. Yeah, you could code that all up into a plugin, but the rollout strategy for plugins isn't the cleanest. And it would just be more hidden, opaque, implicit behavior.
The actual configuration-time evaluated code that should be in your build files is almost zero, besides some if-else for configuration. That’s the intended use of Gradle build files.
That's not how people use Gradle though, it's a footgun, it's happened over and over in my career that Gradle build files were extremely hard to parse and understand as a human. That is a failure of the tool, it allows people to do that so people do it.
At a previous project I decided to start fresh, swallow my pride and become friends with Gradle.
Everyone of the cool Gradle guys can't be wrong, right? There must just be something that hasn't "clicked" for me yet.
As part of the process I analyzed some builds that used Gradle.
It is a while ago now, but I think for something like 24 builds, I found 21 different ways to do it.
Kotlin, Groovy and whether to use constants or not were just the beginning. Constants can be extracted and with a little help of DDG, every file can be updated to its kts version.
But that is just the beginning. It seems to be a rule that every single thing must be possible to do in at least two different ways.
I came out of that process convinced that, no, for most ordinary projects a lot of precious time can be saved by using Maven despite the "uglyness" of the pom.
Maven encourages sticking to the standards. Every time you do something correctly it rewards you be letting you remove more configuration, and in the end you have something that works well, is fast enough, and is immediately recognizable for every experienced Java developer.
Everytime I see the "elephant balancing on the ball" illustration that is sometimes used for Gradle courses I chuckle for myself and think it is perfect: It is a impressive, but one tiny mistake away from collapsing and breaking something big time.
I've managed to make something that's actually makes Gradle seem pretty impressive, I think mostly by accident. I recently refactored Marginalia's code base to a modular arrangement, and to my surprise Gradle actually does an extremely good job at dealing with this. It actually looks like a competent tool for the job. This is a great surprise for anyone who has worked with Gradle before.
Once I got it all set up, each sub module has a fairly clean gradle file that is low in boiler plate and mostly just specifies dependencies and build artifacts. I can specify versions for the entire codebase in a single file, dependency management is very straight forward and it's stupidly fast do incremental re-builds. There are cases where I can not only re-compile but it will also in some circumstances run the pertinent tests all in less than a second.
That said, to be honest it's still very confusing and I still don't understand how the hell Groovy works with it's looks-declarative-but-really-isn't code style.
Although this is the first time I think I've begun to glimpse why Gradle exists other than to torment developers with API incompatibilities upon each (mandatory) upgrade to keep it working with the newest JVM. It's the first time I've felt the tool brought some sort of value to the code base.
But then again you are the Swede that for a long time ran an advanced, publicly available search engine out of your living room as a side project while working full time.
Of course you can get Gradle to work :-)
Also even considering the above I'd say you were a bit lucky in that you could set up everything yourself, instead of having 11 different people on 7 different teams do it for you ;-)
> That said, to be honest it's still very confusing and I still don't understand how the hell Groovy works with it's looks-declarative-but-really-isn't code style.
How?? It's exactly the same as the Groovy syntax in almost every way, specially the nested blocks which makes Groovy look declarative and that Kotlin inherited from Groovy.
> Maven encourages sticking to the standards. Every time you do something correctly it rewards you be letting you remove more configuration, and in the end you have something that works well, is fast enough, and is immediately recognizable for every experienced Java developer.
I experienced the opposite, that even simple things require quite complex configuration in maven. I was shocked by how difficult shading is.
Gradle and SBT gave some confusion, but a great deal less than maven.
> even simple things require quite complex configuration in maven.
I have used Maven on and off since around 2007/2008 sometime.
Since I started taking responsibility for build setups I have realized that I think in every single case where I found unexpected complexity in Maven it could be solved by something like putting some folder or file in the place it was supposed to be all the time and delete most of the configuration.
Convention over configuration is central to Maven after all and extra configuration is only necessary if you do something non standard.
Maven is very slow in big, multi module projects. Gradle can cache module results (avoiding rebuilding), test results and can distribute build artifacts between developers
given a project split in several modules, we don't want to build them separately, we want to type check at compile time that everything works and we have a single build system managed by Gradle. Gradle is smart enough that if we are changing a module, we can build all the dependent ones, and only them.
The cache system comes into play when someone is changing something: when a developer is updating the code locally with changes from other developers, they don't have to compile and run tests of modules they are not changing locally, as gradle is downloading automatically the changes from the build cache.
* Developer 1 changes module A
* code is pushed
* CI pipeline is running and uploading all the artifacts (final production code, final test code, test results)
* Developer 2 pull code locally
* gradle find changes from the last local run, it tries to compile/run the tests of updated modules but results are downloaded from the cache, instead of running locally
> Gradle is smart enough that if we are changing a module, we can build all the dependent ones, and only them.
I rarely build big projects, we usually create multiple small ones instead and if necessary reuse through artifactory or something, but unless I misunderstand you, this is exactly the same as Maven does in a multi module project, isn't it?
> The cache system comes into play when someone is changing something: when a developer is updating the code locally with changes from other developers, they don't have to compile and run tests of modules they are not changing locally, as gradle is downloading automatically the changes from the build cache.
But why should other developers have a relation to the source of dependencies except for debugging?
Isn't it better to upload the packaged jars to some kind of artifact repository at the end of their build pipeline?
> I rarely build big projects, we usually create multiple small ones instead and if necessary reuse through artifactory or something, but unless I misunderstand you, this is exactly the same as Maven does in a multi module project, isn't it?
for example when when you change an api module in a multi module project, gradle will rebuild/test all the modules that depends on that: you can't do automatically with maven, if you have separate artifacts shared via a repository (at least for my understanding). You can use a reactor module, but maven is not caching efficiently in local builds and not caching at all from a common, distributed cache.
> But why should other developers have a relation to the source of dependencies except for debugging?
Because in a multi module project, each developer has access to the complete project source code and can change whatever module: this is the whole idea of the setup (monorepo idea as Google, for example). In this scenario it is possible to have fine grained module boundaries without the cost of creating new project or the evolution of the interface. If a module doesn't provide the information needed it is easy to change it and gradle will rebuild everything that depends on that. If a new module is needed, it is just a new directory with a build.gradle file in it.
Just to be clear, the end result of a multi module project, at least in my setup, is the creation of single artifact that is going to be deployed.
My first experience with maven was the same. Then I learned what project structure , what plugin and dependency management it expects. I also read a 90 page long introduction book from cover to cover. I don't have problems with it since and every project I meet is familiar in an instant.
> During my period of intense frustration with Gradle I found this condescending blog post claiming that anyone who dislikes it is simply using it wrong.
Replace Gradle with literally any tech tool and you'll find the same articles. A hill that I'll gladly die on is that Git sucks, maybe more than any other tool so widely adopted, and yet bringing up how badly it sucks usually results in a deluge of downvotes and "No! You don't understand! Just learn the internals of the tool!" and "Oh yeah well if it's so bad, what do you suggest?!"
I reject this sentiment. I don't have to have all the answers to point out that something sucks. I also shouldn't be expected to learn the inner workings of a tool that we expect code school students to use.
> But that’s stupid. When I took a step back I realized that all of the problems I had with Gradle before still existed, I just understood how to use them to get work done. But the fundamental problem with Gradle is that it simply does not justify these complexities.
Yep. Spot on.
> But goddam it feels like there must be an easier way to accomplish this stuff.
I've done multiple forays into Java and always fall into the trap of "Certainly in <insert year> java tooling must not suck as bad?" but I come up empty. It still sucks. A real mess.
> Replace Gradle with literally any tech tool and you'll find the same articles. A hill that I'll gladly die on is that Git sucks, maybe more than any other tool so widely adopted, and yet bringing up how badly it sucks usually results in a deluge of downvotes and "No! You don't understand! Just learn the internals of the tool!" and "Oh yeah well if it's so bad, what do you suggest?!"
The difference is that Gradle has an obvious better alternative: Maven. The thing that Gradle does better than Maven is that it makes quick hacks easy. But once you try to use Gradle in a "proper" maintainable way, as this article does, you discover that all the things that annoyed you about Maven were there for a reason and you have to essentially reimplement the same things in Gradle. And then, if you have any sense, you go back to Maven.
>The difference is that Gradle has an obvious better alternative: Maven.
The alternative to Git is Mercurial, though I've never used it so I can't vouch for it. I do think I've read that Git is much faster though, but it's generally accepted that Mercurial's UI is a lot better and more consistent.
AFAICT, all the other alternatives to Git really can't do the same stuff Git does (namely, being distributed).
Yeah, you're right, I didn't want to complicate my post but Mercurial does have a cleaner design. But ultimately they never had an impl that was fast enough to use back when it mattered (maybe they still don't). Speed is a feature, and an important one.
I worked on what was claimed to be the second-biggest scala codebase in the world - 10MLOC, 500 contributors. Of course just switching off caching with 0 other workflow changes doesn't work, but I remain convinced that transparent caching does more harm than good.
The last time I used maven I found it to be painfully slow in comparison to gradle. Has this improved? I switched to gradle in 2015 and that was a pretty big motivation at the time.
I've used Maven and Gradle since before 2015 and never had that experience. In theory Gradle's caching should make it faster than Maven when it's working right; in practice I found Gradle's slow startup and the number of times I needed to invalidate the caching anyway more than outweighed that benefit. If it's working for you then great.
Curious question: why do you think git sucks? From my perspective is one of the fundamental tools behind modern software engineering. brilliantly designed and yet so simple to use. I’ll even go ahead of myself to believe you’d be able to make a successful tech company just providing a “git for X” where X is another industry
Of the DVCS systems that came out at around the same time (hg, git, and bzr all came out within a month or two of each other), I would probably rate git the worst.
The most notable issue with git is that it's help is... anti-helpful. It is pretty jargon-filled, and there have been numerous moments where reading a help page has left me actively more confused about what a git command is doing.
Of course, part of the reason why the help is so unhelpful is because git's UX is poorly designed. When even the help page has to admit that a command actually does three different things depending on parameters, you know it's bad.
I'll also point out that I've seen far more repository corruption issues with git than I have with any other VCS (and I've used CVS). Accidentally torching your data is quite easy in git, and appreciate the irony of an immutable store of data needing a garbage collector for the data it's storing. Something like Mercurial's changeset evolution and phases is far, far saner for development than working with branches in git.
Mostly the UX is inconsistent. Git Koans [0], which is now 10 years old, still holds mostly true. I do feel it has improved, though not in ways I've bothered to use. `git switch` for example, was introduced at, uh, some point because `checkout` is so overloaded.
Anyway, git was the first and only VCS I ever learned so I don't really see the big deal myself. I didn't learn all the internals super well, but I generally did just learned it. On the same token I've never really heard of a VCS that people don't complain about. Except Mercurial.
DX is awful. Inconsistent, unnecessarily confusing, obvious and common usability paths aren't straightforward. The simpliest example: why isn't "git undo" a thing?
> From my perspective is one of the fundamental tools behind modern software engineering.
This dogmatism is the root cause behind the lack of improvement. People insist it's already good enough.
> yet so simple to use.
Simple until it's not, which is almost immediately.
> I’ll even go ahead of myself to believe you’d be able to make a successful tech company just providing a “git for X” where X is another industry
Few other industries would be so convoluted into thinking such a terrible user experience would be allowable.
For me it’s the fact that everything about it seems to be about making the tool easier for the developers who wrote it to write it rather than making it easier for the people actually using it. It periodically tells me that garbage collection has given up and I need to run a specific git command to make it work again. Why doesn’t it just run it since it knows the command? Also, as a user, is there any benefit to me knowing that garbage collection happens at all? I’ve never had to know or care about that with any other source control tool. Either it didn’t exist, or it worked so well that it never came up.
There’s a bunch of modal-ness (modality?) throughout git. You can’t be explicit about things so it’s extremely easy to make a mistake. I’ve frequently gone to make a branch only to realize I had the wrong parent checked out, so now my branch is branched off the wrong thing. In other systems I could say “<tool name> create branch x from parent y”. In fact, this has happened so frequently in our group at work, that several reminders have gone out to “make sure you branch off the correct branch…”
It handles large files really poorly and requires a 3rd party tool to make it work reasonably. But then if you use lfs or a similar tool, several other 3rd party tools that work with git don’t work.
> In other systems I could say “<tool name> create branch x from parent y”.
While the default of `git branch` is to branch from the commit you have checked out, the command does take an optional argument for the start point of the new branch:
The general complaint is that Git's UI requires you to understand how it works internally to be able to use it (properly).
IMHO Git is fine, and forcing the complexity on the user is not a big deal as Git is simple enough... as long as you forget that submodules ever existed. Git's approach really shows it's problems when you use submodules:
- Want to clone a repo with submodules? Gotta know beforehand to use git clone --recursive, or remember to use git submodule update --init --recursive after cloning.
- Want to pull? Now you have to use git pull --recurse-submodules instead of git pull.
- Want to checkout a branch? Now you have to remember to do git submodule update --recursive after checking it out.
Etcetera. Instead of being handled transparently by Git like they should be now every time you do something in Git you have to remember to do something else to babysit the submodules.
It's complicated to the point that a lot of people come up with their own alternative instead of using submodules (git subtree, git subrepo, etc)
Gradle is the single biggest reason why I hate Android Studio and shun away from Kotlin, despite it being a beautiful language (and usually loving Intellij)
I have hazy but broadly positive recollections of ant and nant. Wrote some c# and it showed up in the XML, behaved broadly as expected. Why did developers move away from it?
I was the person who first used Ant outside of Sun. James Davidson developed it so that he could build his servlet engine more easily. I brought it into the ASF when we created Jakarta and got the code to the servlet engine (which became Tomcat). I did a lot of early work on Ant.
It solved a problem at the time, which is that Java didn't really have a build system. We were using Makefiles and that wasn't easily compatible with Java.
All code rots over time and part of that is because we learn better ways to do things from prior experiences. In the case of Ant, other solutions were created to solve the problems that Ant didn't solve. For example, Maven promised to clean up the code duplication that Ant created (mentioned in the comment below). Maven created a whole another set of issues though, so Gradle was created to "fix" those issues.
I'm sure one of the fancy newer FAANG build systems is far better than Gradle, but they have issues around being focused on insanely large codebases. At the end of the day, I still don't think this is a solved problem in the way that git kind of solved SCM. Heck, I'm not even sure that a perfect build system can be solved... everyone has their own requirements.
Because most Ant is programmable (I really don't care if it XML based), and if one doesn't use macros (which most didn't), then one ends up with tons of spaghetti code to understand what a set of folders full of build.xml files actually do in practice, in what sequence, and what steps are required to be changed when adding new dependencies.
I work with a large project built with gradle that includes a lot of custom code generation and other automation very specific to our project. Strong disagree with this article.
One thing that makes my gradle experience more pleasant I think is that I assiduously avoid plugins, and just write the meat of my tasks in Java. Adding a new source set is a one-liner, then just put your code generation logic in src/generator/java.
Add a JavaExec task to run your main class and, optionally define inputs and outputs to enable up-to-date checking. Painless, simple, powerful.
I've tried Gradle again and again only to go back to Maven. Gradle is painfully slow on large (and even small) builds. It's backward compatibility story is non-existent. You can pick any two build files and chances are that they are completely different when compared. Groovy dialect has poor autocompletion support.
Maven, on the other hand, has been extremely fast (with or without caching). I've been upgrading Maven for a long time and have yet to see an upgrade that breaks the build. I can pick any two POM files and chances are they are almost identical. XML, for all it's deficiencies, absolutely flies when it comes to autocompletion.
My experience has been same from small projects to large multi-module projects.
This reinforces my opinion about Gradle: there are too many footguns and papercuts. Every time I shot myself in foot using Gradle, some Gradle expert would chime in that I'm probably not using some (poorly documented) way of doing things that would have drastically improved my experience.
This becomes even more problematic in large teams where we have a mix of experienced and novice engineers. Onboarding new people and maintaining the cognitive load about the build using Maven is several magnitudes faster and predictable than with Gradle.
No, gradle is fast in CICD settings as well where daemons are meaningless. It is fast because it has a proper graph model that only updates nodes that need to be updated, and can often do so in parallel.
I have fantastically lost my patience with Gradle builds that is why.
I bet if it wasn't for Android, no one would care about Gradle, Google is basically funding its existence.
There are hardly any talks about optmizing Maven builds, with Gradle on the other hand, not only it comes regularly, there is even tooling to track down build performance issues and regular conference talks on the matter.
My ideal approach to Gradle would be reading every User Manual [1] starting from v0.7 [2]. But not even an entire workweek is enough for that kind of meditative study.
We have a large Gradle project. IntelliJ just stops keeping track of types and plugins after a certain threshold it seems... our build files are almost all lacking any sort of IDE help... even stuff like String stopped working on some projects, like IntelliJ can't even see it's a Groovy file anymore.
Using kts could help but when we built most of it, that didn't exist and when we finally tried, it was so slow and buggy we couldn't use it until maybe a year ago (and we're not going to rewrite thousands of lines of Gradle across multiple projects)... and anyway, Groovy has as much metadata for types as Kotlin as nearly all plugins and Gradle itself use typed Groovy, so this really should work as good in Groovy as in Kotlin. IntelliJ seems to be completely stopping improving the Groovy DSL support, meaning our build will probably become unmaintainable... having had to migrate from Gradle 3 all the way to Gradle 7 over the years, with the many, many breaking changes they've subjected us to over that period, I'd rather rewrite everything in another build system than try to use kts and Gradle 8, which broke even more stuff than previous upgrades.
The best thing to do here is to check the IntelliJ bug tracker (https://youtrack.jetbrains.com/issues/IDEA) to see if your issue is there, and if not create a ticket for it. That way, it is more likely to be fixed.
The RAM usage of the daemon, the fact there are two programming language you can write it in, and you’re often reading documentation based on the other language, with the newer language slowing down already slow, slow build times, the ever changing syntax, the abstractions, caching problems, the fact the build system is significantly more complex than 99% of anything it’ll ever build, makes writing bash scripts look appealing again.
What caching problem? Gradle really is correct in this regard (compared to Maven), it is likely some plugin that might cause troubles for you, which is not something Gradle can defend against.
> the fact the build system is significantly more complex than 99% of anything it’ll ever build
This is the real problem of Gradle — but is their any other tool for that 1%? Because it is definitely not any of the well-liked language specific ones like Cargo. Build tools are a hard problem.
I work with gradle for the better part of 5 years and use it to build Unity projects. I wrote multiple custom plugins and maintain them still. Learning how gradle works was hard. The official docs didn’t help in the beginning. I only know how to author build scripts because I spend a lot of time in the internals. I don’t use many third party plugins because they never 100% work as intended and become a liability later on when a gradle update is needed.
But do I think it is too complicated and abstract? Yes and No. The main problem is that gradle contains a load of old Bagage. Most concepts which are still in the product center around lazy evaluation and to decouple the what from how. The introduction of Providers a couple of years ago was a pivotal point. Writing lazy evaluated scripts was only possible through private methods which used groovy magic to implement this. I never saw a build system or build tool be just simple when the desired outcome is decoupled and fast builds. Would I pick gradle today? I guess not. But I still think it is a great tool. I looked at nix and hazel and these tools are also not simple.
I love good rant and this one sent me into paroxysms of delight!
About 9 out of 10 gradle build configurations that I have encountered fail to build something. That's surprising, and infuriating given that:
* Gradle is based on a portability layer, the JVM.
* Gradle competes against a portable and relatively reliable build system, Maven, and competes against make, which is non-portable due to its use of the system shell for arbitrary code. I would take Maven or make over Gradle any way of the week.
I have one other major beef with Gradle: every single Gradle-based project I have encountered on GitHub includes the Gradle JAR file. The author wants me to download and execute arbitrary Java code without even the option of inspecting it! To hell with that!
I had a breakthrough with Gradle recently: Gradle builds that fail on my Fedora laptop work just fine in an Ubuntu "toolbox" container. So much for portability!
Like the author of the article, I have devoted several (not as many) hours to understanding Gradle. It is largely incomprehensible, when encountered in the wild.
You do not need to use the gradle wrapper - you can use your own trusted version. Wrapper is there just for convenience. I understand your concerns, but it is not the only way and you are not forced to use it.
I actually have tried to use the trusted version that comes with the Fedora packages. It was quite a while ago so I don't remember exactly why that didn't work, but I think it had to do with Fedora packaging an older version of Gradle than the build configuration required.
So, nice thought. I guess I could work out what Ubuntu system the author used and set up a matching Ubuntu toolbox (container) with the right Gradle version. It's not really a win for Gradle if the developer's operating system has to be shipped with the build system.
Except that Gradle constantly releases new versions with breaking changes. You need to install your own trusted version for each version that you use, and know which one to use for each project.
I agree it is not easy to use correct local version, but that is why gradle wrapper is there. In case you do no trust it, you have workaround for it. For projects from company repository, this should not be issue. For random project from github, it may be worth to go the safe way.
Gradle: But at least we gave you a workaround. And this way we don't have to support backwards compatibility when we capriciously change our minds about something.
While Gradle sucks and is complex af I really do like multi-module stuff. We can flesh out components that are intertwined, test them separately and things like that. So if you have a big backend that wouldn't make sense as microservices it really do helps. I haven't seen any other build tools that allow me to build multi-module stuff. Anyone?
Maven? I've worked on quite a few large backend projects using multi-module in Maven, after it's setup it works: parent POM, submodules, dependency management on parent POM.
It depends on what features specifically you are looking for though.
XML is just verbose, after that it's pretty simple. It's all declarative, there's no code execution during build time that you have to keep in mind to build a mental model of what is actually executing, you read the XML, check the steps and configurations and can understand completely what's being executed.
I like Kotlin, I don't like build tools that allow general computing during runtime where I will have to debug build code to understand what exactly has failed. Declarative tools are much better to reason about for most infrastructure tasks.
A general purpose language as the basis for a build tool always felt like a sledgehammer/nail situation to me. Personally, I prefer less concise, less flexible build config tooling.
Agreed, and I think the same applies in multiple other domains. A general purpose language for CI/CD configuration (Groovy), Infrastructure as Code (Pulumi) is just giving you enough flexibility to shoot yourself in the foot while making reusability and readability much more of a problem than it needs to be. I hate YAML for anything more complex than a flat configuration file but I'd take it over Groovy any day.
For personal java/kotlin projects I just use IntelliJ for building. Things are much simpler and it is easy to do most things.
My biggest problem with gradle on personal macs is that memory hog daemon that is mostly unavoidable. Just to run a build once in a while I don't want to run a daemon. Yes daemon can be turned off but it is prevalent in the ecosystem and IntelliJ gradle integ doesn't work without it.
Gradle makes me appreciate how good both cargo is.
Yeah I get where you're coming from. The daemon is pretty sweet in reducing build times, but I wish there was some easy way of informing it of your intent.
If you're just a casual drive-by person compiling some code, then spawning a gradle daemon is just silly.
If you're actively developing on a codebase, not having a gradle daemon will waste a lot of time waiting on builds that could be instant.
So Gradle is not a build tool, it's a tool for building build tools?
That's wonderful, but that's not what I want.
I am trying to solve problems in my particular domain. I want to spend my time solving those problems, not wrestling with my tool for building build tools.
I just want a tool which knows how to build my things, given the dependencies I give it.
I want a tool with clear and up-to-date documentation.
"modern, idiomatic, as-God-intended method of Gradle usage"
Which changes literally every release, and half of my gradle tasks (already cobbled-together voodoo incantations) for shaded jars, dependency copying, integration tests, etc ... as in common tasks for build systems ... breaks.
That said, building is HARD, and man is it HARD to pick up the nuances. Really there should be a massive standard feature set for build frameworks that every build framework has to document an example of.
Ant was too XML-y, and too disorganized. Then came Maven (still XML-y) but way too rigid and limited. Then came gradle, which had potential, but largely is a failure.
Gradle is well supported by IntelliJ. That's why it's the choice currently. I tried IntelliJ with Maven and it failed with some basic stuff, so it's gradle or ... nothing.
The caveat, again, is that, like languages, there are two types of build systems: those you complain about (because you use them) and those that aren't used at all.
I have not used bazel, pants, or any of the newer build frameworks. Make is both zealously pushed by neckbeards, but curiously used almost nowhere outside of C (a bad sign) and I've read really good "make sucks because" posts.
It speaks to the general constant cycle of suck in software that building code, something that we've done in software for a half-century plus, has very little to show in the way of evolution and growth.
But that's because there is nobody that has proper economic investment incentives. Even google probably underfunds Bazel.
I almost wonder why google and those places don't have two groups / virtual companies that compete to be the build system based on project usage (I also wonder why large companies with a dozen datacenters don't have them compete against each other internally for customers / project funny money).
The author of this blog has been cutting the branch they’re sitting on, making the whole ordeal orders of magnitude more complex then it should be.
With that said, Gradle’s official documentation is indeed bad and the necessary working model is hard to figure out, but that model is sound, and is necessary complexity for solving the general problem of dependency management.
The problem is that there is a discrepancy between “download this and that dependency and run it” hobby projects, and the monstrosities that have to exist in large projects. Gradle is a general build tool, it could be used for multiple languages even. The well liked Cargo is of course much more user friendly, but it simply couldn’t be used as a general build tool, so it sheds a lot of weight there.
So I went through this stage too but I stuck it out to pass through to the other side. Mostly because the options weren't any better.
Gradle's main problem is that it doesn't have a learning curve, it has a learning cliff. You either understand it or you don't and that is terrible for adoption and for people trying to get it like this fellow.
What lies beyond this stage is understanding that these things -are- actually complex for a reason. Attempting to create something simpler just for your "simple" use case eventually requires you to add more stuff to do some new thing and before you know it you have a shitty version of Gradle instead of normal Gradle with some custom plugins/tasks/whatever you happen to need.
That said I'm in the process of going deep on Bazel because Gradle still falls short of the mark for me. Bazel has more of an actual learning curve, it's peak complexity is lower than Gradle (though mostly because it's offloaded that complexity into rulesets).
The reality is that these build systems are hard because they are designed to solve very hard problems when you are building programs with lots of artefacts at big scale where very fine grained caching isn't just nice to have but imperative in order to actually do builds at a reasonable cadence.
Appreciation of these systems is tightly coupled to -actually- having those problems. If you don't have those problems you probably don't get why these things are so complicated/baroque/alien/whatever they may seem to you.
In Gradle's case you can generally build a bunch of JVM code without really understanding Gradle. I consider this to be a bit of a trap but I get -why- it's like this which is that it would simply have no adoption if everyone had to understand the phases, source sets, etc but I still don't like copy/paste builds all the same.
Bazel is both slightly better and slightly worse in this regard. You need to understand some more Bazel concepts to build anything at all - which I consider a good thing, understanding vs copy-paste is good in my book. However that understanding is a bit of an illusion once you realise you need something with a modicum of more complexity.
Soon you will open up a ruleset and realise that for instance the inbuilt rules_java is actually lugging around this JavaInfo thing and rules that are "java aware" are doing things with this rather than "just artefacts" as your simple understanding was let you to believe etc.
Most programmers don't give build systems that respect they deserve. They want them to just work and be out of their way, mostly because they don't care how their artefacts get built. I think this is both wrong and sad but I also don't see it changing, too many incentives to not care as long as it builds. Meaning that understanding of real build systems is concentrated on a small number of individuals at a few places big enough to need them.
I have been using Gradle for more then a decade and for the most part it works fine but occasionally there are situations where I have no clue what went wrong.
Onboarding junior developers onto a big multi module projects that use Gradle can be really difficult. Understandably they are unable to grok the mental model (sometimes I even doubt if I do after all this time).
One thing that has helped though has been switching entirely over to the Kotlin DSL.
Steps 2 to 10 are creating a plugin, and some later steps are a consequence of creating plugin subprojects, but I'm not sure why that was needed? From their description, the plugin just runs some things on the command line. Isn't this the most boring form of code generation?
Why would I choose yet-another-build-tool if Maven works, is stable and backwards compatible? Even worse, why I would choose Java as my language to run a build? The only benefit is to avoid learning a tool that isn't Java, there's nothing else to it.
For some Java build tool to actually gain traction it'd need to be better than Maven in every way, that's pretty hard to accomplish and I'd say that starting a tool that runs code already goes against the best practices for infrastructure code: convention-over-configuration, declarative.
I'm not a Java guy, but Maven is much easier to learn but I have found some people in Java world religiously love Gradle. I don't know what is it with jvm language build tools ... Ehem sbt.
Most build systems have their idiosyncrasies. I do a lot of Kotlin (on server, multi platform, and browser), so I've been an early adopter for the kts dialect. I've used maven and ant in the past on the jvm. And off the jvm things like make, webpack, and other things. And of course lots of configuration language tools (puppet, chef, ansible) as well as things like travis, jenkins, and Github actions. So, I know my tools. And occasionally love to hate them.
Most of these tools have stuff they are good at and stuff that while they can technically can do they just aren't the best tool for.
Things I don't like about gradle:
- the whole (kotlin scripting) kts vs. groovy split. IMHO groovy makes things harder at this point and more poorly discoverable. And because the kotlin scripting part has to be sort of compatible with the groovy part you are left with auto completion that works poorly because of the multiple unnecessary levels of indirection that comes in via the groovy centric internals. This has been improving slowly over the years. At least for new projects, kts is now the default (in Android studio).
- all the "maybe this will work" performance hacks that the gradle people built into their tool over the years. You get lots of complexity from layers of caching, daemons, half working offline modes, etc. They work a little bit but the multiple layers of hacks, outdated feature flags, etc. are annoying.
- regular compatibility breaking changes that mean suddenly your build no longer works on a major version change. "we changed our mind, again, about how stuff is supposed to work" and please be fixing all your build files. The how bit is typically left as a hunt through bad documentation.
- the maze of misdirection that is the documentation and the collective body of unhelpful stackoverflow posts and other online articles. Which thanks to regular compatibility breaking changes and the switch to kts tends to be groovy centric and hopelessly outdated. A lot of the "solutions" you find either don't work at all due to this or needs to be translated. Anything over two years old is probably wrong at this point. It's that bad.
On a positive note, it does work once you get it dialed in. And I tend to not mess with my build files too much. If it looks like a shell script, I'm not even going to attempt doing it from gradle. I'll automate via github actions rather than gradle. Just easier.
And while kts is a PITA to deal with in intellij (it freaks out regularly over phantom compile errors for no good reason whatsoever until you turn it off and on again), having auto completion helps. And being able to use regular kotlin to do stuff is also helpful. But doing simple things is still way too hard.
Mostly I stick with gradle because it is the path of the least resistance. It's the thing that is supported by world + dog, probably the most likely thing to be documented, and others sort of expect it at this point. Everything else is more work to figure out and set up.
Used to use sbt at work, then someone came in with the idea that we should migrate to gradle. To be fair to them, they did most of the migration work by themselves, which was impressive.
I haven't really noticed any improvement, just that some workflows I was used to with sbt stopped being available.
Just stop wasting time with Java and any of its many derivatives. Leave it where it belongs: poorly educating students in uni with terrible OOP practices that’ll take 5-10 years to unwind.
Gradle is such garbage. I can't count the number of times our ex-Gradler has said "you're not going to like the answer..."
Everything is action-at-a-distance and implicit. You can't answer basic questions like:
- Why does this task exist?
- Why does this task depend on this other task?
- Why did this file get included or excluded?
- How did I get 4 dependencies on 4 different versions of the same library?
You can't answer this stuff because some random include file in some random place reached into a task that had already been created and modified it. And there's no way to find out where this happened. I don't understand why you would want a build system that has build tasks that are mutable.
And yes, it would be nice if people wouldn't do action-at-a-distance, or would not do weird stuff to tasks after they've been created, but that's not really possible. We've actually gone through the trouble of writing a linter that makes sure that devs aren't doing anything too crazy in their .gradle files. But of course some files have to do crazy stuff, so there's an escape hatch, so everyone just uses that.