Fundamentals 9 min read

Clean Code: Programming for Humans

Published on January 8, 2026

Clean Code: Programming for Humans

When I started programming, I believed the goal was to write code that worked. Period. If the program did what it was supposed to do, it was fine. But over time, I learned a crucial lesson: code is read far more than it is written.

I’ve worked on projects where the code worked perfectly, but every time I needed to make a change, I spent hours trying to understand what each function did. I’ve worked on projects where the code was so clear that I could make changes confidently in minutes.

The difference isn’t in functionality, but in how the code is written. And that difference can be what separates a successful project from one that collapses under its own weight.

Code is for humans, not machines

The CPU doesn’t care what a variable is called or whether a function has 50 lines or 5. The CPU executes instructions. But you, your team, and your future self do care.

Statistics that should make you think:

  • 80% of a developer’s time is spent reading code, not writing it
  • Code is maintained for years, but written once
  • A bug costs 10 times more to fix in production than during development

If code is hard to read, every change takes longer, every bug is harder to find, and every new developer takes longer to become productive.

What is Clean Code?

Clean Code isn’t a set of rigid rules. It’s code that:

  • Is easy to understand: Any developer can read it and understand what it does
  • Is easy to modify: Future changes don’t require deciphering the code first
  • Expresses its intent: The code clearly communicates what it does and why
  • Has no surprises: It does what it says it does, nothing more, nothing less

Names that mean something

The name of a variable, function, or class is the first opportunity to communicate intent.

❌ Bad: Names that say nothing

// What is d? What is t? What does this function do?
function calc(d, t) {
    return d / t;
}

// What is data? What does it contain?
const data = getData();
process(data);

✅ Good: Names that express intent

// Now it's clear: calculates velocity (distance / time)
function calculateVelocity(distance, time) {
    return distance / time;
}

// Now it's clear: gets users and processes them
const users = getActiveUsers();
processUserSubscriptions(users);

Rules for names

  1. Use pronounceable names: userData is better than usrDta
  2. Use searchable names: DAYS_PER_WEEK is better than 7 (if it’s a constant)
  3. Avoid unnecessary prefixes: User is better than CUser or IUser
  4. Use verbs for functions: calculateTotal() is better than total()
  5. Use nouns for classes: UserRepository is better than UserManager

Small functions with a single purpose

A function should do one thing and do it well. If a function does multiple things, it’s hard to understand, test, and maintain.

❌ Bad: Function that does too much

function processUser(user) {
    // Validate
    if (!user.email || !user.email.includes('@')) {
        throw new Error('Invalid email');
    }
    
    // Save to database
    db.users.insert(user);
    
    // Send email
    emailService.sendWelcomeEmail(user.email);
    
    // Generate report
    const report = generateReport(user);
    reportService.save(report);
    
    // Update analytics
    analytics.track('user_created', user.id);
    
    return user;
}

✅ Good: Functions with single responsibility

function createUser(userData) {
    validateUserData(userData);
    const user = saveUserToDatabase(userData);
    sendWelcomeEmail(user);
    generateUserReport(user);
    trackUserCreation(user);
    return user;
}

function validateUserData(userData) {
    if (!userData.email || !isValidEmail(userData.email)) {
        throw new ValidationError('Invalid email');
    }
}

function saveUserToDatabase(userData) {
    return db.users.insert(userData);
}

function sendWelcomeEmail(user) {
    emailService.sendWelcomeEmail(user.email);
}

function generateUserReport(user) {
    const report = generateReport(user);
    reportService.save(report);
}

function trackUserCreation(user) {
    analytics.track('user_created', user.id);
}

Now each function:

  • Has a name that describes exactly what it does
  • Does one thing
  • Is easy to test
  • Is easy to modify without affecting other parts

Comments: When yes, when no

Comments are a form of communication, but the best comment is code that doesn’t need comments.

❌ Bad: Comments that explain obvious code

// Increment counter
counter++;

// Check if user is active
if (user.isActive) {
    // Send email
    sendEmail(user.email);
}

✅ Good: Self-explanatory code

counter++;

if (user.isActive) {
    sendEmail(user.email);
}

✅ Good: Comments that explain the “why”, not the “what”

// We use O(n²) here because most cases have fewer than 10 elements
// and code simplicity is more important than optimization
function findDuplicates(items) {
    // ...
}

// This timeout is necessary because the external service takes ~2 seconds to respond
// and we don't want the user to wait more than 5 seconds
setTimeout(() => {
    handleTimeout();
}, 5000);

Format and structure

Code format matters. Well-formatted code is easier to read and understand.

Consistent indentation

// ❌ Bad: Inconsistent indentation
function processOrder(order){
if(order.items.length > 0){
for(let item of order.items){
if(item.quantity > 0){
processItem(item);
}
}
}
}

// ✅ Good: Consistent indentation
function processOrder(order) {
    if (order.items.length > 0) {
        for (let item of order.items) {
            if (item.quantity > 0) {
                processItem(item);
            }
        }
    }
}

