I wonder what people use/need apart from the usual stuff in Maven nowadays.
In the last 5 years, I didn't need anything more than:
- resource plugin
- compiler plugin
- jar plugin (jars, test jars, javadoc jars)
- surefire/failsafe
- shade plugin for repackaging to avoid classpath hell
- assemble plugin
- license plugin
Most of the issues I had were with the shade/assemble/license plugins.
I consider myself a Maven power-user and like the tool compared to others (Gradle is too ant-like, resulting in non-standard builds, sbt is just a torture tool).
With time, I concluded that the simpler it is, the better.
Kinda fascinated by this opinion, I was never a fan of either ant or maven, but I find Gradle perfectly fine for Kotlin, maybe there is either a little difference between Java/Kotlin, or I was mostly spending my time with "old" maven setups, or something completely different. I'm not saying ant and maven have a lot in common, just that I didn't like either.
Author here. The neat thing is, with Mill you don't need any of those plugins because it's all built in (except for the license plugin I believe), and is tested/documented together in the main codebase. So it generally all works pretty well together and you shouldn't have issues.
Mill generally is able to replace a lot of first-party/third-party extensions with builtin functionality: beyond the plugins above, it also subsumes autoformatting plugins, linting plugins, external tools like sdkman/jenv, and so on. Thus when using Mill you often don't even think about "what plugins I need", because the bulk of common use cases are provided out of the box, and you can instead focus on your actual project and application code
Ya know, I've directed a lot of criticism at Maven over the years... and most of it well-founded, I believe, even in hindsight. And yet, today, I find that of all the Java build tools, Maven is my preferred one. Part of that is me, normalizing the pain of Maven in some regards, and part of that is places where they have improved the tool. But however you break it down, it is indeed the case that for the most part Maven "just works"™. That is, at least for Java. I don't do a lot of Scala or Kotlin or anything, so no comment there. And for Groovy, I'm usually doing Grails projects which default to Gradle, so I tend to use Gradle in that context. But for plain Java projects, I honestly find Maven to be the path of least resistance.
I've had the misfortune of having Maven forced on me for Scala projects, I just can't agree with you. The overhead of starting the compiler so often is a killer, and makes your builds much slower. Unless there's been some improvement of Zinc/Bloop/Etc with Maven since I've used it.
I think I would prefer to use Mill than Maven (I haven't used Mill myself) ... going through the video tutorial, it's clear that it's also a very simple system without some of the ancient baggage of Maven (xml, too many plugins, etc).
I don't think "Scala+Maven is worse than Scala+sbt" is opposite to "Scala+sbt is worse than Java+Maven".
As much as I like to hate Scala, in my past experience if you have sbt console open, then hit compile after every few changes the feedback loop is tight-ish - barring crazy Scala features being used.
If you are doing Kotlin and especially Kotlin multi platform, Gradle with the kotlin dialect (not Groovy) is the default choice in the ecosystem. That might change soon as Jetbrains has been working on their own build tool (amper). Maven is an afterthought in the Kotlin world. You can obviously use it with things like Spring. But it's not a well supported, or well documented thing in many Kotlin projects, the Kotlin documentation, or any of the Kotlin build tooling that Jetbrains supports.
Just look at all your Kotlin library dependencies on Github and do a little surveil of what those use. There's a clear picture that emerges of what is common and what is a distinct outlier. Maven is clearly not a mainstream choice in many Kotlin projects on Github. There are a few maven projects. But mostly it's all gradle. And most of that is using the kotlin DSL dialect of that at this point.
Unlike Maven, Gradle has seen a lot of active development adding many similar features that are being advertised for Mill. For example the configuration cache is now default in the upcoming major version (9.0 release candidates are out). And it does make a big difference if you use that. Although cache coherence issues are obviously a potential problem here.
I don't know Mill and it flew a bit under the radar for me. So I won't say anything negative about it. But, I'm not that optimistic I can use it on my multi platform kotlin projects. That's just because a lot of Kotlin is developed in lockstep with the compiler and gradle plugins that it uses, which are very gradle centric. For example, Kotlin multiplatform builds with its own gradle plugin that ships with each Kotlin release. And replacing that with something else is not really that trivial on the type of projects I do.
A lot of Kotlin multi platform needs quite complicated build steps to interact with build tooling for different platforms (llvm, xcode, wasm, jvm, node, etc.). Most of that stuff is being developed by Jetbrains. They don't spend a lot of time on thinking about or supporting maven.
I'm not a Gradle fan boy BTW. I've used Maven and Ant before that. I really dislike the Groovy legacy in Gradle. It makes everything so complicated and convoluted. And that leaks through in the Kotlin DSL for gradle. And clearly Jetbrains is also not that happy with gradle because they are working on Amper.
But it works and the performance work they've been doing in the Kotlin build tools and Gradle is really paying off. I've seen quite massive build speed improvements over the last few years for the same projects on the same hardware.
I've been around for a while, I've used many different build systems for JVM based builds (Java, Hybrid Java/Clojure, Scala) and Maven is by far the simplest most solid.
The basic reason is it's commitment to being declarative.
I understand why programmers want imperative (we're programmers) but it's just the wrong choice for a build system.
I've worked on many OSS projects. I've never pulled a Maven-based project that didn't immediately build and immediately load into my IDE. However for imperative based build systems (Gradle, Ant, now Mill) it's almost inevitable that you won't be able to get the build to work right away or pulled into your IDE in a sensible way (as IDEs cannot read imperative statemetns).
I've created many many build with Maven with many many different demands (polygot codebase, weird deployment artifacts, weird testing runtime needs, etc etc) and Maven has never let me down. Yes, in some cases I've had to write my own plugin but it was good that I had to do that; it forced me to ensure I really needed to -- the Maven plugin ecosystem is already great and covers 90+% of use cases of builds.
I've met a lot of Maven naysayers and the disdain is almost always either some weird aversion to XML (such a trivial reason to choose a worser build system) and/or because the programmer never took the time to understand the rather simple Maven runtime semantics and architecture.
> imperative based build systems (Gradle, Ant, now Mill)
Build code in Mill is pretty declarative. You're using the word to mean "not 'pure, serialized data'".
> IDEs cannot read imperative statemetns
They can, however, run the code to dump the structure.
It's easy for code to embed pure data; on the flip side it's hard to encode behaviour in serialized data. More often than custom Maven plugins I see people just drop down to using shell.
My main problem with Maven is that it's dog slow and incremental compilation doesn't really work, especially on CI. For huge repos that's a real issue.
In a sense I feel that part of the microservice craze is due to the fact that many of our build systems are not good enough to allow us to work with huge monoliths efficiently. Gradle is a bit better (definitely faster), but comes with additional complexity. Haven't tried Mill.
Author here. Mill definitely works pretty well for monorepos. Within a single module Mill incrementally compiles the Java files, between modules Mill caches and parallelizes things much more aggressively, and when running tests Mill can take your PR's `git diff` and selectively execute only tests downstream of your changes.
This all comes built-in without any plugins or anything, and serves to help speed up dev and CI workflows even on pretty large codebases.
Does Mill have the capability to enforce dependency constraints on transitive versions without declaring them explicitly? I find this to be quite a useful feature for dealing with automated security review and gradle can do that:
Not a lot of love for Mill in these comments. I'm interested in what it has to offer.
I find SBT just has a lot of unnecessary abstractions and complexity. It's so opaque that I'm sure I'm leaving a lot of performance on the table with hidden antipatterns. Mill seems to solve that by being 'boring' (in a good way). Being able to trace what's happening in my IDE would be lovely - and something I don't have in SBT or Maven either.
Now that we also have fast running native CLI tooling, I think it's worth another look over SBT.
A lot of people on the Java side have seen what being able to script your build turns into with Gradle. They've also don't want to learn Scala to do their build.
Reading these comments I'm always amused by how different people/teams have radically different needs & preferences, and how other people seem to be dismissive of those. "I don't need anything more than maven", "maven is too slow", "if it's not bootstrappable I don't want it", "there should only be one ultimate build tool", "I hate programming my build", etc.
These opinions are all valid, but only if they don't discount the validity of other strategies. There is no universal law of optimal build practices. There are cases where build performance is critical, and others where it's not. There are cases where configurability is important, and there are cases where it's not. Etc.
Personally, I love having a programmable build in plain Java, which is why I enjoy bld [1] these days. I'm probably the only person you'll ever meet who actually enjoyed using ant, so you're welcome to take my opinion with as much salt as you need to digest that.
Ant worked well because it was extremely _explicit_: You had to say what you wanted. This gave it a lot of flexibility. Unlike some other tools, you aren't running in front of a train trying to get it to stop and let you on. It doesn't go anywhere or do anything until you say so. The cost was pretty much zero defaults and a good bit of boilerplate.
I think a lot of people expected this from Gradle because the idea of "build as programming" seemed like it would give that stop-the-train-let-me-on-already ability. I don't know that it really worked out that way. Maven is all about running devs over, but it's faster than Gradle, which tries to start a daemon just like SBT.
Anyhow... this bld thing looks interesting, thanks...
> These opinions are all valid, but only if they don't discount the validity of other strategies.
I strongly agree with this as a general principle in nearly all technical discussions.
But also, if the discussion starts with declaring something is better than everything else (as the title here), then there's really no salvaging it anymore.
Who's talking about a day? The difference between a fast build tool and a slow tool is in the order of seconds, maybe minutes. Larger differences aren't down to the build tool but down to the codebase.
The difference between a tool that leverages incremental compilation effectively and one that almost always has to build from scratch (at least on CI) can be pretty huge and not "in the order of seconds".
That's more a CI issue than a build tool issue. Just about any (Java) build tool supports incremental compilation. But if your CI env doesn't have a place to cache those builds, it's not going to matter.
And even then it could be a matter of seconds on a smallish codebase. Nor everyone is building Google sized monorepos.
Even just doing things like "please predownload any dependencies in this one job and cache them, so they can be reused in these 5 other jobs" don't work reliably with maven because certain plugins like surefire download dependencies on the fly.
> Nor everyone is building Google sized monorepos.
Nor is everyone building "small-ish codebases". You don't have to be Google, I worked at a company with maybe 10 developers in total, but the codebase was over 10 years old and had a gazillion features (plus a lot of tech debt, obviously) and the build times were atrocious. It wasn't all maven's fault, of course (we had really badly written tests, for example), but the broken incremental compilation easily added 5-10 more minutes to each build.
Which works fine.....until it doesn't. Many non-trivial builds require custom logic, and trying to do that in maven was painful the last time I tried it.
If you have any complexity, programming against a good abstraction (Gradle is not good good, but decent) beats finding the magical incantation of configuration to get the tool to do what you want.
I think folks just get used to Maven-induced constraints (this applies to SBT, Bazel, others too). When you free yourself from that you realize: builds just aren't all that hard, it's often the tooling that becomes a real limitation.
Of course, sometimes the limitations are good: preventing you from doing "the wrong thing", or encouraging cacheability, etc. But as with any abstraction layer - getting a model that fits across so many disparate use cases can be very challenging.
That is why maven offers a plugin model for your custom logic that can be written in Java/Kotlin and a well defined lifecycle model where you can configure your plugin declaratively.
You can also download custom CLI tools and invoke them as part of a well-defined build lifecycle.
And then fail to do proper incremental builds, leaving you with no choice but a faulty build or clean installing on every occasion.
At the very least, I would move to Gradle which does have proper knowledge of your build graph. But Mill is also a good choice and fills the same niche, with the added benefit that imperative-looking ordinary scala code will simply become a parallelizable, cacheable build graph.
Even better, Maven "POMs" are written in a common, standard format (XML); so we can transform and manipulate it using off-the-shelf tools, if we really want to. I've found this useful e.g. in pre-commit hooks (tidy the formatting, checking/linting, etc.); in Nix builds (e.g. removing the version number, so it doesn't affect the hash and avoids spurious rebuilds); etc. That was a nice bonus when I switched some projects from SBT to Maven (due to SBT being wildly unreproducible).
I hate wrangling with configuration to make an amalgamation of plugins do what's expected in the expected order for my build; Gradle is just code and a DSL
Author here. What most people don't realize is that "I hate programming my build." is a symptom of your existing build tools making programming your build dangerous and risky endeavor.
For example, in Maven you typically extend your build in Bash scripts + maven-exec-plugin, Ant script + maven-antrun-plugin, or custom Maven plugins entirely. None of these are "nice" programming environments, with proper IDE support, typechecking, introspectability, and so on. Writing lots of logic in Bash or Ant is risky, so you would be right to minimize writing code in it
Similarly, in Gradle you extend your build in Groovy/Kotlin, but it's a kind of "weird" Groovy/Kotlin unlike anything you'd write in application code. For example, your IDE support in Gradle-Kotlin is much worse than what you get in normal-Kotlin. Despite Gradle-Kotlin being the same language and same IDE, it's a much worse experience writing it, it's much easier to make mistakes, and so you are right to minimize the code you write in it.
In Mill, the build scripts are in Scala, but that's not the important part. The important part is you write normal code using classes, methods, and overrides. IDEs are very good at navigating classes, methods and overrides, and developers are very familiar with working with classes, methods, and overrides. And so build code in Mill feels as comfortable as your application code in Java/Scala/Kotlin: same quality of IDE experience, same typechecking, even can use the same Java third-party libraries (if you wish).
So it's understandable you hate programming your build. In Maven or Gradle or SBT, I'd hate programming my build as well. What Mill offers is that you can program your build where necessary without the hate that comes with doing so in other build tools!
I am old. I used ant, ported to maven when I discovered it (yes Maven 1). Always stuck with it. Maven has been a really nice consistent build tool. I never had to build something so complex it did not know what to do. And no I never used the ant plugin; it's an anti-pattern for Maven. Custom maven plugin's is perfectly fine (I never needed it) but if you need them you probably are doing it wrong (IMHO). My build are never that complex as I mainly write reasonably standard software for businesses.
Nice how you say "your IDE support in...", to refer to IntelliJ. I avoid that. I use Visual Studio Code and Netbeans.
BTW: writing something in bash means it will work for at least a decade. All people on my team will know how it works and understand it and maintain it. Introducing something new mean we all need to learn it. Yesterday my build for a simple website failed because gulp was updated and I ported the entire build to a bash script. It was faster and way simpler.
Let me make it clear here: I hate Grails. I think you have the same gripe as I do. They keep changing the DSL so you need to rewrite your scripts. Since it is a programming language each project has a different build even though most projects are the same. Because people do the same stuff differently.
When I have an issue with maven I will try Mill. Promised.
IMO there should be one build system for each language and it should be good at the basics, and if you need more then you should write your own (minimal-dependency) build script in the same language as the code you're building, and in that system you do things like generate build files for the big, complicated thing you're trying to build.
In Java terms, if you have a big complicated Java repo that requires lots of steps to build, you should have a separate Java project in there just for building the main repo. That separate Java project should be built and run with maven, and that separate project can do all kinds of fancy things, but ultimately it will be generating maven projects or calling maven with special command line parameters or something like that.
I even put the logic for CI in my build project like this. It makes everything reproducible and debuggable. How cool is it to be able to put a breakpoint in your build script? How about stepping through your CI code? Things are way simpler this way.
I eschew frameworks in this custom build tool, because the build code should look conventional for whatever language it's written in.
> I eschew frameworks in this custom build tool, because the build code should look conventional for whatever language it's written in.
That's really the case with Mill as well, at a deeper more-meaningful level. Mill builds are all built around objects, classes, methods, and overrides.
Maven is XML. Do you write large codebases in XML? Gradle is Groovy/Kotlin, but it's an odd Groovy/Kotlin dialect or DSL that looks totally unlike application codebases, and your IDE is unable to understand and navigate effectively.
Mill is Scala, sure, but what's important is that it is architected around objects, classes, methods, and overrides. Those are the core abstractions when working with Mill, which are the same core abstractions as any Java/Scala/Kotlin application. Thus IDEs are able to navigate Mill builds, profilers like JProfiler or Yourkit can work on Mill builds, and humans who learned Java 101 in college are immediately familiar with the structure and fundamental abstractions of Mill.
I've seen this taken to extremes (eg Kubernetes deployment as Java code simply because the service is in Java) and it's... terrifying. It's justified in the pre-internet era, where learning two languages would require two trips to the library. These days, I really don't see why not using the better tool for whatever part of the job you're doing. It creates a mountain of complexity for no reason other than "I only want to touch language X"
Yeah, hard to see how better this is than Gradle. If anything, it's worse by the mere fact this is Scala, but it's really so incredibly verbose to accomplish basic tasks such as upload to Sonatype.
making it easier to generate maven poms I thought was a reasonable idea 18 years ago but gradle and other tools all went down the "making the build Turing complete" path.
Honestly, I'm getting tired of the endless parade of "better" build tools. Maven works fine for most of what I do, even if it's occasionally painful. Gradle is... well, Gradle has its moments.
That said, Mill's performance claims sound interesting. If it really can cut build times by 3-6x, that's really amazing. I've wasted too much time in my life waiting for builds, especially on larger projects.
The Scala thing is a bit of a turn-off though. I get that you don't need to write Scala yourself, but now I'm dragging in the Scala ecosystem just to build my Java project? Feels heavy.
The IDE integration sounds nice in theory - being able to actually navigate and understand your build in IntelliJ would be pretty sweet.
Still not sure the switching costs are worth it unless you're really hitting Maven/Gradle pain points. But if I was starting a new project from scratch, might be worth a look.
Thanks for jumping in, that’s actually really helpful context. Parallelism and selective test execution built-in sounds like a huge win, especially for CI and larger teams. I’ll admit the Scala dependency still makes me hesitate a bit for pure Java projects, but clearly Mill’s design is tackling real pain points head-on. Definitely tempted to try it out on a greenfield project.
True. Tests usually dominate the time budget. But from what I've experienced, slow builds still hurt, especially when you're iterating frequently or working across multiple modules. Those 10–20 seconds of wait adds up fast when you're in the zone and trying things out.
Is it bootstrappable? I mean really bootstrappable, not "the bootstrap script will download a binary from some server". This is where both Maven and Gradle fail (at least the last time I checked). Kotlin and Scala also have the problem that their compilers themselves are not bootstrappable either.
Mill is in many ways trying to be `uv` for the JVM. 3-6x faster than the existing tools, bundling everything necessary builtin without plugin-hell, superior IDE support and toolability than existing tools. If you want a `uv` for Java, you should try out Mill!
JVM build tools have to do much more than just dependency management because of how the JVM works (e.g. packaging resources). It's not really comparable.
It is literally creating zip file with jar extension and files inside of it. I’m pretty sure if they managed to figure out clusterfuck of Python, they can do the same for Java.
That's like saying "programming is just creating files with content".
Python is a purely interpreted language that runs in the context of your file system. Java code needs to be compiled and packaged, you have to write a manifest, you have to decide whether to include dependencies (fat jar) or not, or maybe you want to create a war (if you're stuck in the 2000s)... then what about maybe distributing the sources too?
Also, Maven works. Gradle works. Mill probably works too. The reason uv is a success is that everything Python had before was a broken mess. When people complain about JVM build tools, they come from a different position, not from one of "stuff is completely broken".
> Java code needs to be compiled and packaged, you have to write a manifest, you have to decide whether to include dependencies (fat jar) or not, or maybe you want to create a war (if you're stuck in the 2000s)... then what about maybe distributing the sources too?
I would probably be amazed at complexity of this, if I never touched Java in life.
The things you’re describing are literally a matter of a bash script to stitch together javac, jar and a couple of Unix tools.
Gradle is a monstrosity and abomination against the world. The only reason it exists is to give job to devex engineers and I’m willing to die on the hill. Maybe if it was rewritten in something faster and less memory hungry I’d be able to tolerate all this needless complexity, but until this piece of shit stops hogging all my memory and CPU time – its place is in the dumpster.
Bazel is just such a nightmare for me. It's amazing when someone understands it really well and can set things up. But for anything short of that, being on the hook to fix or debug things makes it a nightmare. That and trying to port anything over from sbt, like scalafix for instance, to bazel is a pain.
Also too, bazel has this issue of googleability? like I feel like I can take any build issue I've run into in sbt and find the solution and an example by just searching, but with Bazel, anything outside of the happy path is a recipe for pain
My experience with Bazel (7 years rolling it out and maintaining it in a large company) is that it provides huge value for larger teams/codebases, but at a huge cost in complexity. e.g. the three I rollouts I was closest to each took ~2 person-decades to happen; might be easier now than it was in 2016/2017, but Bazel hasn't really gotten simpler over the years
Mill is intended to be much easier than Bazel. Most people would not use Bazel for a 1-person project, or even a 5-person project, and it only starts pencilling out once you have 50-100 engineers on the team. Mill in contrast works great for small projects and medium projects as well.
One way I'd look at it is that Mill works great for 1-500 person projects, while Bazel works great for 100-5000 person projects. There's some overlap in the middle, but fundamentally they target different kinds of users who have very different constraints and requirements
This is kinda funny. Generating a profile that is meant to be visualized with a web browser's built-in profiler, not even an HTML file. I guess if it's already built and works well ¯\_(ツ)_/¯
Please try out Mill! We have a lot of Maven/Gradle/SBT refugees in the community who are quite happy with Mill, so perhaps if you give it a try you'll find a build tool you can be happy with as well
Related. Others?
Mill as a direct style build tool - https://news.ycombinator.com/item?id=43943792 - May 2025 (9 comments)
Why does Mill use Scala? - https://news.ycombinator.com/item?id=42997496 - Feb 2025 (75 comments)
Mill: A fast JVM build tool for Java and Scala - https://news.ycombinator.com/item?id=41967734 - Oct 2024 (162 comments)
What's So Special About the Mill Scala Build Tool - https://news.ycombinator.com/item?id=38877882 - Jan 2024 (1 comment)
Mill: A Build Tool Based on Pure Functional Programming - https://news.ycombinator.com/item?id=25925107 - Jan 2021 (1 comment)
Mill: Better Scala Builds - https://news.ycombinator.com/item?id=16775545 - April 2018 (16 comments)
I wonder what people use/need apart from the usual stuff in Maven nowadays.
In the last 5 years, I didn't need anything more than:
- resource plugin - compiler plugin - jar plugin (jars, test jars, javadoc jars) - surefire/failsafe - shade plugin for repackaging to avoid classpath hell - assemble plugin - license plugin
Most of the issues I had were with the shade/assemble/license plugins.
I consider myself a Maven power-user and like the tool compared to others (Gradle is too ant-like, resulting in non-standard builds, sbt is just a torture tool).
With time, I concluded that the simpler it is, the better.
> Gradle is too ant-like
Kinda fascinated by this opinion, I was never a fan of either ant or maven, but I find Gradle perfectly fine for Kotlin, maybe there is either a little difference between Java/Kotlin, or I was mostly spending my time with "old" maven setups, or something completely different. I'm not saying ant and maven have a lot in common, just that I didn't like either.
Author here. The neat thing is, with Mill you don't need any of those plugins because it's all built in (except for the license plugin I believe), and is tested/documented together in the main codebase. So it generally all works pretty well together and you shouldn't have issues.
Mill generally is able to replace a lot of first-party/third-party extensions with builtin functionality: beyond the plugins above, it also subsumes autoformatting plugins, linting plugins, external tools like sdkman/jenv, and so on. Thus when using Mill you often don't even think about "what plugins I need", because the bulk of common use cases are provided out of the box, and you can instead focus on your actual project and application code
Ya know, I've directed a lot of criticism at Maven over the years... and most of it well-founded, I believe, even in hindsight. And yet, today, I find that of all the Java build tools, Maven is my preferred one. Part of that is me, normalizing the pain of Maven in some regards, and part of that is places where they have improved the tool. But however you break it down, it is indeed the case that for the most part Maven "just works"™. That is, at least for Java. I don't do a lot of Scala or Kotlin or anything, so no comment there. And for Groovy, I'm usually doing Grails projects which default to Gradle, so I tend to use Gradle in that context. But for plain Java projects, I honestly find Maven to be the path of least resistance.
I've had the misfortune of having Maven forced on me for Scala projects, I just can't agree with you. The overhead of starting the compiler so often is a killer, and makes your builds much slower. Unless there's been some improvement of Zinc/Bloop/Etc with Maven since I've used it.
I think I would prefer to use Mill than Maven (I haven't used Mill myself) ... going through the video tutorial, it's clear that it's also a very simple system without some of the ancient baggage of Maven (xml, too many plugins, etc).
I’ve had sbt and scala forced upon me for Java projects and I feel the exact opposite of you!
Sbt and Scalac are so slow they kill any productivity I could have.
I don't think "Scala+Maven is worse than Scala+sbt" is opposite to "Scala+sbt is worse than Java+Maven".
As much as I like to hate Scala, in my past experience if you have sbt console open, then hit compile after every few changes the feedback loop is tight-ish - barring crazy Scala features being used.
I think you can make it 10 years. It is super robust. It just works for java stuff.
If you are doing Kotlin and especially Kotlin multi platform, Gradle with the kotlin dialect (not Groovy) is the default choice in the ecosystem. That might change soon as Jetbrains has been working on their own build tool (amper). Maven is an afterthought in the Kotlin world. You can obviously use it with things like Spring. But it's not a well supported, or well documented thing in many Kotlin projects, the Kotlin documentation, or any of the Kotlin build tooling that Jetbrains supports.
Just look at all your Kotlin library dependencies on Github and do a little surveil of what those use. There's a clear picture that emerges of what is common and what is a distinct outlier. Maven is clearly not a mainstream choice in many Kotlin projects on Github. There are a few maven projects. But mostly it's all gradle. And most of that is using the kotlin DSL dialect of that at this point.
Unlike Maven, Gradle has seen a lot of active development adding many similar features that are being advertised for Mill. For example the configuration cache is now default in the upcoming major version (9.0 release candidates are out). And it does make a big difference if you use that. Although cache coherence issues are obviously a potential problem here.
I don't know Mill and it flew a bit under the radar for me. So I won't say anything negative about it. But, I'm not that optimistic I can use it on my multi platform kotlin projects. That's just because a lot of Kotlin is developed in lockstep with the compiler and gradle plugins that it uses, which are very gradle centric. For example, Kotlin multiplatform builds with its own gradle plugin that ships with each Kotlin release. And replacing that with something else is not really that trivial on the type of projects I do.
A lot of Kotlin multi platform needs quite complicated build steps to interact with build tooling for different platforms (llvm, xcode, wasm, jvm, node, etc.). Most of that stuff is being developed by Jetbrains. They don't spend a lot of time on thinking about or supporting maven.
I'm not a Gradle fan boy BTW. I've used Maven and Ant before that. I really dislike the Groovy legacy in Gradle. It makes everything so complicated and convoluted. And that leaks through in the Kotlin DSL for gradle. And clearly Jetbrains is also not that happy with gradle because they are working on Amper.
But it works and the performance work they've been doing in the Kotlin build tools and Gradle is really paying off. I've seen quite massive build speed improvements over the last few years for the same projects on the same hardware.
I agree 100%.
I've been around for a while, I've used many different build systems for JVM based builds (Java, Hybrid Java/Clojure, Scala) and Maven is by far the simplest most solid.
The basic reason is it's commitment to being declarative.
I understand why programmers want imperative (we're programmers) but it's just the wrong choice for a build system.
I've worked on many OSS projects. I've never pulled a Maven-based project that didn't immediately build and immediately load into my IDE. However for imperative based build systems (Gradle, Ant, now Mill) it's almost inevitable that you won't be able to get the build to work right away or pulled into your IDE in a sensible way (as IDEs cannot read imperative statemetns).
I've created many many build with Maven with many many different demands (polygot codebase, weird deployment artifacts, weird testing runtime needs, etc etc) and Maven has never let me down. Yes, in some cases I've had to write my own plugin but it was good that I had to do that; it forced me to ensure I really needed to -- the Maven plugin ecosystem is already great and covers 90+% of use cases of builds.
I've met a lot of Maven naysayers and the disdain is almost always either some weird aversion to XML (such a trivial reason to choose a worser build system) and/or because the programmer never took the time to understand the rather simple Maven runtime semantics and architecture.
> imperative based build systems (Gradle, Ant, now Mill)
Build code in Mill is pretty declarative. You're using the word to mean "not 'pure, serialized data'".
> IDEs cannot read imperative statemetns
They can, however, run the code to dump the structure.
It's easy for code to embed pure data; on the flip side it's hard to encode behaviour in serialized data. More often than custom Maven plugins I see people just drop down to using shell.
My main problem with Maven is that it's dog slow and incremental compilation doesn't really work, especially on CI. For huge repos that's a real issue.
In a sense I feel that part of the microservice craze is due to the fact that many of our build systems are not good enough to allow us to work with huge monoliths efficiently. Gradle is a bit better (definitely faster), but comes with additional complexity. Haven't tried Mill.
Author here. Mill definitely works pretty well for monorepos. Within a single module Mill incrementally compiles the Java files, between modules Mill caches and parallelizes things much more aggressively, and when running tests Mill can take your PR's `git diff` and selectively execute only tests downstream of your changes.
This all comes built-in without any plugins or anything, and serves to help speed up dev and CI workflows even on pretty large codebases.
Does Mill have the capability to enforce dependency constraints on transitive versions without declaring them explicitly? I find this to be quite a useful feature for dealing with automated security review and gradle can do that:
https://docs.gradle.org/current/userguide/dependency_constra...
Yes, that's handled by Mill's bill-of-materials (BOM) support https://mill-build.org/mill/fundamentals/library-deps.html#_...
Not a lot of love for Mill in these comments. I'm interested in what it has to offer.
I find SBT just has a lot of unnecessary abstractions and complexity. It's so opaque that I'm sure I'm leaving a lot of performance on the table with hidden antipatterns. Mill seems to solve that by being 'boring' (in a good way). Being able to trace what's happening in my IDE would be lovely - and something I don't have in SBT or Maven either.
Now that we also have fast running native CLI tooling, I think it's worth another look over SBT.
A lot of people on the Java side have seen what being able to script your build turns into with Gradle. They've also don't want to learn Scala to do their build.
Reading these comments I'm always amused by how different people/teams have radically different needs & preferences, and how other people seem to be dismissive of those. "I don't need anything more than maven", "maven is too slow", "if it's not bootstrappable I don't want it", "there should only be one ultimate build tool", "I hate programming my build", etc.
These opinions are all valid, but only if they don't discount the validity of other strategies. There is no universal law of optimal build practices. There are cases where build performance is critical, and others where it's not. There are cases where configurability is important, and there are cases where it's not. Etc.
Personally, I love having a programmable build in plain Java, which is why I enjoy bld [1] these days. I'm probably the only person you'll ever meet who actually enjoyed using ant, so you're welcome to take my opinion with as much salt as you need to digest that.
[1] https://rife2.com/bld
Ant worked well because it was extremely _explicit_: You had to say what you wanted. This gave it a lot of flexibility. Unlike some other tools, you aren't running in front of a train trying to get it to stop and let you on. It doesn't go anywhere or do anything until you say so. The cost was pretty much zero defaults and a good bit of boilerplate.
I think a lot of people expected this from Gradle because the idea of "build as programming" seemed like it would give that stop-the-train-let-me-on-already ability. I don't know that it really worked out that way. Maven is all about running devs over, but it's faster than Gradle, which tries to start a daemon just like SBT.
Anyhow... this bld thing looks interesting, thanks...
> These opinions are all valid, but only if they don't discount the validity of other strategies.
I strongly agree with this as a general principle in nearly all technical discussions.
But also, if the discussion starts with declaring something is better than everything else (as the title here), then there's really no salvaging it anymore.
> There are cases where build performance is critical, *and others where it's not.*
I'm curious about those.
Often CI results are not put to use immediately, making build time irrelevant.
So you're okay with waiting a day to get "linter error, you've put this on a wrong line, dumbass"?
Who's talking about a day? The difference between a fast build tool and a slow tool is in the order of seconds, maybe minutes. Larger differences aren't down to the build tool but down to the codebase.
The difference between a tool that leverages incremental compilation effectively and one that almost always has to build from scratch (at least on CI) can be pretty huge and not "in the order of seconds".
That's more a CI issue than a build tool issue. Just about any (Java) build tool supports incremental compilation. But if your CI env doesn't have a place to cache those builds, it's not going to matter.
And even then it could be a matter of seconds on a smallish codebase. Nor everyone is building Google sized monorepos.
> Just about any (Java) build tool supports incremental compilation
I wish that were true. Maven is terrible at this, e.g. here's a bug report that's been open for 12 years: https://issues.apache.org/jira/browse/MCOMPILER-209
Even just doing things like "please predownload any dependencies in this one job and cache them, so they can be reused in these 5 other jobs" don't work reliably with maven because certain plugins like surefire download dependencies on the fly.
> Nor everyone is building Google sized monorepos.
Nor is everyone building "small-ish codebases". You don't have to be Google, I worked at a company with maybe 10 developers in total, but the codebase was over 10 years old and had a gazillion features (plus a lot of tech debt, obviously) and the build times were atrocious. It wasn't all maven's fault, of course (we had really badly written tests, for example), but the broken incremental compilation easily added 5-10 more minutes to each build.
It's nice that there are options but i'm sticking with maven. I hate programming my build; maven is just a configuration file.
Which works fine.....until it doesn't. Many non-trivial builds require custom logic, and trying to do that in maven was painful the last time I tried it.
Yeah. It's Greenspun's tenth rule.
If you have any complexity, programming against a good abstraction (Gradle is not good good, but decent) beats finding the magical incantation of configuration to get the tool to do what you want.
I think folks just get used to Maven-induced constraints (this applies to SBT, Bazel, others too). When you free yourself from that you realize: builds just aren't all that hard, it's often the tooling that becomes a real limitation.
Of course, sometimes the limitations are good: preventing you from doing "the wrong thing", or encouraging cacheability, etc. But as with any abstraction layer - getting a model that fits across so many disparate use cases can be very challenging.
I have found that if it is hard to do in maven you probably shouldn't be doing it or there is a better way.
That is why maven offers a plugin model for your custom logic that can be written in Java/Kotlin and a well defined lifecycle model where you can configure your plugin declaratively.
You can also download custom CLI tools and invoke them as part of a well-defined build lifecycle.
And then fail to do proper incremental builds, leaving you with no choice but a faulty build or clean installing on every occasion.
At the very least, I would move to Gradle which does have proper knowledge of your build graph. But Mill is also a good choice and fills the same niche, with the added benefit that imperative-looking ordinary scala code will simply become a parallelizable, cacheable build graph.
Out of curiosity, what is non-trivial in this context?
> maven is just a configuration file
Even better, Maven "POMs" are written in a common, standard format (XML); so we can transform and manipulate it using off-the-shelf tools, if we really want to. I've found this useful e.g. in pre-commit hooks (tidy the formatting, checking/linting, etc.); in Nix builds (e.g. removing the version number, so it doesn't affect the hash and avoids spurious rebuilds); etc. That was a nice bonus when I switched some projects from SBT to Maven (due to SBT being wildly unreproducible).
I hate wrangling with configuration to make an amalgamation of plugins do what's expected in the expected order for my build; Gradle is just code and a DSL
If I would've gotten a Euro for every time "is just a DSL" was a reason everything was hard to debug and prone to failure, I'd have many Euros by now.
Well, good luck debugging a multi-module maven pom file, then. You can get terrible error messages from both, to be honest.
It’s two DSLs which are versioned and whose behaviors are different.
Gradle groovy is extremely permissive (eg: you can access private class instance variables without even knowing that you are doing so)
Kotlin lacks that permissive quality in exchange for much easier introspection.
It’s often trivial to move from one to the other but those edge cases can find you in a codebase of any complexity.
Author here. What most people don't realize is that "I hate programming my build." is a symptom of your existing build tools making programming your build dangerous and risky endeavor.
For example, in Maven you typically extend your build in Bash scripts + maven-exec-plugin, Ant script + maven-antrun-plugin, or custom Maven plugins entirely. None of these are "nice" programming environments, with proper IDE support, typechecking, introspectability, and so on. Writing lots of logic in Bash or Ant is risky, so you would be right to minimize writing code in it
Similarly, in Gradle you extend your build in Groovy/Kotlin, but it's a kind of "weird" Groovy/Kotlin unlike anything you'd write in application code. For example, your IDE support in Gradle-Kotlin is much worse than what you get in normal-Kotlin. Despite Gradle-Kotlin being the same language and same IDE, it's a much worse experience writing it, it's much easier to make mistakes, and so you are right to minimize the code you write in it.
In Mill, the build scripts are in Scala, but that's not the important part. The important part is you write normal code using classes, methods, and overrides. IDEs are very good at navigating classes, methods and overrides, and developers are very familiar with working with classes, methods, and overrides. And so build code in Mill feels as comfortable as your application code in Java/Scala/Kotlin: same quality of IDE experience, same typechecking, even can use the same Java third-party libraries (if you wish).
So it's understandable you hate programming your build. In Maven or Gradle or SBT, I'd hate programming my build as well. What Mill offers is that you can program your build where necessary without the hate that comes with doing so in other build tools!
I am old. I used ant, ported to maven when I discovered it (yes Maven 1). Always stuck with it. Maven has been a really nice consistent build tool. I never had to build something so complex it did not know what to do. And no I never used the ant plugin; it's an anti-pattern for Maven. Custom maven plugin's is perfectly fine (I never needed it) but if you need them you probably are doing it wrong (IMHO). My build are never that complex as I mainly write reasonably standard software for businesses.
Nice how you say "your IDE support in...", to refer to IntelliJ. I avoid that. I use Visual Studio Code and Netbeans.
BTW: writing something in bash means it will work for at least a decade. All people on my team will know how it works and understand it and maintain it. Introducing something new mean we all need to learn it. Yesterday my build for a simple website failed because gulp was updated and I ported the entire build to a bash script. It was faster and way simpler.
Let me make it clear here: I hate Grails. I think you have the same gripe as I do. They keep changing the DSL so you need to rewrite your scripts. Since it is a programming language each project has a different build even though most projects are the same. Because people do the same stuff differently.
When I have an issue with maven I will try Mill. Promised.
IMO there should be one build system for each language and it should be good at the basics, and if you need more then you should write your own (minimal-dependency) build script in the same language as the code you're building, and in that system you do things like generate build files for the big, complicated thing you're trying to build.
In Java terms, if you have a big complicated Java repo that requires lots of steps to build, you should have a separate Java project in there just for building the main repo. That separate Java project should be built and run with maven, and that separate project can do all kinds of fancy things, but ultimately it will be generating maven projects or calling maven with special command line parameters or something like that.
I even put the logic for CI in my build project like this. It makes everything reproducible and debuggable. How cool is it to be able to put a breakpoint in your build script? How about stepping through your CI code? Things are way simpler this way.
I eschew frameworks in this custom build tool, because the build code should look conventional for whatever language it's written in.
> I eschew frameworks in this custom build tool, because the build code should look conventional for whatever language it's written in.
That's really the case with Mill as well, at a deeper more-meaningful level. Mill builds are all built around objects, classes, methods, and overrides.
Maven is XML. Do you write large codebases in XML? Gradle is Groovy/Kotlin, but it's an odd Groovy/Kotlin dialect or DSL that looks totally unlike application codebases, and your IDE is unable to understand and navigate effectively.
Mill is Scala, sure, but what's important is that it is architected around objects, classes, methods, and overrides. Those are the core abstractions when working with Mill, which are the same core abstractions as any Java/Scala/Kotlin application. Thus IDEs are able to navigate Mill builds, profilers like JProfiler or Yourkit can work on Mill builds, and humans who learned Java 101 in college are immediately familiar with the structure and fundamental abstractions of Mill.
I've seen this taken to extremes (eg Kubernetes deployment as Java code simply because the service is in Java) and it's... terrifying. It's justified in the pre-internet era, where learning two languages would require two trips to the library. These days, I really don't see why not using the better tool for whatever part of the job you're doing. It creates a mountain of complexity for no reason other than "I only want to touch language X"
this is a horrific build file: https://github.com/swaldman/c3p0/blob/0.11.x/build.mill
You can write bad code in any language. Don’t ask me how I know :)
Yeah, hard to see how better this is than Gradle. If anything, it's worse by the mere fact this is Scala, but it's really so incredibly verbose to accomplish basic tasks such as upload to Sonatype.
making it easier to generate maven poms I thought was a reasonable idea 18 years ago but gradle and other tools all went down the "making the build Turing complete" path.
https://github.com/spullara/graven/blob/master/pom.groovy
I don't know why I don't see anyone ever mention JeKa. https://jeka.dev/
You don't have to learn a separate language or some weird config.
This is the first time I have heard of it.
Honestly, I'm getting tired of the endless parade of "better" build tools. Maven works fine for most of what I do, even if it's occasionally painful. Gradle is... well, Gradle has its moments.
That said, Mill's performance claims sound interesting. If it really can cut build times by 3-6x, that's really amazing. I've wasted too much time in my life waiting for builds, especially on larger projects.
The Scala thing is a bit of a turn-off though. I get that you don't need to write Scala yourself, but now I'm dragging in the Scala ecosystem just to build my Java project? Feels heavy.
The IDE integration sounds nice in theory - being able to actually navigate and understand your build in IntelliJ would be pretty sweet.
Still not sure the switching costs are worth it unless you're really hitting Maven/Gradle pain points. But if I was starting a new project from scratch, might be worth a look.
When has build time been an issue though? Running tests have taken the most time by far on every project I’ve ever worked on.
Author here. Mill also helps with running tests. Apart from all tests running parallel by default with heuristics to try and maximize performance (https://mill-build.org/blog/11-jvm-test-parallelism.html), Mill also can selectively execute tests in CI based on code changes (https://mill-build.org/mill/large/selective-execution.html), and provide performance profiles so you can see what is taking up time and improve it (https://mill-build.org/mill/depth/parallelism.html#_mill_chr...). All this is built in and comes for free, with the end result that even tests can take much less time in Mill than they do in other build tools
Thanks for jumping in, that’s actually really helpful context. Parallelism and selective test execution built-in sounds like a huge win, especially for CI and larger teams. I’ll admit the Scala dependency still makes me hesitate a bit for pure Java projects, but clearly Mill’s design is tackling real pain points head-on. Definitely tempted to try it out on a greenfield project.
True. Tests usually dominate the time budget. But from what I've experienced, slow builds still hurt, especially when you're iterating frequently or working across multiple modules. Those 10–20 seconds of wait adds up fast when you're in the zone and trying things out.
Is it bootstrappable? I mean really bootstrappable, not "the bootstrap script will download a binary from some server". This is where both Maven and Gradle fail (at least the last time I checked). Kotlin and Scala also have the problem that their compilers themselves are not bootstrappable either.
I just want `uv`/`npm`/`crate` for JVM. Is that too much to ask?
Can someone from Astral fix Java too, please?
Mill is in many ways trying to be `uv` for the JVM. 3-6x faster than the existing tools, bundling everything necessary builtin without plugin-hell, superior IDE support and toolability than existing tools. If you want a `uv` for Java, you should try out Mill!
JVM build tools have to do much more than just dependency management because of how the JVM works (e.g. packaging resources). It's not really comparable.
It is literally creating zip file with jar extension and files inside of it. I’m pretty sure if they managed to figure out clusterfuck of Python, they can do the same for Java.
That's like saying "programming is just creating files with content".
Python is a purely interpreted language that runs in the context of your file system. Java code needs to be compiled and packaged, you have to write a manifest, you have to decide whether to include dependencies (fat jar) or not, or maybe you want to create a war (if you're stuck in the 2000s)... then what about maybe distributing the sources too?
Also, Maven works. Gradle works. Mill probably works too. The reason uv is a success is that everything Python had before was a broken mess. When people complain about JVM build tools, they come from a different position, not from one of "stuff is completely broken".
> Java code needs to be compiled and packaged, you have to write a manifest, you have to decide whether to include dependencies (fat jar) or not, or maybe you want to create a war (if you're stuck in the 2000s)... then what about maybe distributing the sources too?
I would probably be amazed at complexity of this, if I never touched Java in life.
The things you’re describing are literally a matter of a bash script to stitch together javac, jar and a couple of Unix tools.
Gradle is a monstrosity and abomination against the world. The only reason it exists is to give job to devex engineers and I’m willing to die on the hill. Maybe if it was rewritten in something faster and less memory hungry I’d be able to tolerate all this needless complexity, but until this piece of shit stops hogging all my memory and CPU time – its place is in the dumpster.
Great, then why don't you ship your software with bash scripts then?
Because Android is so entangled with Gradle that it is impossible to do anything without it.
I'm still happy with ant.
It works well and I don't have huge dependencies.
I only use mail, db's, JSON and QR, so I can upload the dependencies into the repo and they haven changed for decades.
Anyone have a comparison to Bazel? They only compare to JVM-centric tools eg Maven, sbt, Gradle.
Bazel is just such a nightmare for me. It's amazing when someone understands it really well and can set things up. But for anything short of that, being on the hook to fix or debug things makes it a nightmare. That and trying to port anything over from sbt, like scalafix for instance, to bazel is a pain.
Also too, bazel has this issue of googleability? like I feel like I can take any build issue I've run into in sbt and find the solution and an example by just searching, but with Bazel, anything outside of the happy path is a recipe for pain
I've been trying to write a about Bazel from first principles to help out. https://fzakaria.com/
My experience with Bazel (7 years rolling it out and maintaining it in a large company) is that it provides huge value for larger teams/codebases, but at a huge cost in complexity. e.g. the three I rollouts I was closest to each took ~2 person-decades to happen; might be easier now than it was in 2016/2017, but Bazel hasn't really gotten simpler over the years
Mill is intended to be much easier than Bazel. Most people would not use Bazel for a 1-person project, or even a 5-person project, and it only starts pencilling out once you have 50-100 engineers on the team. Mill in contrast works great for small projects and medium projects as well.
One way I'd look at it is that Mill works great for 1-500 person projects, while Bazel works great for 100-5000 person projects. There's some overlap in the middle, but fundamentally they target different kinds of users who have very different constraints and requirements
Where's the obligatory Nix comment though ? /sarcasm (I really like Nix. It brings me joy.)
Maintainability: Mill’s config and custom logic is written in concise type-checked JVM code - Inside - "why mill uses scala?" LOL))
Half expected this to be built in Rust...
https://mill-build.org/mill/depth/parallelism.html#_mill_chr...
This is kinda funny. Generating a profile that is meant to be visualized with a web browser's built-in profiler, not even an HTML file. I guess if it's already built and works well ¯\_(ツ)_/¯
That profiler is very powerful and used to analyze beasts like Android and Chromium. It's a pro, not a con.
https://ui.perfetto.dev
I had no clue it was its own independent, comprehensive project. Very cool.
Reminded me of this: https://xkcd.com/927/
Please anything beyond gradle. Didn't hate maven but didn't love it either. Happy to investigate alternatives
Please try out Mill! We have a lot of Maven/Gradle/SBT refugees in the community who are quite happy with Mill, so perhaps if you give it a try you'll find a build tool you can be happy with as well