Clean Code Principles

Writing Code That Speaks for Itself

← Back to Index

What is Clean Code?

Clean code is code that is easy to understand, easy to change, and easy to test. It reads like well-written prose and clearly communicates its intent.

Core Philosophy

"Any fool can write code that a computer can understand. Good programmers write code that humans can understand." - Martin Fowler

Meaningful Names

Use Intention-Revealing Names

// BAD: What does this do?
int d;
List<int[]> list1;

public List<int[]> getThem() {
    List<int[]> list1 = new ArrayList<>();
    for (int[] x : theList) {
        if (x[0] == 4)
            list1.add(x);
    }
    return list1;
}

// GOOD: Clear intent
int elapsedTimeInDays;
List<Cell> flaggedCells;

public List<Cell> getFlaggedCells() {
    List<Cell> flaggedCells = new ArrayList<>();
    for (Cell cell : gameBoard) {
        if (cell.isFlagged()) {
            flaggedCells.add(cell);
        }
    }
    return flaggedCells;
}

Avoid Encodings

// BAD: Hungarian notation and prefixes
String strName;
IUserService m_userService;
int iCount;

// GOOD: Let the type system do its job
String name;
UserService userService;
int count;

Class Names vs Method Names

// Classes: Nouns - what it IS
public class Customer { }
public class OrderProcessor { }
public class AccountValidator { }

// Methods: Verbs - what it DOES
public void save() { }
public Order processOrder() { }
public boolean isValid() { }

Functions Should Be Small

One Level of Abstraction

// BAD: Mixed levels of abstraction
public void processOrder(Order order) {
    // High level
    validateOrder(order);

    // Low level - database details
    Connection conn = DriverManager.getConnection(url);
    PreparedStatement stmt = conn.prepareStatement(
        "INSERT INTO orders (id, total) VALUES (?, ?)"
    );
    stmt.setLong(1, order.getId());
    stmt.setDouble(2, order.getTotal());
    stmt.executeUpdate();

    // High level again
    sendConfirmation(order);
}

// GOOD: Consistent abstraction level
public void processOrder(Order order) {
    validateOrder(order);
    saveOrder(order);
    sendConfirmation(order);
}

private void saveOrder(Order order) {
    orderRepository.save(order);
}

Do One Thing

// BAD: Function does multiple things
public void processUserAndSendEmailAndLogActivity(User user) {
    // validate
    // save
    // send email
    // log
}

// GOOD: Each function does one thing
public void registerUser(User user) {
    validate(user);
    save(user);
    notifyUser(user);
    auditRegistration(user);
}

private void validate(User user) { /* validation only */ }
private void save(User user) { /* persistence only */ }
private void notifyUser(User user) { /* notification only */ }
private void auditRegistration(User user) { /* logging only */ }

Function Arguments

// Ideal: zero arguments (niladic)
public void run() { }

// Good: one argument (monadic)
public void process(Order order) { }

// Acceptable: two arguments (dyadic)
public Point createPoint(int x, int y) { }

// Avoid: three or more arguments
// BAD
public void createUser(String name, String email, int age,
                       String address, String phone) { }

// GOOD: Use an object
public void createUser(UserRegistration registration) { }

// Avoid flag arguments
// BAD
public void render(boolean isSuite) { }

// GOOD: Two separate methods
public void renderForSuite() { }
public void renderForSingleTest() { }

Avoid Side Effects

// BAD: Hidden side effect
public boolean checkPassword(String userName, String password) {
    User user = UserGateway.findByName(userName);
    if (user != null) {
        if (user.getPassword().equals(password)) {
            Session.initialize();  // Hidden side effect!
            return true;
        }
    }
    return false;
}

// GOOD: Explicit and predictable
public boolean checkPassword(String userName, String password) {
    User user = userRepository.findByName(userName);
    return user != null && user.getPassword().equals(password);
}

