06-Jul-2021| Debugging, Dev Tools, Java |9 min read

If you want to ensure code maintainability over the long term, you should follow best coding practices and style guide rules. One of the best ways to achieve this, while also potentially finding bugs and other issues with your code, is to use a linter.

Linters are best described as static code analyzers because they check your code before it even runs. They can work inside your IDE, run as part of your build process, or be inserted into your workflow anywhere in between. While the use cases for linters can be rather varied, their utility usually focuses on code cleanup and standardization. In other words, using a linter helps make your code less sloppy and more maintainable.

Check out the below example for a demonstration of how a linter works, from Checkstyle:

Before:

public abstract class Plant {

  private String roots;

  private String trunk;

  protected void validate() {

    if (roots == null) throw new IllegalArgumentException("No roots!");

    if (trunk == null) throw new IllegalArgumentException("No trunk!");

  }

  public abstract void grow();

}

public class Tree extends Plant {

  private List leaves;

  @Overrides

  protected void validate() {

    super.validate();

    if (leaves == null) throw new IllegalArgumentException("No leaves!");

  }

  public void grow() {

    validate();

  }

}

After:

public abstract class Plant {

  private String roots;

  private String trunk;

  private void validate() {

    if (roots == null) throw new IllegalArgumentException("No roots!");

    if (trunk == null) throw new IllegalArgumentException("No trunk!");

    validateEx();

  }

  protected void validateEx() { }

  public abstract void grow();

}

In this article, I’ll examine ten of the best linters for Java. You’ll find that while most linters aren’t “better” or “worse” than others, there are certainly some that come with a wider breadth of features, making them more powerful or flexible than some of their niche counterparts.

Ultimately, it’s best to choose a linter that works best for your specific business use case and workflow.

1. Checkstyle

checkstyle

Checkstyle is one of the most popular linters available. With this popularity comes regular updates, thorough documentation, and ample community support. Checkstyle works natively with Ant and CLI. It is also available as a plugin for a wide variety of IDE’s and toolsets, including Eclipse, Codacy, Maven, and Gradle – although these plugins are managed by third parties, so there’s no guarantee of long-term support.

Checkstyle comes with pre-made config files that support both Sun Code Conventions and Google Java Style, but because these files are XML, they are highly configurable to support your workflow and production needs.

It is also worth mentioning that a project with Checkstyle built into its build process will fail to build even if minor errors are present. This might be a problem if you’re only looking to catch larger errors and don’t have the resources to fix tiny errors that don’t have a perceptible impact.

2. Lightrun

The second member of this list is not actually a linter per se, but it will help you improve your code quality and prevent bugs before they become serious problems. Whereas everything to this point has been a static code analyzer, Lightrun is a runtime debugger. At the end of the day, static code analysis and linting can only get you so far, so if you need a little more, Lightrun is worth adding to your workflow.

Production is the ultimate stress test for any codebase, especially in the age of cloud computing. Lightrun allows you to insert logs, metrics, and snapshots into your code, even at runtime, directly from your IDE or CLI. Lightrun lets you debug issues in production and run data analysis on your code without slowing down or interrupting your application.

3. PMD

pmd

What do the PMD initials stand for? It seems even the developers don’t know…

Like Checkstyle, PMD is a popular static code analyzer with an emphasis on Java. Unlike Checkstyle, PMD supports multi-language analysis, including JavaScript, Salesforce.com, Apex, and Visualforce. This could be helpful if you’d like to use a single linter for a frontend and backend codebase.

In the developers’ own words, “[PMD] finds common programming flaws like unused variables, empty catch blocks, unnecessary object creation, and so forth.” In addition, it comes with a copy-paste-detector (CPD) to find duplicated code in a myriad of languages, so it is easier to find code that could be refactored.

4. Uncrustify

uncrustify

Uncrustify diverges from the previous linters in that Java is not its primary focus. Instead, it is a “code beautifier” made for C and C-like languages, including Java. On the one hand, Uncrustify is great for projects with a C-based or C-analogue-based workflow. On the other hand, its feature list begins and ends with simply making your code look nicer.

Uncrustify works by running through your code and automatically updating its white space, bracketing, and other formatting conventions to match a ruleset. Because this is an automated process, the developers themselves caution against running Uncrustify on an entire project without checking the changes afterward.

