Why Code Quality Matters
Code quality tools help maintain consistent, readable, and bug-free code. They catch issues early in the development cycle, reducing technical debt and making code easier to maintain.
- Bug Prevention - Catch potential issues before runtime
- Consistency - Enforce coding standards across teams
- Maintainability - Easier to read and modify code
- Security - Identify security vulnerabilities early
- Knowledge Sharing - New team members learn standards quickly
Static Analysis Tools Overview
| Tool | Purpose | Integration |
|---|---|---|
| SonarQube/SonarCloud | Comprehensive quality platform | CI/CD, IDE plugins |
| Checkstyle | Code style enforcement | Maven/Gradle, IDE |
| PMD | Bug patterns, best practices | Maven/Gradle, IDE |
| SpotBugs | Bug detection via bytecode analysis | Maven/Gradle, IDE |
| Error Prone | Compile-time bug detection | Compiler plugin |
SonarQube
SonarQube is the most comprehensive code quality platform, analyzing code for bugs, vulnerabilities, code smells, and technical debt.
Key Metrics
- Bugs - Code that will likely cause unexpected behavior
- Vulnerabilities - Security issues
- Code Smells - Maintainability issues
- Coverage - Test coverage percentage
- Duplications - Repeated code blocks
- Technical Debt - Estimated time to fix all issues
Maven Configuration
<!-- pom.xml -->
<properties>
<sonar.projectKey>my-project</sonar.projectKey>
<sonar.organization>my-org</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
</properties>
<!-- Run analysis -->
# mvn sonar:sonar -Dsonar.token=YOUR_TOKEN
GitHub Actions Integration
# .github/workflows/sonar.yml
name: SonarCloud Analysis
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
sonar:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: maven
- name: Cache SonarCloud packages
uses: actions/cache@v3
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
- name: Build and analyze
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: mvn -B verify sonar:sonar
Quality Gate
// SonarQube Quality Gate conditions (customizable)
// Default "Sonar way" gate:
New Code Coverage: > 80%
New Duplicated Lines: < 3%
New Maintainability Rating: A
New Reliability Rating: A
New Security Rating: A
New Security Hotspots: 100% reviewed
// Quality Gate status blocks CI/CD if conditions not met
Checkstyle
Checkstyle enforces coding conventions and style guidelines. It's highly configurable and can use Google or Sun coding standards.
Maven Plugin Configuration
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<!-- Use Google's coding style -->
<configLocation>google_checks.xml</configLocation>
<!-- Or use Sun's style -->
<!-- <configLocation>sun_checks.xml</configLocation> -->
<consoleOutput>true</consoleOutput>
<failsOnError>true</failsOnError>
</configuration>
<executions>
<execution>
<id>validate</id>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
Custom Configuration
<!-- checkstyle.xml -->
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
<property name="charset" value="UTF-8"/>
<module name="TreeWalker">
<!-- Naming conventions -->
<module name="ConstantName"/>
<module name="LocalVariableName"/>
<module name="MethodName"/>
<module name="ParameterName"/>
<module name="TypeName"/>
<!-- Imports -->
<module name="AvoidStarImport"/>
<module name="UnusedImports"/>
<!-- Size violations -->
<module name="LineLength">
<property name="max" value="120"/>
</module>
<module name="MethodLength">
<property name="max" value="50"/>
</module>
<!-- Whitespace -->
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround"/>
<!-- Coding -->
<module name="EmptyStatement"/>
<module name="EqualsHashCode"/>
<module name="MissingSwitchDefault"/>
</module>
</module>
Common Checkstyle Rules
// VIOLATION: Star import
import java.util.*; // Avoid!
// CORRECT: Explicit imports
import java.util.List;
import java.util.ArrayList;
// VIOLATION: Line too long (>120 chars)
public void processUserDataAndSendNotificationWithAllRequiredParametersAndValidation(...) { }
// VIOLATION: Missing braces
if (condition)
doSomething();
// CORRECT: Always use braces
if (condition) {
doSomething();
}
// VIOLATION: Constant naming
public static final int maxSize = 100; // Should be MAX_SIZE
PMD
PMD finds common programming flaws, unused code, suboptimal code, and overly complicated expressions.
Maven Configuration
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.21.2</version>
<configuration>
<rulesets>
<ruleset>/category/java/bestpractices.xml</ruleset>
<ruleset>/category/java/codestyle.xml</ruleset>
<ruleset>/category/java/design.xml</ruleset>
<ruleset>/category/java/errorprone.xml</ruleset>
<ruleset>/category/java/performance.xml</ruleset>
</rulesets>
<failOnViolation>true</failOnViolation>
<printFailingErrors>true</printFailingErrors>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
Common PMD Rules
// UnusedLocalVariable
public void process() {
int unused = 5; // PMD: Variable 'unused' is never used
}
// EmptyCatchBlock
try {
riskyOperation();
} catch (Exception e) {
// PMD: Empty catch block - at least log the exception!
}
// AvoidReassigningParameters
public void process(String input) {
input = input.trim(); // PMD: Don't reassign parameters
}
// UseCollectionIsEmpty
if (list.size() == 0) { } // PMD: Use isEmpty()
if (list.isEmpty()) { } // Better!
// SimplifyBooleanReturns
if (condition) {
return true;
} else {
return false;
}
// Should be: return condition;
SpotBugs
SpotBugs (successor to FindBugs) analyzes bytecode to find potential bugs, including null pointer dereferences, infinite loops, and resource leaks.
Maven Configuration
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.8.2.0</version>
<configuration>
<effort>Max</effort>
<threshold>Low</threshold>
<failOnError>true</failOnError>
<plugins>
<!-- Additional plugins -->
<plugin>
<groupId>com.h3xstream.findsecbugs</groupId>
<artifactId>findsecbugs-plugin</artifactId>
<version>1.12.0</version>
</plugin>
</plugins>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
Common SpotBugs Findings
// NP_NULL_ON_SOME_PATH - Possible null pointer dereference
public void process(User user) {
String name = user.getName(); // user could be null!
name.toUpperCase();
}
// RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
String s = "hello";
if (s != null) { } // Redundant - string literal is never null
// OBL_UNSATISFIED_OBLIGATION - Resource leak
public void readFile() {
FileInputStream fis = new FileInputStream("file.txt");
// SpotBugs: Stream not closed!
}
// DM_BOXED_PRIMITIVE_FOR_PARSING
int value = new Integer("123"); // Inefficient
int value = Integer.parseInt("123"); // Better
// EQ_COMPARETO_USE_OBJECT_EQUALS
// Class implements compareTo but not equals - inconsistent!
Error Prone
Error Prone is a compile-time static analysis tool from Google that catches common Java mistakes.
Maven Configuration
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.12.1</version>
<configuration>
<release>17</release>
<compilerArgs>
<arg>-XDcompilePolicy=simple</arg>
<arg>-Xplugin:ErrorProne</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_core</artifactId>
<version>2.24.1</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
Common Error Prone Checks
// StringSplitter - split() has surprising behavior with regex
String[] parts = "a.b.c".split("."); // Returns empty array!
String[] parts = "a.b.c".split("\\."); // Correct
// CollectionIncompatibleType
List<String> strings = new ArrayList<>();
strings.contains(123); // Always returns false!
// EqualsHashCode
class User {
@Override
public boolean equals(Object o) { ... }
// Missing hashCode()!
}
// MissingOverride
class Child extends Parent {
public void process() { } // Should have @Override
}
Combining Tools
Use multiple tools together for comprehensive coverage. Each tool has different strengths.
Recommended Combination
<!-- Complete quality setup in pom.xml -->
<build>
<plugins>
<!-- Checkstyle - Style and formatting -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version>
<!-- ... configuration ... -->
</plugin>
<!-- PMD - Code patterns and best practices -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.21.2</version>
<!-- ... configuration ... -->
</plugin>
<!-- SpotBugs - Bug detection -->
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.8.2.0</version>
<!-- ... configuration ... -->
</plugin>
<!-- JaCoCo - Code coverage -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.11</version>
<!-- ... configuration ... -->
</plugin>
</plugins>
</build>
# Run all checks
mvn verify
IDE Integration
IntelliJ IDEA
// Built-in inspections: Settings -> Editor -> Inspections
// Checkstyle plugin
// 1. Install "CheckStyle-IDEA" plugin
// 2. Settings -> Tools -> Checkstyle
// 3. Add configuration file
// SonarLint plugin
// 1. Install "SonarLint" plugin
// 2. Connect to SonarQube/SonarCloud for synchronized rules
// 3. Real-time analysis as you code
Eclipse
// Checkstyle plugin: eclipse-cs
// PMD plugin: pmd-eclipse-plugin
// SpotBugs plugin: spotbugs-eclipse-plugin
// SonarLint: Available from Eclipse Marketplace
Configure your IDE to run static analysis on save. This provides immediate feedback and prevents issues from accumulating.
Code Formatting
Spotless (Format Enforcement)
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<version>2.41.1</version>
<configuration>
<java>
<!-- Use Google Java Format -->
<googleJavaFormat>
<version>1.18.1</version>
<style>GOOGLE</style>
</googleJavaFormat>
<!-- Or use Eclipse formatter -->
<!-- <eclipse><file>eclipse-formatter.xml</file></eclipse> -->
<removeUnusedImports/>
</java>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
# Check formatting
mvn spotless:check
# Apply formatting
mvn spotless:apply
Best Practices
- Start early - Add tools to new projects from the beginning
- Fail the build - Don't allow new violations
- Fix gradually - For legacy code, use baselines and fix incrementally
- Customize rules - Disable rules that don't apply to your project
- Review false positives - Suppress with proper justification
- Integrate in CI/CD - Make quality gates mandatory
Suppressing False Positives
// Suppress specific SpotBugs warning
@SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH",
justification = "Null check happens in caller")
public void process(User user) { }
// Suppress PMD warning
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
public void test() { }
// Suppress Checkstyle (in checkstyle-suppressions.xml)
<suppressions>
<suppress checks="MagicNumber" files="Constants\.java"/>
</suppressions>
// SonarQube - suppress with @SuppressWarnings or //NOSONAR
@SuppressWarnings("java:S1135") // Track uses of "TODO" tags
public void method() {
// TODO: implement this //NOSONAR
}