Skip to content
best-practices·25.10.2025·6 min read

Summary of Clean Code by Robert C. Martin - Essential Guidelines for Better Software

Code is clean if it can be understood easily by everyone on the team. A comprehensive summary of Robert C. Martin's Clean Code principles and best practices.

Summary of Clean Code by Robert C. Martin - Essential Guidelines for Better Software

Introduction

Code is clean if it can be understood easily – by everyone on the team. Clean code can be read and enhanced by a developer other than its original author. With understandability comes readability, changeability, extensibility and maintainability.

Robert C. Martin's "Clean Code" is a fundamental guide for writing maintainable, readable, and professional code. Here's a comprehensive summary of the key principles and practices.

General Rules

  1. Follow standard conventions - Consistency is key across your codebase
  2. Keep it simple stupid (KISS) - Simpler is always better. Reduce complexity as much as possible
  3. Boy scout rule - Leave the campground cleaner than you found it
  4. Always find root cause - Always look for the root cause of a problem

Design Rules

Keep configurable data at high levels

// Good: Configuration at the top level
const CONFIG = {
  API_URL: 'https://api.example.com',
  TIMEOUT: 5000,
}

class ApiService {
  constructor(config = CONFIG) {
    this.apiUrl = config.API_URL
    this.timeout = config.TIMEOUT
  }
}

Prefer polymorphism to if/else or switch/case

// Bad: Multiple if/else statements
function calculateArea(shape) {
  if (shape.type === 'circle') {
    return Math.PI * shape.radius * shape.radius
  } else if (shape.type === 'rectangle') {
    return shape.width * shape.height
  }
}

// Good: Polymorphism
class Circle {
  calculateArea() {
    return Math.PI * this.radius * this.radius
  }
}

class Rectangle {
  calculateArea() {
    return this.width * this.height
  }
}

Additional Design Principles:

  • Separate multi-threading code
  • Prevent over-configurability
  • Use dependency injection
  • Follow Law of Demeter - A class should know only its direct dependencies

Understandability Tips

  • Be consistent - If you do something a certain way, do all similar things in the same way
  • Use explanatory variables
  • Encapsulate boundary conditions - Put boundary processing in one place
  • Prefer dedicated value objects to primitive types
  • Avoid logical dependency - Don't write methods that depend on something else in the same class
  • Avoid negative conditionals

Naming Rules

// Bad naming
let d // elapsed time in days
let users = []

function calc(u) {
  return u.age * 365
}

// Good naming
let elapsedTimeInDays
let registeredUsers = []

function calculateDaysFromAge(user) {
  return user.age * DAYS_PER_YEAR
}

Key naming principles:

  • Choose descriptive and unambiguous names
  • Make meaningful distinctions
  • Use pronounceable names
  • Use searchable names
  • Replace magic numbers with named constants
  • Avoid encodings and prefixes

Function Rules

// Bad: Large function doing multiple things
function processUserData(users) {
  // Validate users
  users.forEach((user) => {
    if (!user.email || !user.name) {
      throw new Error('Invalid user')
    }
  })

  // Calculate statistics
  let totalAge = 0
  users.forEach((user) => (totalAge += user.age))

  // Send emails
  users.forEach((user) => {
    sendEmail(user.email, 'Welcome!')
  })

  return { averageAge: totalAge / users.length }
}

// Good: Small functions doing one thing
function validateUsers(users) {
  users.forEach(validateUser)
}

function calculateAverageAge(users) {
  const totalAge = users.reduce((sum, user) => sum + user.age, 0)
  return totalAge / users.length
}

function sendWelcomeEmails(users) {
  users.forEach((user) => sendEmail(user.email, 'Welcome!'))
}

Function principles:

  • Keep functions small
  • Do one thing
  • Use descriptive names
  • Prefer fewer arguments
  • Have no side effects
  • Don't use flag arguments

Comments Rules

// Bad: Redundant comment
let age = 25 // Set age to 25

// Bad: Commented out code
// function oldFunction() {
//   return 'deprecated';
// }

// Good: Explanation of intent
// Using binary search for O(log n) performance
function findUser(users, targetId) {
  // Implementation here
}

// Good: Warning of consequences
// Don't run this in production - it will delete all data
function resetDatabase() {
  // Implementation here
}

Comment guidelines:

  • Always try to explain yourself in code first
  • Don't be redundant
  • Don't add obvious noise
  • Don't comment out code - just remove it
  • Use comments for explanation of intent and warnings

Source Code Structure

  • Separate concepts vertically - Use blank lines to separate different concepts
  • Related code should appear vertically dense
  • Declare variables close to their usage
  • Dependent functions should be close
  • Similar functions should be close
  • Place functions in downward direction
  • Keep lines short
  • Use whitespace to associate related things

Objects and Data Structures

// Good: Hide internal structure
class BankAccount {
  #balance = 0

  deposit(amount) {
    this.#balance += amount
  }

  getBalance() {
    return this.#balance
  }
}

// Good: Prefer data structures for simple data
const userPreferences = {
  theme: 'dark',
  language: 'en',
  notifications: true,
}

Object principles:

  • Hide internal structure
  • Prefer data structures for simple data
  • Avoid hybrid structures (half object, half data)
  • Keep objects small
  • Do one thing
  • Prefer non-static methods to static methods

Test Guidelines

// Good: One assert per test
describe('Calculator', () => {
  it('should add two numbers correctly', () => {
    const result = calculator.add(2, 3)
    expect(result).toBe(5)
  })

  it('should handle negative numbers', () => {
    const result = calculator.add(-2, 3)
    expect(result).toBe(1)
  })
})

Test principles:

  • One assert per test
  • Tests should be readable
  • Tests should be fast
  • Tests should be independent
  • Tests should be repeatable

Code Smells to Avoid

  1. Rigidity - Software difficult to change; small changes cause cascading effects
  2. Fragility - Software breaks in many places due to single change
  3. Immobility - Cannot reuse parts of code in other projects
  4. Needless Complexity - Over-engineering solutions
  5. Needless Repetition - DRY principle violations
  6. Opacity - Code that's hard to understand

Quick Reference Checklist

Before committing code, ask yourself:

  • ✓ Can another developer easily understand this?
  • ✓ Are function and variable names descriptive?
  • ✓ Are functions small and focused?
  • ✓ Is the code properly structured?
  • ✓ Are there unnecessary comments?
  • ✓ Is the code as simple as possible?

Conclusion

Clean code is not written by following a set of rules. Clean code is written by programmers who care about their craft and take the time to make their code readable and maintainable.

Remember: "Any fool can write code that a computer can understand. Good programmers write code that humans can understand." - Martin Fowler

The investment in clean code pays dividends in reduced maintenance costs, fewer bugs, and improved team productivity. Start applying these principles today, and your future self (and teammates) will thank you.

Enjoyed this?

Get new posts as they land.

Subscribe via RSS

Keep reading