Uncrustify is best used in conjunction with other linters and dev tools. It isn’t particularly powerful on its own but could come in handy for niche workflows that involve multiple C-based languages.

5. Error Prone

Error Prone is an error finder for your code builds, specifically built for Java. It is designed to supplement your compiler’s static type checker, and find potential runtime errors without running the code. The example provided on their website seems trivial, especially to any developers who’ve been working out of an IDE for most of their careers.

But for codebases where the compile and run process can stretch into hours or even days, having that extra check can save a lot of time and headaches, especially if a particular bug might be in an uncommonly accessed block of code.

6. Tattletale

Tattletale

Tattletale might not be considered a linter in the traditional sense. While it does analyze your static code like the other linters on this list, it is less concerned with individual blocks of code or particular development standards, and it is more focused on finding package and library redundancies in your project.

Not only will Tattletale identify different dependencies within your JAR files, but it will also suss out duplicate JAR files, find duplicate or missing classes, and check similar JARs with different version numbers. Long-term, not only will this keep your project size slimmer, but it will also help prevent head-scratching errors where you’re calling two different versions of the same package and getting different results because of changes between versions. All of this information is put into an HTML report for easy viewing.

Because of the high-level intention of this tool, it won’t help much with line-to-line code edits. But with that said, if you’re running a purely Java codebase, Tattletale is a tool worth adding to your arsenal.

7. UCDetector

ucdetector

UCDetector, short for Unnecessary Code Detector, does exactly what its name implies. In addition to finding “dead” code, it also marks classes whose privacy modifier could be changed from public to something more restricted and methods or fields which can be set to final.

One of the earliest OOP concepts taught in school is that programmers should only set classes, methods, and data fields to public if they explicitly know those elements will be accessed or modified by external classes. However, when in the thick of coding, even with the best-defined UMLs, it can sometimes be difficult to determine when a class or method should be public, private, or protected.

After your code is completed and debugged, a pass through the UCDetector will help you catch any code blocks you missed or mistakenly set to the wrong privacy modifier, potentially saving you headaches down the road and preventing sensitive field members from being unintentionally exposed to clients.

8. linter for Scala

So, let’s say that you’re looking to move on from Java. You want something familiar and that will integrate well with your current back end. Instead of going with C#, you decide to upgrade to Scala. Not coincidentally, the more niche a language is, the more difficult it will be to find Linting tools for it. That’s not saying that Scala is terribly niche, just that it can be a little more difficult to find support for it than for vanilla Java.

With that said, a nice starting point would be this toolset, simply titled: linter Compiler Plugin. According to the Github page, “Linter is a Scala static analysis compiler plugin which adds compile-time checks for various possible bugs, inefficiencies, and style problems.” Not only is it written for Scala, but it is also written almost exclusively in Scala.

Unfortunately, the last commit on the project was in 2016, so it might not be well-equipped for any new features introduced to the language in the past five years.

9. Scalastyle

scalastyle

The developers of Scalastyle describe it thusly: “Scalastyle examines your Scala code and indicates potential problems with it. If you have come across Checkstyle for Java, then you’ll have a good idea of what Scalastyle is. Except that it’s for Scala obviously.”

So for those of you who loved Checkstyle but are moving to a Scala workflow, rejoice, for Scalastyle is here. It’s kept up to date and more likely a better option than linter for Scala above.

10. Coala

coala

Of all the linters on this list, Coala seems to aim for the most flexibility. It claims that it works by, “linting and fixing code for all languages.” While Fortran is located nowhere on their list of supported languages, Coala does support quite an extensive list, including (of course) Java.

All of these languages can be linted using a single config file, so if you’re working in a multi-language web environment (is there any other kind of web environment?) you’ll find Coala is well-suited to your needs.

Final Thoughts

Linters make an awesome addition to just about any Java development environment. Debugging, error-checking, and issue prevention are all multi-step procedures that should take place in every phase of development. A linter is a great tool for when you want to analyze code without having to run it, maintain coding best practices, and ensure long-term code maintainability. However, each linter’s usefulness only extends so far and should thus be supplemented by other tools, including runtime debugging software like Lightrun.

J.C. Chambers

All the Lightrun News That’s Fit to Print(f)!

Get the latest posts, news,
feature releases, updates, and articles directly to your inbox