Java best practices

Keeping your applications current has become important and difficult. This document provides some basic steps for Java developers to get started with DevOps practices. It is by no means an exhaustive list. Most of these ideas come from the DORA DevOps research and assessment studies which provide a fuller overview of best practices. Other sources include Accelerate: The Science of Lean Software and DevOps: Building and Scaling High Performing Technology Organizations by Nicole Forsgren PhD, Jez Humble, and Gene Kim; and Software Engineering at Google curated by Titus Winters, Tom Manshreck and Hyrum Wright.

Before reading this page it's recommended that you read: Setting Up a Java Development Environment.

Every development organization's needs are unique, but a basic system could be constructed using one of the following tech stacks:

Sample Tech Stack 1 Sample Tech Stack 2
  • Cloud Source Repositories, Github, or Bitbucket for source code.
  • Whitesource RenovateBot for keeping artifacts and libraries up to date.
  • Cloud Build for unit / presubmit testing
  • Cloud Build for integration testing
  • Cloud Build for deployment
  • Github for source code.
  • Github Dependabot for keeping artifacts and libraries up to date.
  • Github Actions for presubmit testing
  • Github Actions for integration testing
  • Github Actions for deployment

A simple system built with these components can allow you to improve quality and cycle time. It can also allow you to easily keep your code up to date with the latest bug fixes and security updates.

Version Control

Research (page 17 , page 14 , page 31 , and page 60) has found that version control for source code combined with automation and testing predicts improved quality and many other benefits.

Github, Gitlab, and Bitbucket are also good homes for your source code.

Automated Testing

Continuous testing can be critical to your success using most of these techniques. For additional thoughts about testing best practices, take a look at the SRE Book chapter Testing for Reliability and the Google Testing blog.

Java developers are mostly concerned with automated unit tests and integration testing. JUnit, Testing with Spring, Apache Maven Surefire, and Gradle Java testing are useful resources for Java developers.

Continuous Integration / Deployment Automation

Continuous integration and Deployment automation power modern DevOps jobs. Build, Test, and Deploy.

  • Cloud Build [Quickstarts] [Java Specific] [Deployment] [Trigger] provides a free (120 build minutes / day) or low cost easy-to-use build system that can be easily customized for most jobs.
  • Tekton is an open source project that lets you easily adapt the Cloud Build ideas to your systems.
  • Spinnaker is an open-source, multi-cloud continuous delivery platform that helps you release software changes with high velocity and confidence. It helps you manage the process of releasing and rolling back complex software systems.
  • Github's Actions is a third-party solution that lets you easily setup tests and run them on Github.
  • There are also many other solutions such as Gitlab, Circle CI, and Travis CI.

Cloud Client Libraries

There are many ways of using Google services, but the preferred way is to follow the instructions on the Cloud Client Libraries page. For Java, the Libraries-BOM can help ensure that you're using compatible versions of each artifact.

If you choose to pick your own versions of client libraries, it is possible that an incompatible artifact might get selected. This is known as the diamond dependency problem. If you still must pick your individual libraries, then update them one at a time and test to see if the update introduced an error. The latest versions are always listed on this page, or can be found by searching Maven-Central.

Keep dependencies current

To protect against malicious actors, it's critical that you keep your dependencies up to date. There are many third party tools that help you with this:

These tools, when configured properly, help you keep your dependencies up to date. When you combine automated testing and Continuous Integration / Continuous Deployment the flow becomes:

  • Dependency automation proposes a change to your source control.
  • The continuous build system builds and tests the change.
  • A human reviews the proposal, and if acceptable, accepts the change, possibly along with other changes.
  • After changes are accepted, a proposal is made to the Continuous Delivery system to release the code to production. (Or your custom process is followed.)

Use a supported Java Runtime Environment (JRE)

The JRE, a subset of the Java Development Kit, sits on top of your operating system and provides the software and resources required to run a Java application. Most users prefer to use the latest LTS version in production so that they have access to updates, security, and bug fixes. It is usually possible to update to a later JRE even if your code is compiled against an earlier JDK.

If you work with multiple JDK versions, SDKMAN! can helps you use and manage different JDK versions.

Using containers (Google Kubernetes Engine, Cloud Run, Anthos clusters)

If you're using Docker containers with either RenovateBot or DependaBot, the bot periodically proposes updates to your JRE and your JDK – you'll want to keep the JDK and the JRE on the same version.

We recommend using Jib to containerize your Java applications in most circumstances.

If you manually update your Dockerfile, just change the JRE to latest and rebuild.

Using Compute Engine

This tends to be very application specific. We recommend using a startup script. To upgrade, you should update the script.

App Engine flexible environment

Supports Java 8 only.

App Engine Standard

See Migrating your App Engine app from Java 8 to Java 11.

Use a LTS version of the Java Development Kit

The JDK is a set of tools for developing Java applications. New language features are tied to a particular JDK. We recommend you pin your usage to a JDK with Long Term Support (LTS), upgrading to the next LTS version when it's appropriate for your application. We recommend that you use the latest minor release of the pinned major LTS release.

Most users will want to keep the JDK and JRE in sync. Sometimes that isn't possible (for example when the JDK is no longer supported), and you need to compile with a later JDK and run on an earlier JRE.

To do this with Maven:

Set the language level you want to code in and the target JRE. Update your pom.xml file as follows (for Java 8): xml <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target>

To update to Java 11 you would change it to:

    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>

If you're using Gradle, then update your build.gradle file for Java 8 with:

compileJava {
  sourceCompatibility = 1.8
  targetCompatibility = 1.8
}

Or for Java 11:

compileJava {
  sourceCompatibility = 11
  targetCompatibility = 11
}

Note that for Java 8 and earlier the versions had a 1. prefix (1.7 for Java 7, 1.8 for Java 8) that was dropped after Java 8.