Java Code Smells: 7 Patterns to Catch in Code Review (With Fixes)
A code smell is not a bug — it is a pattern that makes bugs more likely and changes more expensive. Java codebases tend to accumulate the same handful of smells, especially in long-lived enterprise services. This guide walks through seven Java code smells worth catching in every review, with examples, refactorings, and the thresholds automated tools typically use.
1. Empty catch blocks
An empty catch swallows the exception and lets the caller believe the operation succeeded. In Java it often appears around checked exceptions that a developer wanted to “make go away”.
try {
invoiceClient.send(invoice);
} catch (IOException e) {
}
try {
invoiceClient.send(invoice);
} catch (IOException e) {
log.error("Failed to send invoice {}", invoice.getId(), e);
throw new InvoiceDeliveryException(invoice.getId(), e);
}
2. Long methods
Methods past roughly 60 lines usually do more than one thing: validation, mapping, persistence, and notification all tangled together. Extract each step into a well-named private method so the top-level method reads like a summary.
3. Large classes
A class past a few hundred lines is often a “service” that has absorbed several responsibilities. Split by reason to change: one class for pricing rules, another for persistence, another for external calls. Smaller classes are easier to test and safer to modify.
4. Too many parameters
Six or more parameters make call sites unreadable and invite argument-order mistakes when several share a type. Group related values into a parameter object or record.
public record TransferRequest(
String fromAccount, String toAccount, BigDecimal amount,
Currency currency, String reference, Instant requestedAt) {}
public TransferResult transfer(TransferRequest request) { ... }
5. Deep nesting
Five or more levels of nested blocks hide the main path of a method. Use guard clauses to return early, invert conditions, and move inner loops into helper methods. See our guide on reducing nested if statements for patterns that work in any language.
6. Hardcoded credentials
Passwords and tokens in constants or properties files committed to Git are a security finding, not just a style issue. Load them from the environment or a secret store and rotate anything that was ever committed.
7. Missing logging and unguarded I/O
File and network calls (FileInputStream, Files.read, HttpURLConnection) without surrounding error handling, and classes with exception handlers but no logging, make production incidents hard to diagnose. Every handled failure should leave a trace with enough context to reproduce it.
Catching Java code smells automatically
Reviewers are good at design questions and poor at counting lines or nesting levels across hundreds of files. CodeInspector’s Java rules cover the seven smells above — empty catch (JAVA0002), missing exception handling around file and network I/O (JAVA0001), long methods over 60 lines (JAVA0003), classes over 400 lines (JAVA0004), more than 6 parameters (JAVA0005), nesting deeper than 5 levels (JAVA0006), hardcoded credentials (JAVA0007), and missing logging (JAVA0008).
Scan a GitHub repository at https://codeinspector.app, then use the findings as a checklist so human review time goes to architecture and behaviour instead.