eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – Summer Sale 2026 – NPI EA (cat= Baeldung)
announcement - icon

Yes, we're now running our only Summer Sale. All Courses are 30% off until 20th July, 2026:

>> EXPLORE ACCESS NOW

Course – Summer Sale 2026 – NPI (cat=Baeldung)
announcement - icon

Yes, we're now running our only Summer Sale. All Courses are 30% off until 20th July, 2026:

>> EXPLORE ACCESS NOW

1. Introduction

Ensuring code quality is crucial for the successful deployment of our applications. The presence of bugs and errors can significantly hamper the functionality and stability of software. Here comes one valuable tool that aids in identifying such errors: Error Prone.

Error Prone is a library maintained and used internally by Google. It assists Java developers in detecting and fixing common programming mistakes during the compilation phase.

In this tutorial, we explore the functionalities of the Error Prone library, from installation to customization, and the benefits it offers in enhancing code quality and robustness.

2. Installation

The library is available in the Maven Central repository. We’ll add a new build configuration to configure our application compiler to run the Error Prone checks:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.12.1</version>
            <configuration>
                <release>17</release>
                <encoding>UTF-8</encoding>
                <compilerArgs>
                    <arg>-XDcompilePolicy=simple</arg>
                    <arg>-Xplugin:ErrorProne</arg>
                </compilerArgs>
                <annotationProcessorPaths>
                    <path>
                        <groupId>com.google.errorprone</groupId>
                        <artifactId>error_prone_core</artifactId>
                        <version>2.23.0</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

Due to the strong encapsulations of the JDK internals added in version 16, we’ll need to add some flags to allow the plugin to run. One option would be creating a new file .mvn/jvm.config if it doesn’t already exist and adding the required flags for the plugin:

--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
--add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
--add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED

If our maven-compiler-plugin uses an external executable or the maven-toolchains-plugin is enabled, we should add the exports and opens as compilerArgs:

<compilerArgs>
    // ...
    <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
    <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
    <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
    <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
    <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
    <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
    <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
    <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
    <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
    <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
</compilerArgs>

3. Bug Patterns

Identifying and understanding common bug patterns is essential for maintaining the stability and reliability of our software. By recognizing these patterns early in our development process, we can proactively implement strategies to prevent them and improve our code’s overall quality.

3.1. Pre-defined Bug Patterns

The plugin contains more than 500 pre-defined bug patterns. One of those bugs is the DeadException, which we’ll exemplify:

public static void main(String[] args) {
    if (args.length == 0 || args[0] != null) {
        new IllegalArgumentException();
    }
    // other operations with args[0]
}

In the code above, we want to ensure that our program receives a non-null parameter. Otherwise, we want to throw an IllegalArgumentException. However, due to carelessness, we just created the exception and forgot to throw it. In many cases, without a bug-checking tool, this case could go unnoticed.

We can run the Error Prone checks on our code using the maven clean verify command. If we do so, we’ll get the following compilation error:

