| name | clean-code |
| description | Clean Code principles (DRY, KISS, YAGNI), naming conventions, function design, and refactoring. Use when user says "clean this code", "refactor", "improve readability", or when reviewing code quality. |
Clean Code Principles for Java
Core Principles
| Principle | Meaning | Rule of Thumb |
|---|
| DRY | Don't Repeat Yourself | If you copy-paste, extract |
| KISS | Keep It Simple, Stupid | Simplest solution that works |
| YAGNI | You Ain't Gonna Need It | Don't build for imaginary futures |
| SRP | Single Responsibility | One reason to change |
| LoD | Law of Demeter | Don't talk to strangers |
DRY - Don't Repeat Yourself
Violation - Duplicated Validation Logic
public class UserService {
public void registerUser(String email) {
if (email == null || !email.contains("@") || !email.contains(".")) {
throw new IllegalArgumentException("Invalid email");
}
}
}
public class NotificationService {
public void sendEmail(String email) {
if (email == null || !email.contains("@") || !email.contains(".")) {
throw new IllegalArgumentException("Invalid email");
}
}
}
Refactored - Extract Validator
public class EmailValidator {
private static final Pattern EMAIL_PATTERN =
Pattern.compile("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$");
public static void validate(String email) {
if (email == null || !EMAIL_PATTERN.matcher(email).matches()) {
throw new InvalidEmailException(email);
}
}
}
public class UserService {
public void registerUser(String email) {
EmailValidator.validate(email);
}
}
public class NotificationService {
public void sendEmail(String email) {
EmailValidator.validate(email);
}
}
DRY Exceptions - When Duplication Is Acceptable
Sometimes two pieces of code look similar but serve different purposes and evolve independently.
public class ShippingCalculator {
public BigDecimal calculate(Order order) {
BigDecimal base = new BigDecimal("5.00");
if (order.getWeight() > 10) {
base = base.add(new BigDecimal("3.00"));
}
if (order.isInternational()) {
base = base.multiply(new BigDecimal("2.5"));
}
return base;
}
}
public class InsuranceCalculator {
public BigDecimal calculate(Order order) {
BigDecimal base = new BigDecimal("5.00");
if (order.getWeight() > 10) {
base = base.add(new BigDecimal("3.00"));
}
if (order.isInternational()) {
base = base.multiply(new BigDecimal("2.5"));
}
return base;
}
}
KISS - Keep It Simple, Stupid
Violation - Optional Abuse
public String getUserDisplayName(Long userId) {
return Optional.ofNullable(userId)
.flatMap(id -> Optional.ofNullable(userRepository.findById(id)))
.flatMap(opt -> opt)
.map(user -> Optional.ofNullable(user.getDisplayName())
.orElseGet(() -> Optional.ofNullable(user.getFirstName())
.map(first -> first + " " + Optional.ofNullable(user.getLastName()).orElse(""))
.orElse(user.getEmail())))
.orElse("Unknown User");
}
Refactored - Simple Null Check
public String getUserDisplayName(Long userId) {
if (userId == null) {
return "Unknown User";
}
User user = userRepository.findById(userId).orElse(null);
if (user == null) {
return "Unknown User";
}
if (user.getDisplayName() != null) {
return user.getDisplayName();
}
if (user.getFirstName() != null) {
String lastName = user.getLastName() != null ? user.getLastName() : "";
return user.getFirstName() + " " + lastName;
}
return user.getEmail();
}
YAGNI - You Ain't Gonna Need It
Violation - Over-Engineered Generic Repository
public abstract class AbstractGenericRepository<T, ID extends Serializable>
implements GenericRepository<T, ID> {
private final Class<T> entityClass;
private final EntityManager entityManager;
private final CacheManager cacheManager;
private final EventPublisher eventPublisher;
private final AuditLogger auditLogger;
@Override
public T findById(ID id) {
T cached = cacheManager.get(entityClass, id);
if (cached != null) return cached;
T entity = entityManager.find(entityClass, id);
cacheManager.put(entityClass, id, entity);
auditLogger.logAccess(entityClass, id);
eventPublisher.publish(new EntityAccessedEvent<>(entity));
return entity;
}
}
Refactored - Minimal Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
List<User> findByActiveTrue();
}
Naming Conventions
Variables
int d; int elapsedDays;
String s; String customerName;
List<int[]> lst; List<int[]> flaggedCells;
Map<String, Object> m; Map<String, Object> userAttributes;
Booleans
boolean flag; boolean isActive;
boolean status; boolean hasPermission;
boolean check; boolean canExecute;
boolean process; boolean shouldRetry;
Methods
void process(); void processPayment();
Data get(); UserProfile getUserProfile();
void do(); void sendNotification();
boolean check(Order o); boolean isEligibleForDiscount(Order order);
void handle(Event e); void handlePaymentFailed(PaymentEvent event);
Classes
class Data {} class UserProfile {}
class Manager {} class OrderProcessor {}
class Helper {} class PriceCalculator {}
class Utils {} class DateFormatter {}
class Processor {} class PaymentGatewayClient {}
Functions
Keep Small
public void processOrder(Order order) {
}
public void processOrder(Order order) {
validateOrder(order);
BigDecimal total = calculateTotal(order);
total = applyDiscounts(order, total);
updateInventory(order);
sendOrderConfirmation(order, total);
}
Single Abstraction Level
public void registerUser(UserDto dto) {
validateUserData(dto);
String hashedPassword = BCrypt.hashpw(dto.getPassword(), BCrypt.gensalt(12));
Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(
"INSERT INTO users (email, password_hash) VALUES (?, ?)");
ps.setString(1, dto.getEmail());
ps.setString(2, hashedPassword);
ps.executeUpdate();
sendWelcomeEmail(dto.getEmail());
}
public void registerUser(UserDto dto) {
validateUserData(dto);
User user = createUser(dto);
userRepository.save(user);
sendWelcomeEmail(user.getEmail());
}
Limit Parameters
public User createUser(String firstName, String lastName, String email,
String phone, String address, String city,
String state, String zip, String country) { ... }
public User createUser(CreateUserRequest request) { ... }
public record CreateUserRequest(
String firstName,
String lastName,
String email,
String phone,
Address address
) {}
Avoid Flag Arguments
public void sendNotification(User user, boolean isUrgent) {
if (isUrgent) {
} else {
}
}
public void sendUrgentNotification(User user) { ... }
public void sendStandardNotification(User user) { ... }
Comments
Avoid Obvious Comments
int age = user.getAge();
counter++;
if (user.isActive()) { ... }
Good Comments - Explain Why
if (user.getCreatedAt().isBefore(LEGACY_CUTOFF_DATE)) {
return applyLegacyPricing(order);
}
insertionSort(nearlyOrderedItems);
Thread.sleep((long) Math.pow(2, retryCount) * 1000);
Common Code Smells
| Smell | Symptom | Fix |
|---|
| Long Method | > 20 lines | Extract methods |
| Long Parameter List | > 3 parameters | Parameter Object |
| Feature Envy | Uses another class's data more than its own | Move method |
| Data Clumps | Same group of fields together | Extract class |
| Primitive Obsession | Strings for emails, money as double | Value Objects |
| Shotgun Surgery | One change touches many classes | Move related code together |
| Divergent Change | One class changed for many reasons | Split class (SRP) |
| God Class | Class does everything | Break into focused classes |
| Dead Code | Unreachable or unused code | Delete it |
| Speculative Generality | Abstract/generic code with one implementation | Simplify (YAGNI) |
Magic Numbers
if (user.getAge() > 18) { ... }
if (retryCount > 3) { ... }
if (price > 1000.00) { ... }
private static final int LEGAL_AGE = 18;
private static final int MAX_RETRY_ATTEMPTS = 3;
private static final BigDecimal FREE_SHIPPING_THRESHOLD = new BigDecimal("1000.00");
if (user.getAge() > LEGAL_AGE) { ... }
if (retryCount > MAX_RETRY_ATTEMPTS) { ... }
if (price.compareTo(FREE_SHIPPING_THRESHOLD) > 0) { ... }
Primitive Obsession - Use Value Objects
public class User {
private String email;
private String phoneNumber;
private double balance;
}
public record Email(String value) {
public Email {
if (value == null || !value.matches("^[A-Za-z0-9+_.-]+@.+\\..+$")) {
throw new InvalidEmailException(value);
}
}
}
public record PhoneNumber(String value) {
public PhoneNumber {
String digits = value.replaceAll("[^0-9]", "");
if (digits.length() < 10 || digits.length() > 15) {
throw new InvalidPhoneNumberException(value);
}
}
}
public record Money(BigDecimal amount, Currency currency) {
public Money {
Objects.requireNonNull(amount, "Amount cannot be null");
Objects.requireNonNull(currency, "Currency cannot be null");
if (amount.scale() > currency.getDefaultFractionDigits()) {
throw new IllegalArgumentException("Too many decimal places");
}
}
public Money add(Money other) {
if (!this.currency.equals(other.currency)) {
throw new CurrencyMismatchException(this.currency, other.currency);
}
return new Money(this.amount.add(other.amount), this.currency);
}
}
Refactoring Quick Reference
| Technique | When | How |
|---|
| Extract Method | Long method, commented sections | Pull block into named method |
| Extract Class | Class with multiple responsibilities | Split into focused classes |
| Inline Method | Method body is as clear as its name | Replace call with body |
| Rename | Name doesn't reveal intent | Choose descriptive name |
| Replace Temp with Query | Temp variable used once | Extract to method |
| Introduce Parameter Object | Multiple related parameters | Group into class/record |
| Replace Conditional with Polymorphism | Complex if/switch on type | Use strategy pattern |
| Guard Clauses | Deeply nested conditionals | Early returns |
Guard Clauses
public double calculateDiscount(Order order) {
double discount = 0;
if (order != null) {
if (order.getCustomer() != null) {
if (order.getCustomer().isPremium()) {
if (order.getTotal() > 100) {
discount = order.getTotal() * 0.1;
}
}
}
}
return discount;
}
public double calculateDiscount(Order order) {
if (order == null) return 0;
if (order.getCustomer() == null) return 0;
if (!order.getCustomer().isPremium()) return 0;
if (order.getTotal() <= 100) return 0;
return order.getTotal() * 0.1;
}
Clean Code Checklist
Related Skills
java-code-review - Systematic code review process
design-patterns - When refactoring reveals need for patterns
api-contract-review - Clean API design