binkley / modern-java-practices

Modern Java/JVM Build Practices
The Unlicense
920 stars 69 forks source link

<img src="./images/public-domain.svg" alt="Public Domain" align="right" width="10%" height="auto"/>

Read the book! (this jumps to the wiki)
Jump to the project card wall to see upcoming book and code changes (the card wall tracks Issues for the project).

Modern Java/JVM Build Practices

<img src="./images/modern-agile-wheel-english.png" alt="Modern Agile" align="right" width="20%" height="auto"/>

Gradle build Maven build vulnerabilities coverage pull requests issues license

[!WARNING] For those using the DependencyCheck plugins for Gradle or Maven, over the July 1st weekend the upstream API for fetching security CVEs changed a major version, and stopped supporting older versions of the data. To get up to date, update to at least version 10.0.0 of either the Gradle or Maven plugin.

After the update, the first build will take a very long time, but should perform normally afterwards. And during the first week or so after this change, you may see multiple connection failures as OWASP NVD is overloaded with projects all catching up at the same time. The Maven plugin shows progress as CVE records are pulled: to see progress with the Gradle plugin, use the --info command-line flag.

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:

Two recurring themes

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.

What is a Starter?

A project starter has several goals:

This starter project is focused on build:

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.

Summing up

[!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.)


<img src="./images/try.png" alt="Run from a local script" align="right" width="20%" height="auto"/>

Try it

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 locally, 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.

[!NOTE] This project uses NVD to check for CVEs with your dependencies which can take a long time to download. You can speed up your build time by requesting an NVD API key (it can take quite a while to fetch the CVEs list or update it, and may fail with 403 or 404 without a key).

When you request a key, NVD sends you an email to confirm your identity, and then share an API key web page. See Shift security left for more details.

See what the starter "run" program does:

Both Gradle and Maven (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 out[^1].

[^1]: No, I'm just kidding. Amazon or Google or Microsoft cloud would have quite different problems than "white smoke" from computers[^2].

[^2]: Actually, this really happened me in a data center before the cloud. We had to get out a fire extinguisher.


<img src="./images/changes.png" alt="Changes" align="right" width="20%" height="auto"/>

Recent significant changes

(For detailed changes in the example code, browse the commit log.)


<img src="./images/table-of-contents.png" alt="Table of Contents" align="right" width="20%" height="auto"/>

Table of Contents


Contributing

See CONTRIBUTING.md. Please file issues, or contribute pull requests! I'd love a conversation with you.


Credits

Many thanks to:

All suggestions and ideas welcome! Please file an issue. ☺