[ERROR] /C:/Dev/incercare_2/src/main/java/org/example/Main.java:[6,12] [DeadException] Exception created but not thrown
    (see https://errorprone.info/bugpattern/DeadException)
  Did you mean 'throw new IllegalArgumentException();'?

We can see that the plugin not only detected our error but also provided us with a solution for it.

3.2. Custom Bug Patterns

Another notable feature of Error Prone is its ability to support the creation of custom bug checkers. These custom bug checkers enable us to tailor the tool to our specific codebase and address domain-specific issues efficiently.

To create our custom checks, we need to initialize a new project. Let’s call it my-bugchecker-plugin. We’ll start by adding the configuration for the bug checker:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.12.1</version>
            <configuration>
                <annotationProcessorPaths>
                    <path>
                        <groupId>com.google.auto.service</groupId>
                        <artifactId>auto-service</artifactId>
                        <version>1.0.1</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

<dependencies>
    <dependency>
        <groupId>com.google.errorprone</groupId>
        <artifactId>error_prone_annotation</artifactId>
        <version>2.23.0</version>
    </dependency>
    <dependency>
        <groupId>com.google.errorprone</groupId>
        <artifactId>error_prone_check_api</artifactId>
        <version>2.23.0</version>
    </dependency>
    <dependency>
        <groupId>com.google.auto.service</groupId>
        <artifactId>auto-service-annotations</artifactId>
        <version>1.0.1</version>
    </dependency>
</dependencies>

We added some more dependencies this time. As we can see, besides the Error Prone dependencies, we added Google AutoService. Google AutoService is an open-source code generator tool developed under the Google Auto project. This will discover and load our custom checks.

Now we’ll create our custom check, which will verify if we have any empty methods in our code base:

@AutoService(BugChecker.class)
@BugPattern(name = "EmptyMethodCheck", summary = "Empty methods should be deleted", severity = BugPattern.SeverityLevel.ERROR)
public class EmptyMethodChecker extends BugChecker implements BugChecker.MethodTreeMatcher {

    @Override
    public Description matchMethod(MethodTree methodTree, VisitorState visitorState) {
        if (methodTree.getBody()
          .getStatements()
          .isEmpty()) {
            return describeMatch(methodTree, SuggestedFix.delete(methodTree));
        }
        return Description.NO_MATCH;
    }
}

First, The annotation BugPattern contains the name, a short summary, and the severity of the bug. Next, the BugChecker itself is an implementation of MethodTreeMatcher because we want to match methods that have an empty body. Lastly, the logic in matchMethod() should return a match if the method tree body does not have any statements.

To use our custom bug checker in another project, we should compile it into a separate JAR. We’ll do it by running the maven clean install command. After that, we should include the generated JAR as a dependency in the build configuration of our main project by adding it to the annotationProcessorPaths:

<annotationProcessorPaths>
    <path>
        <groupId>com.google.errorprone</groupId>
        <artifactId>error_prone_core</artifactId>
        <version>2.23.0</version>
    </path>
    <path>
        <groupId>com.baeldung</groupId>
        <artifactId>my-bugchecker-plugin</artifactId>
        <version>1.0-SNAPSHOT</version>
    </path>
</annotationProcessorPaths>

This way, our bug checker also becomes reusable. Now, if we write a new class with an empty method:

public class ClassWithEmptyMethod {

    public void theEmptyMethod() {
    }
}

If we run the maven clean verify command again, we’ll get an error:

[EmptyMethodCheck] Empty methods should be deleted

4. Customizing Checks

Google Error Prone is a wonderful tool that can help us eliminate many bugs even before they are introduced into the code. However, it can be too harsh sometimes with our code. Let’s say we want to throw an exception for our empty function, but only for this one. We can add the SuppressWarnings annotation with the name of the check that we want to bypass:

@SuppressWarnings("EmptyMethodCheck")
public void emptyMethod() {}

Suppressing warnings is not recommended but might be needed in some cases, like when working with external libraries that do not implement the same code standards as our project.

In addition to this, we can control the severity of all checks using additional compiler arguments:

  • -Xep:EmptyMethodCheck: turns on EmptyMethodCheck with the severity level from its BugPattern annotation
  • -Xep:EmptyMethodCheck: OFF turns off EmptyMethodCheck check
  • -Xep:EmptyMethodCheck: WARN turns on EmptyMethodCheck check as a warning
  • -Xep:EmptyMethodCheck: ERROR turns on EmptyMethodCheck check as an error

We also have some blanket severity-changing flags that are global for all checks:

  • -XepAllErrorsAsWarnings
  • -XepAllSuggestionsAsWarnings
  • -XepAllDisabledChecksAsWarnings
  • -XepDisableAllChecks
  • -XepDisableAllWarnings
  • -XepDisableWarningsInGeneratedCode

We can also combine our custom compiler flags with the global ones:

<compilerArgs>
    <arg>-XDcompilePolicy=simple</arg>
    <arg>-Xplugin:ErrorProne -XepDisableAllChecks -Xep:EmptyMethodCheck:ERROR</arg>
</compilerArgs>

By configuring our compiler as above, we’ll disable all checks except the custom check we created.

5. Refactoring Code

A feature that separates our plugin from the rest of the static code analysis programs is the possibility of patching the codebase. Aside from identifying errors during our standard compilation phase, Error Prone can also provide suggested replacements. As we saw in subpoint 3, when Error Prone found the DeadException, it also suggested a fix for it:

Did you mean 'throw new IllegalArgumentException();'?

In this context, Error Prone recommends resolving this problem by adding the throw keyword. We can also use Error Prone to modify the source code with the suggested replacements. This is useful when first adding Error Prone enforcement to our existing codebase. To activate this, we need to add two compiler flags to our compiler invocation:

  • XepPatchChecks: followed by the checks that we want to patch. If a check does not suggest fixes, then it won’t do anything.
  • -XepPatchLocation: the location where to generate the patch file containing the fixes

So, we can rewrite our compiler configuration like this:

<compilerArgs>
    <arg>-XDcompilePolicy=simple</arg>
    <arg>-Xplugin:ErrorProne -XepPatchChecks:DeadException,EmptyMethodCheck -XepPatchLocation:IN_PLACE</arg>
</compilerArgs>

We’ll tell the compiler to fix the DeadException and our custom EmptyMethodCheck. We set the location to IN_PLACE, meaning that it will apply the changes in our source code.

Now, if we run the maven clean verify command on a buggy class:

public class BuggyClass {

    public static void main(String[] args) {
        if (args.length == 0 || args[0] != null) {
             new IllegalArgumentException();
        }
    }

    public void emptyMethod() {
    }
}

It will refactor the class:

public class BuggyClass {

    public static void main(String[] args) {
        if (args.length == 0 || args[0] != null) {
             throw new IllegalArgumentException();
        }
    }
}

6. Conclusion

In summary, Error Prone is a versatile tool that combines effective error identification with customizable configurations. It empowers developers to enforce coding standards seamlessly and facilitates efficient code refactoring through automated suggested replacements. Overall, Error Prone is a valuable asset for enhancing code quality and streamlining the development process.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

Course – Summer Sale 2026 – NPI EA (cat= Baeldung)
announcement - icon

Yes, we're now running our only Summer Sale. All Courses are 30% off until 20th July, 2026:

>> EXPLORE ACCESS NOW

Course – Summer Sale 2026 – NPI (All)
announcement - icon

Yes, we're now running our only Summer Sale. All Courses are 30% off until 20th July, 2026:

>> EXPLORE ACCESS NOW

eBook Jackson – NPI EA – 3 (cat = Jackson)