Gradle still sucks
silverhammermba.github.io
silverhammermba.github.io
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.
They are not mutable.. their configuration is. That’s like the whole point of gradle.
Also, people should just learn to write plugins - that’s the proper way to add functionality.
Same issue I had with Ruby and Scala codebases...
Gradle really should fix their documentation though, as even though people are at the wrong here, if everyone is misusing it something is not fine.
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.
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.
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.)
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.
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.
It's like we got captured by the tools.
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.
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?
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...
I used to dislike Maven until I was forced to work on a large codebase with Gradle.
Now I think Maven is actually quite good.
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.
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 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.
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.
You care to elaborate on this point? Seems a bit too terse to convey whatever benefit this provides?
I would have thought an artefact repo solves this?
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
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?
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.
Besides I haven't seen anyone presenting any conventions that I would rather use, even if it would magically work in Maven and across all IDEs.
I like maven.
The first few Gradle projects I've seen looked completely different, one project contained more Gradle configuration than actual source code.
And it becomes worse and worse, as every major Gradle version adds new stuff on top.
I can also checkout an old Maven project and it will probably work with a recent Maven version.
With Gradle, not so much. Breaking changes left and right...
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.
e.g.
https://github.com/MarginaliaSearch/MarginaliaSearch/blob/ma...
https://github.com/MarginaliaSearch/MarginaliaSearch/blob/ma...
The now default Kotlin syntax helps somewhat.
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 ;-)
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.
I really enjoyed this blog post.
immediate unsubscribe :)
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.
Just noting that this is unrelated to a piece of software being good. People complain about tools that're A.) crappy and/or B.) Widely used.
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.
I don't see how this is ironic. Do you also think it's ironic that pure functional programming languages like Haskell are garbage collected?
You cannot have exclusive immutability without a garbage collector.
Have you ever used a programming language with exclusively immutable data that didn't have garbage collector?
Try implementing one. You'll see.
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.
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.
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:
git branch <new-branch> [<start-point>]
or, to create and check out in one go git switch -c <new-branch> [<start-point>]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)
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.
I do not find Maven to be an obvious improvement. I would say it's equally bad.
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).
Gradle is Ant re-invented by those that didn't learn why we moved away from Ant.
But show me any build tool that could replace gradle in its role in android development.
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.
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.
Here's an example: https://gist.github.com/akbertram/1d543c8648ddf322f9866c3738...
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.
You just likely have tasks run at configuration time (you don’t use doFirst/doLast in custom tasks), or some other problematic build files.
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.
You can’t possibly believe that the configuration file itself takes any significant percentage of running a full build?
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.
As a comparison, building Java with make:
https://www.oreilly.com/openbook/make3/book/ch09.pdf [PDF]
From Managing Projects with GNU Make 3e (2004) by Robert Mecklenburg
I'm not sure who said that but I wrote it down a long time ago and use it to remind me about making things both simple and understandable.
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 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 use bash for my personal projects now. Thankfully I need none of the advanced features gradle provides/inflicts upon you.
And bash is an abomination, I would much rather use brainfuck :D
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.
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.
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.
Me: The workaround sucks.
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.
Me: Thanks?
It depends on what features specifically you are looking for though.
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.
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.
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.
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.
I want a tool whose behavior I can reason over.
Is that too much to ask?
Apparently, for gradle, it's too much to ask.
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).
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.
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.
Fundamentally all I want is:
- A “build” monad where the steps are sandbox execution
- Expressions written in a simple language, doesn’t have to be Starlark but it does the job
- Remote caching and execution
- Cross compilation
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.
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.
At that level of suffering, it sounds as though one has crafted a custom build tool for each non-trivial project.
Every time articles like that end with this, and yet there’s still no answer.
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.
I haven't really noticed any improvement, just that some workflows I was used to with sbt stopped being available.