loading...
Cover image for Enforcing Java Coding Styles

Enforcing Java Coding Styles

ketiko profile image Ryan Hansen ・4 min read

In any large organization there is often a desire to be able to move engineers between teams and projects. Having a consistent coding style will often make that easier. There are several reasons for this and many people have articulated them far better than I could. I'll assume you already have or want to begin using a style guide.

I'm a firm believer that if you are going to have a style guide you have to enforce it. As engineers, we automate everything. Manually spot-checking pull-requests to ensure consistency is fraught with errors. If you are going to enforce it, then you should make it as easy as possible through tooling. Engineers should not be spending their time formatting if blocks or sorting import statements. We want to enforce that style but not make it time-consuming.

The Ruby language has a pretty standard style guide. There is a great CLI tool called Rubocop that will check against this style guide for violations. But my favorite part of Rubocop is rubocop --auto-correct. This command will fix almost all of your violations for you every time.

Java has something similar. One of the most widely used style guides is from Google. You can use fmt-maven-plugin to check for violations. And just like Rubocop, it can auto-correct some of your code for you by running mvn com.coveo:fmt-maven-plugin:format.

There are several other static analysis tools in Java. Some of my favorites are maven-pmd-plugin, maven-checkstyle-plugin, and spotbugs-maven-plugin. I won't deep dive into any of these tools, but I highly recommend you take a look at them.

Getting back on track...

Ok, so we have a style guide. We have lots of tools to check it, automate auto-corrections, and analyze for other inconsistencies. So what now?

As always, your CI is a great place to enforce it!

Here is a sample bash script I use. It will return a non-zero exit code if any of the commands inside it fail. This makes it perfect to throw into any CI flow.

#!/bin/bash
set -eo pipefail

mvn fmt:check
mvn checkstyle:check
mvn pmd:check
mvn spotbugs:check

mohanpedala has a great explanation of set -eo pipefail.

Locally you can setup a git hook to run mvn com.coveo:fmt-maven-plugin:format so that you auto-correct some styles before you even push changes.

Here is a sample pom.xml with these configured...

<plugin>
    <groupId>com.coveo</groupId>
    <artifactId>fmt-maven-plugin</artifactId>
    <version>2.9</version>
    <configuration>
        <sourceDirectory>src/main/java/com/ketiko</sourceDirectory>
        <testSourceDirectory>src/test</testSourceDirectory>
        <style>google</style>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>format</goal>
            </goals>
        </execution>
    </executions>
</plugin>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-pmd-plugin</artifactId>
    <version>3.12.0</version>
    <configuration>
        <verbose>true</verbose>
    </configuration>
</plugin>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-checkstyle-plugin</artifactId>
    <version>3.1.0</version>
    <configuration>
        <configLocation>google_checks.xml</configLocation>
        <consoleOutput>true</consoleOutput>
        <failOnViolation>true</failOnViolation>
        <failsOnError>true</failsOnError>
        <includeTestSourceDirectory>true</includeTestSourceDirectory>
        <linkXRef>false</linkXRef>
        <logViolationsToConsole>true</logViolationsToConsole>
        <violationSeverity>warning</violationSeverity>
        <suppressionsLocation>
            checkstyle-suppressions.xml
        </suppressionsLocation>
    </configuration>
</plugin>
<plugin>
    <groupId>com.github.spotbugs</groupId>
    <artifactId>spotbugs-maven-plugin</artifactId>
    <version>3.1.12</version>
</plugin>

** Update **

elmuerte brought up an excellent point in the comments that I wanted to reply to. Maven has several phases during its build process. The verify phase is described as follows:

Run any checks to verify the package is valid and meets quality criteria.

Using this you can run a single maven command in your CI, mvn verify. Simply update those plugins to run during that phase and you are all set! This is an excellent way to accomplish the same thing.

However, maven phases run in order. That means that verify runs after the test phase. I like to run the quality checks first thing in my CI without waiting for my test suite to complete.

You could just run those goals individually in your CI as well, without the bash script. When I wanted to run them locally I kept forgetting all the checks I had configured in the CI. So I created the bash script to quickly run them locally and reuse it on the CI.

Posted on by:

Discussion

markdown guide
 

Why not simply execute those plugins during the verify phase, making mvn verify the only command you need.

 

That is another great way to do it! Doing it in the verify phase is specifically where maven defines running these types of checks. However I like to run these checks early in the CI and fail fast before waiting for all my tests to run in the phases preceding verify. If you don't have a preference on the order in your CI then I would definitely recommend doing like you suggest. I should have called out the reasons why I chose this way. Thanks for the question!