Best Practices
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"
Enjoying these tutorials?