public void login(String userName, String password) {
    if (checkPassword(userName, password)) {
        sessionManager.initialize();  // Explicit!
    }
}

Comments: When and When Not

Bad Comments

// BAD: Redundant comment
// Increment i
i++;

// BAD: Noise comment
/**
 * Default constructor
 */
public User() { }

// BAD: Commented-out code
// userService.oldMethod();
// This was the old way we did things

// BAD: Misleading comment
// Returns user's age in months
public int getAge() {
    return age;  // Actually returns years!
}

Good Comments

// GOOD: Legal comments
// Copyright 2026 Company. All rights reserved.

// GOOD: Explanation of intent
// We use insertion sort here because the list is nearly sorted
// and small (< 20 elements), making it faster than quicksort

// GOOD: Warning of consequences
// Warning: This test takes 30 minutes to run

// GOOD: TODO comments (temporarily)
// TODO: Implement caching when performance becomes an issue

// GOOD: Clarification of obscure code
// Format: YYMMDD + 4-digit sequence number
String orderId = dateFormat.format(date) + sequence;

Error Handling

// BAD: Returning null
public List<User> getUsers() {
    if (noUsersFound) {
        return null;  // Caller must check for null!
    }
    return users;
}

// GOOD: Return empty collection
public List<User> getUsers() {
    if (noUsersFound) {
        return Collections.emptyList();
    }
    return users;
}

// GOOD: Use Optional
public Optional<User> findById(Long id) {
    return Optional.ofNullable(userMap.get(id));
}

// BAD: Passing null
public double calculate(Point p1, Point p2) {
    // What if p1 or p2 is null?
}

// GOOD: Use Objects.requireNonNull
public double calculate(Point p1, Point p2) {
    Objects.requireNonNull(p1, "p1 must not be null");
    Objects.requireNonNull(p2, "p2 must not be null");
    // Safe to proceed
}

DRY - Don't Repeat Yourself

// BAD: Duplicated logic
public void validateUser(User user) {
    if (user.getName() == null || user.getName().isEmpty()) {
        throw new ValidationException("Name required");
    }
    if (user.getName().length() > 100) {
        throw new ValidationException("Name too long");
    }
}

public void validateProduct(Product product) {
    if (product.getName() == null || product.getName().isEmpty()) {
        throw new ValidationException("Name required");
    }
    if (product.getName().length() > 100) {
        throw new ValidationException("Name too long");
    }
}

// GOOD: Extract common logic
private void validateName(String name, String fieldName) {
    if (name == null || name.isEmpty()) {
        throw new ValidationException(fieldName + " required");
    }
    if (name.length() > 100) {
        throw new ValidationException(fieldName + " too long");
    }
}

public void validateUser(User user) {
    validateName(user.getName(), "User name");
}

public void validateProduct(Product product) {
    validateName(product.getName(), "Product name");
}

Code Organization

Newspaper Metaphor

// Organize code like a newspaper article:
// - Headline (class name) tells you what it's about
// - High-level summary first (public methods)
// - Details come later (private methods)

public class OrderService {

    // PUBLIC: High-level operations (the "headline")
    public Order createOrder(OrderRequest request) {
        validate(request);
        Order order = buildOrder(request);
        return save(order);
    }

    public void cancelOrder(Long orderId) {
        Order order = findOrder(orderId);
        markAsCancelled(order);
        notifyCustomer(order);
    }

    // PRIVATE: Implementation details (the "body")
    private void validate(OrderRequest request) { ... }
    private Order buildOrder(OrderRequest request) { ... }
    private Order save(Order order) { ... }
    private Order findOrder(Long id) { ... }
    private void markAsCancelled(Order order) { ... }
    private void notifyCustomer(Order order) { ... }
}

Clean Code Checklist

Quick Review
  • Can you understand the code without comments?
  • Are functions small and focused?
  • Do names reveal intent?
  • Is error handling consistent?
  • Is there any duplicated code?
  • Could a new team member understand this?