Introduction: Why Clean Code is Not a Luxury, but a Necessity
Every developer has at least once encountered code that is impossible to understand without a cup of coffee and meditation. Variables named a, b, temp, methods 500 lines long, and comments like "magic here, don't touch" are sure signs that a project is turning into "legacy." Clean Code is a philosophy and a set of practices that make code readable, easily maintainable, and extensible.
In this article, we will break down the key principles of Clean Code in Java with specific examples. You will learn how to write code that you and your colleagues will be grateful to read six months from now.
Principle 1: Meaningful Names
A variable, method, or class name should answer three questions: what it is, why it exists, and how to use it. A bad name is the main source of confusion.
Bad Example
public List<Integer> getList(int a) { List<Integer> list = new ArrayList<>(); for (int i = 0; i < a; i++) { list.add(i * 2); } return list;}What does this method do? What is a? Does it return a list of even numbers? Or just multiply something by two?
Good Example (Clean Code)
public List<Integer> generateEvenNumbers(int count) { List<Integer> evenNumbers = new ArrayList<>(); for (int i = 0; i < count; i++) { evenNumbers.add(i * 2); } return evenNumbers;}Now everything is clear without comments: the method generates a list of even numbers of a given length.
Naming Rules
- Classes: nouns (
User,PaymentProcessor) - Methods: verbs (
saveUser(),calculateTotal()) - Variables: meaningful nouns (
userName,totalPrice) - Avoid: abbreviations (
usrNm), single-letter names (except loop countersi,j)
Principle 2: Small Methods with a Single Responsibility
A method should do only one thing and do it well. If a method can be described with the conjunction "AND" (first it does A, AND then B), it needs to be split.
Bad Example: Monolith
public void processOrder(Order order) { // Validation if (order.getItems().isEmpty()) { throw new IllegalArgumentException("Order is empty"); } // Cost calculation double total = 0; for (Item item : order.getItems()) { total += item.getPrice() * item.getQuantity(); } // Sending email EmailService.send(order.getCustomerEmail(), "Your order total: " + total); // Saving to DB orderRepository.save(order);}This method does 4 things: validates, calculates, sends an email, and saves. If something breaks, you'll have to search for the cause throughout the entire method.
Good Example: Clean Methods
public void processOrder(Order order) { validateOrder(order); double total = calculateTotal(order); sendConfirmationEmail(order, total); saveOrder(order);}
private void validateOrder(Order order) { if (order.getItems().isEmpty()) { throw new IllegalArgumentException("Order cannot be empty"); }}
private double calculateTotal(Order order) { return order.getItems().stream() .mapToDouble(item -> item.getPrice() * item.getQuantity()) .sum();}
private void sendConfirmationEmail(Order order, double total) { String message = "Your order total: " + total; EmailService.send(order.getCustomerEmail(), message);}
private void saveOrder(Order order) { orderRepository.save(order);}Each method is a building block. They are le