DEV Community

Cover image for Is your Java code really production-ready?

Is your Java code really production-ready?

scottshipp profile image scottshipp ・5 min read

Is your Java really ready for prime time in production?

Here are five tips for writing Java that can withstand the abuses of your users!

Duke, the Java mascot, rocking out

Write dumb code

My article Why Senior Devs Write Dumb Code and How to Spot a Junior From A Mile Away is still shared often on Twitter even though its almost two years old. The advice to write dumb code isn't mine, it's Java architect Brian Goetz's, who said:

Often, the way to write fast code in Java applications is to write dumb code — code that is straightforward, clean, and follows the most obvious object-oriented principles.

This is an extension of the KISS principle and basically means: avoid being clever. Write simple code the way it was meant to be written. Know and write effective Java.

But there's another advantage to "dumb code:" the compiler knows how to optimize it. So you pick up performance improvements as well.

Start with the most defensive option

You may have heard some advice that you should limit the visibility of methods or variables and yadda-yadda-yadda something about encapsulation. It all sounds kind of boring, scientific, and yawn-inducing.

Duke, the Java mascot, experimenting in a chemistry lab

Meanwhile, most Java tutorials will provide examples of classes where the only accessibility modifier being used is public. IDE class generation also seems to default to public classes and methods. If you fully generate a class in an IDE, you're bound to end up with something like this:

public class Contact {
    private String name;
    private String emailAddress;

    public Contact(String name, String emailAddress) { = name;
        this.emailAddress = emailAddress;

    public String getName() {
        return name;

    public void setName(String name) { = name;

    public String getEmailAddress() {
        return emailAddress;

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;

On the contrary, I advise devs to start with the most defensive option possible and relax to a less defensive option only when necessary. So for the above example, make each member variable final and avoid implementing getters or setters unless you can prove you need them.

Furthermore, default to package-private for any constructors or methods. It may be helpful to review Java access modifiers.

Thus, the prior code becomes the following before it becomes anything else:

class Contact {
    private final String name;
    private final String emailAddress;

    Contact(String name, String emailAddress) { = name;
        this.emailAddress = emailAddress;

A dev writing code like that is thinking about scope and immutability defensively, which is a good thing. Furthermore, that leads to my next point.

Duke, the Java mascot, giving a thumbs up

Prefer to expose behavior over state

You may have noticed that you can't do anything with an instance of Contact that's written according to my "starting point" guideline. Exactly! So you may be wondering where do you go from there? And my answer is: prefer to expose behavior over state.

This forces the question, what do you want to do with Contact? There's a big difference between a data transfer object whose purpose actually is to carry state somewhere and a normal object whose purpose should be to provide behavior.

Perhaps the latter is hard to imagine, so let me provide the example of a simple mail app where what we need from our contact list is the ability to email a contact. In a simple mail app, I would recommend making Contact the source of that behavior.

public class Contact implements Emailable {
    // ...

    public EmailResult sendEmailTo(Contact destination, String body) {
        // ...

As the application evolves, maybe this approach goes extinct. But its a good idea at this time. Notice that the name and email address which form the state of the Contact object are also used within that object. This organizes state and behavior together in a tidy way and negates the need to share any state publicly.

And if the purpose of Contactis to transfer data, make it an immutable object which is both thread-safe and protected from accidental programming errors that set its values incorrectly.

Duke, the Java mascot, holding a globe

Write comprehensive tests

Testing is often the first step put on the chopping block under deadline pressure. This unfortunate result of human psychology is potentially deadly. Java in large, complex applications should be considered buggy until proven otherwise. And only tests can prove otherwise.

Java in large, complex applications should be considered buggy until proven otherwise.

Get to know a test framework like JUnit and related tools like Mockito.

Two great resources to learn great testing practices include xUnit Test Patterns by Gerard Meszaros and Pragmatic Unit Testing in Java 8 by Jeff Langr.

Fix linter and IDE warnings

In addition to what the compiler tells you, let the IDE and static analysis tools like CheckStyle inform you of ways your code can be more modern, secure, and correct. IntelliJ IDEA has a great code inspections feature that is like having a Java expert at your side as you build your application. Don't waste that advice!

IntelliJ Code Inspections, courtesy of IntellIJ Hel
IntelliJ Code Inspections, courtesy of IntellIJ Help

Production Java!

Following these tips will result in production-ready Java! That is: Java that can withstand the wild. Java is a great language with a helpful compiler and a strong ecosystem of tools and libraries to help increase and insure quality. By following best practices and making use of these tools, I'm sure that any application can be improved, and you as a Java dev can get busy with what's important--building your application--rather than spending all your time fixing bugs that could have been caught earlier. Good luck!


Editor guide
connorphee profile image
Connor Phee

Great post, Scott! I love the idea of exposing behavior over state. I think the same could be said for implementation as well. If there are getters and setters for everything, consumers are able to see everything that the class is using to produce the end behavior.