Clean Code Principles Every Developer Should Follow

July 22, 2024 (1y ago)

Clean Code Principles Every Developer Should Follow

Clean code is code that is easy to read, understand, and maintain. Here are the principles that will make you a better developer.

Meaningful Names

Variables

// Bad
const d = new Date();
const arr = users.filter(u => u.a > 18);
 
// Good
const currentDate = new Date();
const adultUsers = users.filter(user => user.age > 18);

Functions

// Bad
function process(data) { ... }
function handle(x, y) { ... }
 
// Good
function validateUserInput(formData) { ... }
function calculateTotalPrice(items, discount) { ... }

Booleans

// Bad
const flag = true;
const val = user.active;
 
// Good
const isLoading = true;
const hasPermission = user.isAdmin;
const canEdit = document.isOwner && !document.isLocked;

Functions

Keep Functions Small

// Bad - Does too many things
function processUser(user) {
  // Validate user
  if (!user.name || !user.email) throw new Error('Invalid');
  
  // Format data
  user.name = user.name.trim();
  user.email = user.email.toLowerCase();
  
  // Save to database
  database.save(user);
  
  // Send email
  emailService.send(user.email, 'Welcome!');
  
  // Log activity
  logger.log('User created', user.id);
}
 
// Good - Single responsibility
function validateUser(user) {
  if (!user.name || !user.email) {
    throw new Error('Name and email are required');
  }
}
 
function formatUserData(user) {
  return {
    ...user,
    name: user.name.trim(),
    email: user.email.toLowerCase(),
  };
}
 
async function createUser(userData) {
  validateUser(userData);
  const formattedUser = formatUserData(userData);
  const user = await database.save(formattedUser);
  await emailService.sendWelcome(user.email);
  logger.info('User created', { userId: user.id });
  return user;
}

Limit Parameters

// Bad
function createUser(name, email, age, address, phone, role) { ... }
 
// Good
interface CreateUserParams {
  name: string;
  email: string;
  age?: number;
  address?: string;
  phone?: string;
  role?: string;
}
 
function createUser(params: CreateUserParams) { ... }

Avoid Side Effects

// Bad - Mutates input
function addItem(cart, item) {
  cart.items.push(item);
  return cart;
}
 
// Good - Returns new object
function addItem(cart, item) {
  return {
    ...cart,
    items: [...cart.items, item],
  };
}

Avoid Magic Numbers

// Bad
if (user.age >= 18) { ... }
if (password.length < 8) { ... }
setTimeout(callback, 86400000);
 
// Good
const LEGAL_AGE = 18;
const MIN_PASSWORD_LENGTH = 8;
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
 
if (user.age >= LEGAL_AGE) { ... }
if (password.length < MIN_PASSWORD_LENGTH) { ... }
setTimeout(callback, ONE_DAY_MS);

Early Returns

// Bad - Nested conditions
function getDiscount(user) {
  let discount = 0;
  if (user) {
    if (user.isPremium) {
      if (user.yearsActive > 2) {
        discount = 20;
      } else {
        discount = 10;
      }
    }
  }
  return discount;
}
 
// Good - Guard clauses
function getDiscount(user) {
  if (!user) return 0;
  if (!user.isPremium) return 0;
  if (user.yearsActive > 2) return 20;
  return 10;
}

Comments

Avoid Obvious Comments

// Bad
// Loop through users
users.forEach(user => { ... });
 
// Increment counter
counter++;
 
// Good - Explain why, not what
// Filter inactive users to prevent sending emails to abandoned accounts
const activeUsers = users.filter(user => user.lastLogin > thirtyDaysAgo);

Use Self-Documenting Code

// Bad
// Check if user can access admin panel
if (user.role === 'admin' || user.role === 'superadmin') { ... }
 
// Good
const canAccessAdminPanel = (user) => 
  ['admin', 'superadmin'].includes(user.role);
 
if (canAccessAdminPanel(user)) { ... }

Error Handling

// Bad
function getUser(id) {
  try {
    return database.find(id);
  } catch (e) {
    console.log(e);
    return null;
  }
}
 
// Good
class UserNotFoundError extends Error {
  constructor(userId: string) {
    super(`User not found: ${userId}`);
    this.name = 'UserNotFoundError';
  }
}
 
async function getUser(id: string) {
  const user = await database.find(id);
  
  if (!user) {
    throw new UserNotFoundError(id);
  }
  
  return user;
}

DRY (Don't Repeat Yourself)

// Bad - Repetition
const adminEmails = users
  .filter(u => u.role === 'admin')
  .map(u => u.email);
 
const editorEmails = users
  .filter(u => u.role === 'editor')
  .map(u => u.email);
 
// Good - Abstraction
const getEmailsByRole = (users, role) =>
  users.filter(u => u.role === role).map(u => u.email);
 
const adminEmails = getEmailsByRole(users, 'admin');
const editorEmails = getEmailsByRole(users, 'editor');

SOLID Principles Summary

  1. Single Responsibility - One reason to change
  2. Open/Closed - Open for extension, closed for modification
  3. Liskov Substitution - Subtypes replaceable for base types
  4. Interface Segregation - Small, specific interfaces
  5. Dependency Inversion - Depend on abstractions

Code Review Checklist

Clean code is not about being clever—it's about being clear. Write code that your future self will thank you for!