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?