Blank lines to separate concepts

// ❌ Bad: Everything together
function calculateTotal(items, taxRate, discount) {
    let subtotal = 0;
    for (let item of items) {
        subtotal += item.price * item.quantity;
    }
    const tax = subtotal * taxRate;
    const discountAmount = subtotal * discount;
    const total = subtotal + tax - discountAmount;
    return total;
}

// ✅ Good: Concepts separated
function calculateTotal(items, taxRate, discount) {
    const subtotal = calculateSubtotal(items);
    
    const tax = calculateTax(subtotal, taxRate);
    const discountAmount = calculateDiscount(subtotal, discount);
    
    const total = subtotal + tax - discountAmount;
    return total;
}

Error handling

Error handling should be clear and explicit.

❌ Bad: Silent errors

function getUser(id) {
    const user = db.users.findById(id);
    return user; // What if user is null?
}

function processPayment(amount) {
    try {
        paymentProcessor.charge(amount);
    } catch (e) {
        // Error silenced - user doesn't know what happened
    }
}

✅ Good: Explicit errors

function getUser(id) {
    const user = db.users.findById(id);
    if (!user) {
        throw new UserNotFoundError(`User with id ${id} not found`);
    }
    return user;
}

function processPayment(amount) {
    try {
        paymentProcessor.charge(amount);
    } catch (error) {
        logger.error('Payment processing failed', { amount, error });
        throw new PaymentProcessingError('Failed to process payment', error);
    }
}

Don’t repeat yourself (DRY)

If you find duplicated code, it’s a sign you should extract it into a function.

❌ Bad: Duplicated code

function validateUser(user) {
    if (!user.email || !user.email.includes('@')) {
        throw new Error('Invalid email');
    }
    if (!user.name || user.name.length < 2) {
        throw new Error('Invalid name');
    }
}

function validateAdmin(admin) {
    if (!admin.email || !admin.email.includes('@')) {
        throw new Error('Invalid email');
    }
    if (!admin.name || admin.name.length < 2) {
        throw new Error('Invalid name');
    }
}

✅ Good: Reusable code

function validateEmail(email) {
    if (!email || !email.includes('@')) {
        throw new ValidationError('Invalid email');
    }
}

function validateName(name) {
    if (!name || name.length < 2) {
        throw new ValidationError('Invalid name');
    }
}

function validateUser(user) {
    validateEmail(user.email);
    validateName(user.name);
}

function validateAdmin(admin) {
    validateEmail(admin.email);
    validateName(admin.name);
}

Practical principles

1. Boy Scout Rule

“Leave the code better than you found it.” Every time you touch code, make a small improvement.

2. Rule of three

The first time you do something, do it. The second time you do something similar, refactor.

3. Small steps

Don’t try to refactor everything at once. Make small, frequent changes.

4. Tests as documentation

Well-written tests are executable documentation. They show how the code is used.

describe('calculateTotal', () => {
    it('should calculate total with tax and discount', () => {
        const items = [
            { price: 10, quantity: 2 },
            { price: 5, quantity: 1 }
        ];
        
        const total = calculateTotal(items, 0.1, 0.05);
        
        expect(total).toBe(26.25); // (20 + 5) * 1.1 * 0.95
    });
});

My practical experience

I’ve seen projects where the code worked, but every change was a nightmare. I’ve seen projects where the code was so clear that new developers could be productive in days, not weeks.

Applying these principles constantly isn’t about perfection, it’s about continuous improvement. Every time you touch code, try to leave it a little better.

Practical example: Refactoring

Before (code that worked but was hard to understand):

function proc(u) {
    let t = 0;
    for (let i = 0; i < u.length; i++) {
        if (u[i].s === 'a') {
            t += u[i].v;
        }
    }
    return t;
}

After (clear code):

function calculateTotalActiveUsersValue(users) {
    return users
        .filter(user => user.status === 'active')
        .reduce((total, user) => total + user.value, 0);
}

The code does exactly the same thing, but now anyone can understand it in seconds.

The Clean Code mindset

Clean Code isn’t a set of rules you follow blindly. It’s a mindset:

  • Think about the reader: Will it be easy for someone else to understand this?
  • Think about the future: Will I understand this in 6 months?
  • Think about the team: Will my team be able to work with this?

It’s not about writing perfect code from the start. It’s about improving continuously, making the code a little better every time you touch it.

My personal perspective

Clean code isn’t a luxury, it’s a necessity. In a world where code is read far more than it’s written, clarity is more important than cleverness.

I’ve seen projects where the code worked, but every change was a nightmare. I’ve seen projects where the code was so clear that I could make changes confidently in minutes.

I’ve learned that the best code isn’t the cleverest or the most optimized, but the clearest. Code that any developer can read and understand. Code that doesn’t need a deciphering manual.

Applying these principles constantly isn’t about perfection, it’s about continuous improvement. Every time I touch code, I try to leave it a little better.

Because at the end of the day, you’re programming for humans. And the most important human you’re programming for is your future self, who will thank you for writing code they can understand.