← Back to Blog
Software Development13 min

Clean Code Is Not About Cleanliness

By Noor Ali MomtazJanuary 22, 2026
Clean Code Is Not About Cleanliness

The Clean Code Trap

We've all read the books. We know the rules. But clean code isn't about perfect formatting or clever abstractions. It's about one thing: making future changes easy.

What Clean Code Actually Means

Clean code minimizes the time to understand what changes are needed and where to make them. That's it. Everything else is secondary.

Bad "Clean" Code

// Technically clean: Short functions, no duplication
function getUserDisplayName(user) {
  return formatName(extractFirstName(user), extractLastName(user));
}

function extractFirstName(user) {
  return user.name.split(' ')[0];
}

function extractLastName(user) {
  return user.name.split(' ')[1];
}

function formatName(first, last) {
  return `${first} ${last}`;
}

// Result: 4 functions to do something trivial
// Nobody can find where names are formatted

Actually Clean Code

// Simple and obvious
function getUserDisplayName(user) {
  const [firstName, lastName] = user.name.split(' ');
  return `${firstName} ${lastName}`;
}

// Or even simpler if you just need the name:
const displayName = user.name;

The Real Rules

1. Optimize for Reading, Not Writing

Code is read 10x more than it's written. Optimize for the reader:

// Hard to read (but short to write)
const p = u.filter(x => x.a && x.s === 'active').map(x => x.id);

// Easy to read (slightly longer to write)  
const activePaidUsers = users
  .filter(user => user.isPaid && user.status === 'active')
  .map(user => user.id);

2. Comments Explain Why, Not What

// Bad: Stating the obvious
// Increment the counter
counter++;

// Good: Explaining the reason
// Keep session alive during long-running export
counter++;

// Better: Code that doesn't need explanation
session.keepAlive();

3. Don't Abstract Until You Have 3 Examples

// Premature abstraction (2 similar cases)
function createEntity(type, data) {
  if (type === 'user') {
    return createUser(data);
  } else if (type === 'company') {
    return createCompany(data);
  }
}

// Better: Just call them directly
await createUser(userData);
await createCompany(companyData);

// Abstract when you have 3+ cases and clear pattern

Code Smells That Actually Matter

1. Functions That Do Multiple Things

// Does too much
function handleCheckout(cart) {
  validateCart(cart);
  const total = calculateTotal(cart);
  const discount = applyDiscount(total);
  const tax = calculateTax(total - discount);
  chargePayment(user, total - discount + tax);
  updateInventory(cart.items);
  sendConfirmationEmail(user);
  logAnalytics('checkout', { total, items: cart.items.length });
}

// Better: One clear purpose
function processCheckout(cart) {
  const order = await createOrder(cart);
  await chargePayment(order);
  await fulfillOrder(order);
  return order;
}

2. Boolean Parameters

// What does true mean here?
fetchUsers(true);

// Self-documenting
fetchUsers({ includeInactive: true });

// Or separate functions
fetchAllUsers();
fetchActiveUsers();

3. Long Parameter Lists

// Too many parameters
function createUser(name, email, age, country, timezone, language, theme) {
  // ...
}

// Better: Use an object
function createUser({ name, email, age, country, timezone, language, theme }) {
  // ...
}

// Even better: Split into smaller operations
const user = await createUser({ name, email });
await setUserPreferences(user.id, { timezone, language, theme });
await setUserProfile(user.id, { age, country });

When to Break the Rules

Performance

// Readable but slow (O(n²))
const duplicates = arr.filter((item, index) => 
  arr.indexOf(item) !== index
);

// Less obvious but fast (O(n))
const seen = new Set();
const duplicates = arr.filter(item => {
  if (seen.has(item)) return true;
  seen.add(item);
  return false;
});
// Add comment explaining why

External Constraints

// Sometimes APIs force ugly code
const response = await stripe.charges.create({
  amount: amount * 100, // Stripe wants cents
  currency: 'usd',
  source: token,
  metadata: {
    orderId: order.id.toString(), // Must be string
  },
});

// Document the weirdness
/** 
 * Stripe requires:
 * - Amounts in cents (multiply by 100)
 * - Metadata values as strings
 */

Refactoring Strategy

The Boy Scout Rule

Leave code better than you found it, but don't rewrite everything:

// Before: You're adding a feature
function processOrder(order) {
  const t = order.items.reduce((a,i) => a + i.price, 0);
  const s = t * 0.1;
  // Your new feature here
}

// After: Clean up what you touch
function processOrder(order) {
  const subtotal = calculateSubtotal(order.items);
  const shipping = calculateShipping(subtotal);
  // Your new feature here
}

function calculateSubtotal(items) {
  return items.reduce((total, item) => total + item.price, 0);
}

Testing Clean Code

Clean code is testable code:

// Hard to test (side effects, dependencies)
function sendWelcomeEmail(userId) {
  const user = db.users.find(userId);
  const email = emailService.render('welcome', user);
  emailService.send(user.email, email);
  logger.info(`Sent welcome email to ${user.email}`);
}

// Easy to test (pure function, injected dependencies)
function createWelcomeEmail(user, templates) {
  return {
    to: user.email,
    subject: 'Welcome!',
    html: templates.render('welcome', user),
  };
}

// Test without databases or email service
test('creates welcome email', () => {
  const email = createWelcomeEmail(
    { email: 'test@example.com', name: 'John' },
    mockTemplates
  );
  expect(email.to).toBe('test@example.com');
  expect(email.html).toContain('John');
});

Code Review Checklist

  • Can I understand what this does in 30 seconds?
  • Are variable names clear and specific?
  • Is the code doing one thing well?
  • Could I test this easily?
  • Will this be easy to change later?
  • Are edge cases handled?
  • Is error handling clear?

What Actually Matters

  1. Naming - Good names eliminate comments
  2. Small functions - But not too small
  3. No surprises - Code does what it says
  4. Error handling - Fail loudly and clearly
  5. Consistency - Same patterns throughout

Conclusion

Clean code isn't about perfection. It's about empathy for future developers (including future you). Write code that's easy to understand, easy to change, and easy to test.

Rules are guidelines, not laws. Break them when needed, but know why you're breaking them. The goal is maintainability, not rule-following.

← Back to All Articles