Web Analytics

Best Practices

Advanced ~20 min read

Writing code that works is not enough; professional code must be readable, maintainable, and efficient. These best practices will help you write code that your future self and teammates will thank you for.

Naming Conventions

Element Convention Example
Classes PascalCase (nouns) UserAccount, HttpClient
Interfaces PascalCase (adjectives or nouns) Runnable, Comparable
Methods camelCase (verbs) calculateTotal(), getUserById()
Variables camelCase (nouns) userName, totalCount
Constants UPPER_SNAKE_CASE MAX_SIZE, DEFAULT_TIMEOUT
Packages lowercase com.company.project

Clean Code Principles

1. Single Responsibility Principle (SRP)

A class should have one, and only one, reason to change.

// Bad: Class does too many things
class UserManager {
    void createUser() { }
    void deleteUser() { }
    void sendEmail() { }      // Not user management!
    void generateReport() { } // Not user management!
}

// Good: Each class has one responsibility
class UserService {
    void createUser() { }
    void deleteUser() { }
}

class EmailService {
    void sendEmail() { }
}

class ReportGenerator {
    void generateReport() { }
}

2. Meaningful Names

// Bad
int d; // elapsed time in days
List<int[]> list1;

// Good
int elapsedTimeInDays;
List<int[]> flaggedCells;

// Bad method name
public List<User> get() { }

// Good method name
public List<User> getActiveUsersByDepartment(String dept) { }

3. Keep Methods Small

// Bad: Method does too much
public void processOrder(Order order) {
    // validate order (20 lines)
    // calculate totals (15 lines)
    // apply discounts (10 lines)
    // save to database (10 lines)
    // send confirmation email (15 lines)
}

// Good: Extract methods
public void processOrder(Order order) {
    validateOrder(order);
    calculateTotals(order);
    applyDiscounts(order);
    saveOrder(order);
    sendConfirmation(order);
}

private void validateOrder(Order order) { /* ... */ }
private void calculateTotals(Order order) { /* ... */ }
// etc.

Defensive Programming

Validate Inputs

public void setAge(int age) {
    if (age < 0 || age > 150) {
        throw new IllegalArgumentException("Age must be between 0 and 150");
    }
    this.age = age;
}

// Using Objects utility class
public void setName(String name) {
    this.name = Objects.requireNonNull(name, "Name cannot be null");
}

Return Empty Collections, Not Null

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

// Good: Returns empty collection
public List<User> getUsers() {
    if (noUsersFound) {
        return Collections.emptyList();  // Safe to iterate
    }
    return users;
}

Use Optional for Nullable Returns

// Bad: Returns null
public User findUserById(Long id) {
    // Returns null if not found
}

// Good: Returns Optional
public Optional<User> findUserById(Long id) {
    return Optional.ofNullable(userRepository.find(id));
}

// Usage
findUserById(123)
    .ifPresent(user -> System.out.println(user.getName()));

String name = findUserById(123)
    .map(User::getName)
    .orElse("Unknown");

Exception Handling

// Bad: Catching generic Exception
try {
    processFile();
} catch (Exception e) {
    e.printStackTrace();  // Don't just print!
}

// Good: Catch specific exceptions, handle properly
try {
    processFile();
} catch (FileNotFoundException e) {
    logger.error("File not found: " + e.getMessage());
    throw new ProcessingException("Configuration file missing", e);
} catch (IOException e) {
    logger.error("Error reading file", e);
    throw new ProcessingException("Failed to read file", e);
}

// Good: Try-with-resources for auto-closing
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
    String line;
    while ((line = reader.readLine()) != null) {
        process(line);
    }
}  // Reader automatically closed

Immutability

Immutable objects are thread-safe and easier to reason about.

// Immutable class
public final class Money {
    private final BigDecimal amount;
    private final Currency currency;

    public Money(BigDecimal amount, Currency currency) {
        this.amount = amount;
        this.currency = currency;
    }

    // No setters! Return new instance instead
    public Money add(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new IllegalArgumentException("Currency mismatch");
        }
        return new Money(this.amount.add(other.amount), this.currency);
    }

    public BigDecimal getAmount() {
        return amount;  // BigDecimal is already immutable
    }
}

// Using records (Java 16+) - automatically immutable
public record Point(int x, int y) { }

Code Comments

// Bad: Comment states the obvious
i++; // Increment i

// Bad: Comment compensates for bad code
// Check if employee is eligible for benefits
if ((employee.flags & HOURLY_FLAG) && (employee.age > 65)) { }

// Good: Self-documenting code
if (employee.isEligibleForBenefits()) { }

// Good: Explain WHY, not WHAT
// Using insertion sort here because the list is nearly sorted
// and insertion sort is O(n) for nearly sorted data
insertionSort(nearySortedList);

// Good: Javadoc for public APIs
/**
 * Calculates compound interest.
 *
 * @param principal Initial investment amount
 * @param rate Annual interest rate (e.g., 0.05 for 5%)
 * @param years Number of years
 * @return Final amount after compound interest
 */
public BigDecimal calculateCompoundInterest(
        BigDecimal principal, double rate, int years) { }

Common Pitfalls

String Comparison

// Bad: == compares references
if (str1 == str2) { }

// Good: equals() compares content
if (str1.equals(str2)) { }

// Better: Handles null safely
if (Objects.equals(str1, str2)) { }

// Best for literals: Literal first avoids NullPointerException
if ("expected".equals(str1)) { }

Floating Point Comparison

// Bad: Direct comparison fails due to precision
if (0.1 + 0.2 == 0.3) { }  // false!

// Good: Use threshold
double EPSILON = 0.0001;
if (Math.abs(a - b) < EPSILON) { }

// Best for money: Use BigDecimal
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
BigDecimal c = new BigDecimal("0.3");
a.add(b).equals(c);  // true

Summary

  • Use meaningful names that reveal intent
  • Keep methods small and focused (SRP)
  • Validate inputs and fail fast
  • Return empty collections, not null
  • Use Optional for nullable returns
  • Catch specific exceptions and handle properly
  • Prefer immutable objects when possible
  • Write self-documenting code; comment the "why"