Design Principles

Clean Code Principles

Write code that is easy to read, understand, and maintain. Clean code practices that separate professional developers from the rest.

15 min readEssential Skills

1Meaningful Names

Names should reveal intent. If a name requires a comment, then the name doesn't reveal its intent.
Bad Names
int d; // elapsed time in days
List<int[]> list1;
void doStuff();
boolean flag;
String s;
Good Names
int elapsedTimeInDays;
List<Cell> flaggedCells;
void calculateMonthlyRevenue();
boolean isValid;
String customerName;
Naming Rules
  • Use intention-revealing names - The name should tell why it exists
  • Avoid disinformation - Don't use 'accountList' if it's not a List
  • Make meaningful distinctions - 'a1, a2' tells nothing; 'source, destination' does
  • Use pronounceable names - 'genymdhms' vs 'generationTimestamp'
  • Use searchable names - 'MAX_CLASSES_PER_STUDENT' vs '7'

2Functions

Small

Functions should be small. 20 lines max is a good rule.

Do One Thing

Functions should do one thing. Do it well. Do it only.

One Level of Abstraction

Don't mix high-level and low-level operations.

Few Arguments

Zero is ideal, one or two is fine, three is suspicious.

Function Refactoring Example
// BAD: Does too many things
public void processOrder(Order order) {
    // Validate
    if (order.getItems().isEmpty()) {
        throw new InvalidOrderException("No items");
    }
    if (order.getCustomer() == null) {
        throw new InvalidOrderException("No customer");
    }
    
    // Calculate totals
    double subtotal = 0;
    for (OrderItem item : order.getItems()) {
        subtotal += item.getPrice() * item.getQuantity();
    }
    double tax = subtotal * 0.1;
    double total = subtotal + tax;
    order.setTotal(total);
    
    // Save to database
    orderRepository.save(order);
    
    // Send notification
    emailService.sendOrderConfirmation(order);
}

// GOOD: Each function does one thing
public void processOrder(Order order) {
    validateOrder(order);
    calculateTotals(order);
    saveOrder(order);
    sendConfirmation(order);
}

private void validateOrder(Order order) {
    if (order.getItems().isEmpty()) {
        throw new InvalidOrderException("No items");
    }
    if (order.getCustomer() == null) {
        throw new InvalidOrderException("No customer");
    }
}

private void calculateTotals(Order order) {
    double subtotal = calculateSubtotal(order.getItems());
    double tax = calculateTax(subtotal);
    order.setTotal(subtotal + tax);
}

private double calculateSubtotal(List<OrderItem> items) {
    return items.stream()
        .mapToDouble(i -> i.getPrice() * i.getQuantity())
        .sum();
}

private double calculateTax(double amount) {
    return amount * TAX_RATE;
}

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

private void sendConfirmation(Order order) {
    emailService.sendOrderConfirmation(order);
}

3Comments

Don't comment bad code - rewrite it. Comments are often a failure to express yourself in code. Good code is self-documenting.
Bad Comments
// Check if employee is eligible
// for full benefits
if ((e.flags & HOURLY) &&
    (e.age > 65))

// Returns the day of month
public int getDayOfMonth() {
    return dayOfMonth;
}

// TODO: fix this later
// FIXME: hack
Redundant, misleading, or noise comments
Good Comments
// Self-documenting code
if (employee.isEligibleForBenefits())

// Explanation of intent
// We use insertion sort here because
// our data is almost sorted (O(n) case)

// Warning of consequences
// Don't run without checking memory!

// Public API documentation
/** Creates a new user account */
Intent, warnings, or API docs

4Error Handling

Error Handling Principles
  • Use exceptions rather than return codes - Cleaner separation of concerns
  • Write try-catch-finally first - Defines the scope
  • Provide context with exceptions - Include operation, values, what failed
  • Don't return null - Return empty collections, Optional, or throw
  • Don't pass null - Leads to NullPointerExceptions everywhere
Bad Error Handling
public List<User> getUsers() {
  try {
    return userRepo.findAll();
  } catch (Exception e) {
    return null; // Bad!
  }
}

// Caller must check for null
List<User> users = getUsers();
if (users != null) {
  for (User u : users) { }
}
Good Error Handling
public List<User> getUsers() {
  try {
    return userRepo.findAll();
  } catch (DatabaseException e) {
    log.error("Failed to fetch", e);
    return Collections.emptyList();
  }
}

// Caller doesn't need null checks
List<User> users = getUsers();
for (User u : users) { }

5DRY, KISS, YAGNI

DRY
Don't Repeat Yourself

Every piece of knowledge should have a single, unambiguous representation. If you find yourself copying code, extract it.

KISS
Keep It Simple, Stupid

Simplicity should be a key goal. Avoid unnecessary complexity. The simplest solution is usually the best.

YAGNI
You Aren't Gonna Need It

Don't add functionality until it's necessary. Building for hypothetical future needs often wastes time.

6Interview Follow-up Questions

Interview Follow-up Questions

Common follow-up questions interviewers ask

7Key Takeaways

1Use meaningful names that reveal intent.
2Keep functions small and focused on one thing.
3Good code is self-documenting - minimize comments.
4Handle errors properly - don't return or pass null.
5Follow DRY but avoid premature abstraction.
6Apply KISS and YAGNI - don't over-engineer.