Jump to the Wiki
for latest documentation.
Jump to the project card wall
to see upcoming changes.
Modern Java/JVM Build Practices is an article-as-repo on building modern Java/JVM projects using Gradle and Maven, and a starter project in Java (the advice works for non-Java languages on the JVM though details may change).
Important
See the wiki for all pages and sections. This README is only introduction, motivation, and project status. You can use the table of contents below to quickly jump to bits that interest you.
Regardless of what language(s) or build tool(s) you choose, and you should treat your build and your pipeline as worthy of your attention just as you would your project source code: If it doesn't build right for customers as it does for developers, you have something to think about.
I'm showing you practices and tools that help you make your build and pipeline to production as first-class the same as your own source code. An example of this philosophy for a non-Java language is Clojure.
Your focus, and the focus of this article, is best build practices and project hygiene, and helping you have local work that is identical in production. This project is agnostic between Gradle and Maven: discussion in each section covers both tools (alphabetical order, Gradle before Maven). See My Final Take on Gradle (vs. Maven) for an opinionated view (not my own).
This is not a JVM starter for only Java: I use it for starting my Kotlin projects, and substitute complilation and code quality plugins. Any language on the JVM can find practices and tips.
Note
Scala and Clojure have their own prefered build tools not covered here; however, the advice and examples for your build pipeline are intended to be just as helpful for those JVM languages. Groovy and Kotlin can use the examples directly (they both tend towards the Gradle option on build tools).
As a guide, this project focuses on:
- A quick starter for JVM projects using Gradle or Maven. Fork me, clone me, copy/paste freely! I am Public Domain
- Discuss—and illustrate (through code)—sensible default practices; highlight good build tools and plugins
- Document pitfalls that turned up. Some were easy to address after Internet search; some were challenging (see "Tips" sections)
- Do not be an "all-in-one" solution. You know your circumstances best. I hope this project helps you discover build improvements you love. Please share with others through issues or PRs
- Shift problems left — Find issues earlier in your build—before you see them in production
- Make developer life easier — Automate build tasks often done by hand: get your build to complain (fail) locally before sharing with your team, or fail in CI before deployment
But ... you must judge and measure the advice here against your own systems and processes. Some things (many or most things) may work for you: keep an eye for things that do not work for you.
A project starter has several goals:
- Help a new project get up and running with minimal fuss.
- Show examples of best practices.
- Explain the why for choices, and help you pick what makes most sense for your project.
This starter project is focused on build:
- Easy on-ramp for new folks to try out your project for themselves
- Support new contributors to your project that they become productive quickly
- Support current contributors in the build, get out of their way, and make everyday things easy
This starter project has minimal dependencies. The focus is on Gradle and Maven plugins and configuration so that you and contributors can focus on the code, not on setting up the build.
- I'm not a great programmer; I'm just a good programmer with great habits. — Kent Beck
- Make it work, make it right, make it fast — C2 Wiki
Note
NB — This is a living document.
The project is frequently updated to pick up new dependency or plugin
versions, and improved practices; the README.md
and
wiki update
recommendations.
This is part of what great habits looks like: you do not just show love
for your developers and users, but enable them to feed back into projects
and help others.
See Reusing this
project
for tips on pulling in updates.
(Credit to Yegor Bugayenko for Elegant READMEs.)
You should "kick the tires" and get a feel for what parts of this project you'd like to pull into your own projects and builds. You run across lots of projects: Let's make this one helpful for you.
After cloning or forking this project to your machine, try out the local build that makes sense for you:
$ ./gradlew build # Local-only build
$ earthly +build-with-gradle # CI build with Earthly
$ ./mvnw verify # Local-only build
$ earthly +build-with-maven # CI build with Earthly
Notice that you can run the build purely localy, or in a container?
I want to convince you that running your builds in a container fixes the "it worked on my machine" problem, and show you how to pick up improvements for your build that helps you and others be awesome.
See what the starter "run" program does:
$ ./run-with-gradle.sh
$ ./run-with-maven.sh
Both (after building if needed) should print:
TheFoo(label=I AM FOOCUTUS OF BORG)
A "starter" program is the simplest of all possible "smoke tests", meaning, the minimal things just work, and when you check other things, maybe smoke drifts from your computer as circuits burn out1.
- IN PROGRESS: Move sections from
README.md
to the GitHub wiki. This is for breaking up an overlong (11k words) README into digestible sections. - Batect: Remove support for Batect as the author has archived that project. Please use Earthly for local containerized builds. I'll be researching other options, and updating to show those and examples. Advice remains the same: Run your local build in a container for reproducibility, and have CI do the same to exactly repeat your local builds.
- This project uses JDK 21. Here is the previous commit using JDK 17
- Gradle: build with Gradle 8.x.
- Gradle: remove use of
testsets
plugin for integration testing in favor of native Gradle. This is in support of Gradle 8 and may be helpful in seeing changes you need for Gradle 8 support.
- Recent significant changes
- Introduction
- Using this project
- What is a build pipeline?
- Commits
- Cycle time
- Getting your project started
- The JDK
- Use Gradle or Maven
- Setup your CI
- Keep local consistent with CI
- Maintain your build
- Choose your code style
- Generate code
- Leverage the compiler
- Use linting
- Use static code analysis
- Shift security left
- Leverage unit testing and coverage
- Use mutation testing
- Use integration testing
- Debugging
- Performance problems
- Other problems
- Samples
- Going further
- Credits
See CONTRIBUTING.md
.
Please file issues,
or contribute pull
requests!
I'd love a conversation with you.
Many thanks to:
- Dan Wallach for discussions on security
- Kristoffer Haugsbakk
- Sam Gammon
- Sergei Bukharov
All suggestions and ideas welcome! Please file an issue. ☺