Checkstyle, PMD and Findbugs, why and how?

In software development context code quality plays an important role in making code base well comprehensible by all developers, as it measures the usefulness and efficiency of the final product.

Code quality distinguishes between high-quality code, that is reliable, efficient, maintainable, easy to understand, refactored, documented, and performant; and low-quality code that is more prone to bugs and difficult to test properly which can create issues in the software. Different teams may use different definitions, based on context, but we all agree that having quality code is as essential as having a functional software.

How Can We Achieve a Good Code Quality?

Given the usual constraints already present in software development, maintaining a quality standard should not be regarded as a time-consuming task, but rather as one of the main goals when developing software; and to ensure this in the most effective way, several tools can be used. PMD, FindBugs and Checkstyle, are the most popular open-source code analyzers, they are extensively used in Java development to improve the codebase and identify potential vulnerabilities along with design flaws; every tool has its feature, purpose and strength, targeting a specific type of coding rules. We will approach the said tools separately.

In this first article, we will start with Checkstyle by detailing its characteristics, showing an example of use and initiating a basic configuration.

What is Checkstyle?

Checkstyle is a static code analyzer for style and conventions, to help programmers write Java code that adheres to a defined coding standard. It automates the process of reviewing and checking Java code to spare humans of this repetitive task. This makes it ideal for projects that want to enforce a coding standard.
Here are some things Checkstyle will catch:

  • Naming conventions
    Example using default configuration AbstractClassName

    abstract class AbstractNimbleWaysClass {} // OK
    abstract class NimbleWaysClass {} // violation, it should match pattern "^Abstract.+$"
    
  • Whitespace

    Example using default configuration WhitespaceAfter

    public void myTestWhitespaceAfter() {
        if (one) {              // OK
            //...
        } else if(two) {        // violation, space after 'if' is required
            //...
        }
    
        testMethodWhitespaceAfter(one, two); // OK
        testMethodWhitespaceAfter(one,two); // violation, space after ',' is required
    }
    
  • Missing Javadoc on public methods

  • Checks parameters of a method (number, naming ...)

Configuration

To integrate Checkstyle with our codebase, we need to follow these steps:

  1. Add checkstyle as a plugin in pom.xml file
  2. Create configuration file checkstyle.xml
  3. Run checkstyle command

Let's start.

  1. Add checkstyle as a plugin in pom.xml file
<plugins>
	...
	<plugin>

		<groupId>org.apache.maven.plugins</groupId>
		<artifactId>maven-checkstyle-plugin</artifactId>
		<version>3.1.1</version>

		<configuration>
			<failOnViolation>true</failOnViolation>
			<configLocation>checkstyle.xml</configLocation>
			<excludes>.mvn/**/*</excludes>
		</configuration>

		<executions>
			<execution>
				<goals>
					<goal>check</goal>
				</goals>
			</execution>
		</executions>

	</plugin>
	...
</plugins>

groupId: this tag is used to identify a group that created the project.
artifactId: this tag indicates a unique name used to name the artifacts to build.
version: version of the artifact generated by the project.
failOnViolation: if true, fail the build on a violation. default value is true.
configLocation: we specified the location of the XML configuration to use.
excludes: specifies the names filter of the source files to be excluded for Checkstyle.

You find all the parameters: here.

  1. Create configuration file checkstyle.xml

A simple checkstyle configuration example:

<?xml version="1.0"?>
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
	"https://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">

	<!-- Configure checker to use UTF-8 encoding -->
	<property name="charset" value="UTF-8"/>

	<!-- Configure checker to run on files with these extensions -->
	<property name="fileExtensions" value="java"/>

	<!-- Size File Violations -->
	<module name="FileLength">
		<property name="max" value="500"/>
	</module>

	<module name="TreeWalker">
		<module name="MethodLength"/>
		<module name="ParameterNumber">
			<property name="max" value="4"/>
		</module>
	</module>
</module>

module: A configuration file is mainly made up of modules. A module has a name attribute that represents what it does.
property: Properties of a module control how the module performs its task, you are not required to define a property in the configuration document if the default value is satisfactory.
FileLength: Checks for long source files (500 lines).
MethodLength: Checks for long methods (default value 150 lines).
ParameterNumber: Checks the number of parameters of a method (4 parameters).

Root module Checker has child FileSetChecks FileLength, TreeWalker. Module TreeWalker has submodules MethodLength and ParameterNumber.

  1. Run checkstyle command

Now that our Maven plugin is configured, we can check our code by running the command, it will scan the files for violations and the build will fail if any violations are found.

 mvn clean verify

We can run to check our code

mvn checkstyle:check

We can generate a report Checkstyle for our code by running the command, once construction is complete, the report is available in the target/site folder under the name checkstyle.html

mvn checkstyle:checkstyle

Example

Let's show you a concrete example of how we can use checktyle

<?xml version="1.0"?>
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
	"https://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">

	<property name="charset" value="UTF-8"/>
	<property name="fileExtensions" value="java"/>

	<module name="LineLength">
		<property name="max" value="80"/>
		<property name="ignorePattern" value="^package.*|^import.*"/>
	</module>

	<module name="TreeWalker">
		<module name="WhitespaceAfter"/>
		<module name="MethodLength">
			<property name="max" value="15"/>
		</module>
		<module name="MissingJavadocMethod"/>
		<module name="MethodName"/>
		<module name="LocalVariableName"/>
		<module name="EmptyBlock"/>
	</module>
</module>

This configuration allow us to:

  • Detect overflow of number of character per line as defined 80 character in our configuration
  • Detect lack of whitspaces after if or ,
  • Detect public methods without Javadoc
  • Detect method and variable naming convention
  • Detect empty bloc

After executing our configuration on code above, several errors are revealed as shown:

We integrate modifications in our code:

Correcting those errors improve the quality of our code since all criteria mentioned in our configuration code are respected:

This is quite a basic example. In case you're interested in seeing more of what Checkstyle can offer in terms of possibilities, you'll be able to find more interesting feature in the following documentation: https://checkstyle.sourceforge.io/checks.html

Conclusion

In summary, code quality is the most beneficial investment you should initiate to ensure a long term success of your software. Trying to incorporate better coding standards across your team and making use of automated tools in each significant step of your code production will undoubtedly help you maintain your software quality. Regardless of what software package is used, running a static code analyzer is a great idea for java projects. Finally, as exposed, Checkstyle will not only enforce project-wide coding standards, but it will also help prevent buggy code from reaching a critical environment at all.