Clean Code

Maintaining code quality is one of the basic concepts developers must follow in software engineering. However, developers often spend more time maintaining code than actual coding. In fact, a developer spends an average of 13.5 hours a week on technical debt. That’s over 700 hours a year spent on fixing past mistakes.

Do not be too stressed about this, as you can easily manage technical debt by implementing clean coding principles.

What is Clean Code?

Clean code is code that works and is easy to read, understand, and maintain. The term caught fire with the release of Robert C. Martin’s influential book Clean Code: A Handbook of Agile Software Craftsmanship; hence, he became popularly known as Uncle Bob. Clean code practices make it easier for new developers to understand code quickly, reducing onboarding time and avoiding confusion.

Importance of Clean Code

  • Improved collaboration: When code is well-structured, team members can easily understand each other’s work, making collaboration smoother.
  • Reduced technical debt: Clean code rules reduce technical debt, avoiding the need to go back and fix shortcuts that might save time initially but cause issues later.
  • Improved maintainability: The presence of clean, self-documenting code provides developers with the ability to change or rectify a code portion without unintentionally altering other segments.
  • Productivity: With clear, organized code, developers spend less time dealing with messy or “spaghetti” code and more time delivering real value.

Clean Coding Principles

clean coding principles

1. Single Responsibility Principle (SRP)

Every class or function in a program should have one reason to change, meaning it must focus on doing just one thing or responsibility. This makes the code modular and easier to test, debug, and update without affecting other parts of the system.

class UserCreator {
  createUser(userData) {
    // Code to create a user
  }
}

class EmailService {
  sendWelcomeEmail(userEmail) {
    // Code to send a welcome email
  }
}

class UserActivityLogger {
  logUserActivity(userId) {
    // Code to log user activity
  }
}

2. DRY (Don’t Repeat Yourself)

DRY says you keep your code tidy by not writing the same stuff over and over. Instead, you’ll put common code in one place so it can be reused wherever needed.

Violates DRY Principle:

// Repeated validation code for different fields
if (username.length < 5) {
alert("Username is too short!");
}

if (password.length < 8) {
alert("Password is too short!");
}

if (email.indexOf('@') === -1) {
alert("Invalid email!");
}

Follows DRY Principle:

function validateField(field, condition, errorMessage) {
if (!condition(field)) {
alert(errorMessage);
}
}

validateField(username, (field) => field.length >= 5, "Username is too short!");
validateField(password, (field) => field.length >= 8, "Password is too short!");
validateField(email, (field) => field.includes('@'), "Invalid email!");

3. KISS (Keep It Simple, Stupid)

KISS encourages keeping the code simple and avoiding unnecessary complexity.

Overly Complex Code (Violates KISS):

function getUserInfo(id) {
  let user = null;
  if (id !== null && id !== undefined) {
    for (let i = 0; i < users.length; i++) {
      if (users[i].id === id) {
        user = users[i];
        break;
      }
    }
  }
  return user;
}

Simplified Code (Follows KISS):

function getUserInfo(id) {
  return users.find(user => user.id === id) || null;
}

4. Meaningful Variable and Function Names

Self-describing names for variables, functions, and classes make code self-explanatory and easier to read without needing comments.

Bad Variable Name:

let x = 5 # What does 'x' represent?

Good Variable Name:

let total_score = 5 # Now it's clear this variable holds a score.

Bad Function Name:

function process() {
  // Unclear purpose
}

Good Function Name:

function calculate_total_score(){
// Clearly indicates the purpose
}

5. Error Handling

Effective error handling prevents the application from crashing unexpectedly, improving the user experience. Using try-catch blocks and specific error-handling mechanisms allows graceful recovery.

Without Error Handling:

let data = fs.readFileSync('file.txt', 'utf8');
process(data);
// This code will crash if the file does not exist.

With Error Handling:

const fs = require('fs');

try {
  let data = fs.readFileSync('file.txt', 'utf8');
  process(data);
} catch (error) {
  if (error.code === 'ENOENT') {
    console.log("File not found. Please check the filename.");
  } else {
    console.log(An unexpected error occurred: ${error.message} );
  }
}
qodo
Code. As you meant it.
TestGPT
Try Now

Common Clean Code Rules

common clean code rules

1. Avoid Deep Nesting

Excessive nesting within loops or conditions makes code difficult to read and understand. Deep nesting increases cognitive load as it becomes harder to track the flow and structure of the code. Flattening code structures by using early returns, guard clauses, or splitting logic into smaller functions can greatly enhance readability.

Deeply Nested Code:

function checkUserPermissions(user) {
  if (user.isActive()) {
    if (user.isAdmin()) {
      if (user.hasPermission('edit')) {
        console.log("User has permission to edit.");
      } else {
        console.log("User does not have permission to edit.");
      }
    } else {
      console.log("User is not an admin.");
    }
  } else {
    console.log("User is not active.");
  }
}

Flattened Structure:

function checkUserPermissions(user) {
  if (!user.isActive()) {
    console.log("User is not active.");
    return;
  }
  if (!user.isAdmin()) {
    console.log("User is not an admin.");
    return;
  }
  if (!user.hasPermission('edit')) {
    console.log("User does not have permission to edit.");
    return;
  }
  console.log("User has permission to edit.");
}

2. Keep Functions Short

Short functions with a single purpose are easy to read, understand, and test. A rule of thumb is that a function should not be longer than 20 lines. Simpler functions also reduce the bug count because each functionality is self-contained and easy to understand.

function validateUser(user) {
  return user ? true : "Invalid user";
}

function calculateScore(user) {
  let score = user.age * 2;
  if (user.country === "US") score += 10;
  if (user.hasSubscription) score += 5;
  return score;
}

function processUserData(user) {
  const validation = validateUser(user);
  if (validation !== true) return validation;
  return calculateScore(user);
}

3. Limit the Use of Comments

Comments are helpful, but some redundant comments may clutter the code. As a rule, self-documenting code needs fewer comments. Only comment on the sections that are difficult to understand and unintuitive or explain why one way of doing something is different from another.

Self-Explanatory Code:

function handleAdminAccess(user) {
if (user.isLoggedIn && user.isAdmin) {
accessAdminPanel();
}
}

Wrapping Up

Clean code makes a product better, keeps code readable for future developers, reduces bugs, and speeds up delivery. It’s recommended to start with small improvements—like clear naming, consistent formatting, and adding comments. The time you spend writing clean code today saves hours of troubleshooting tomorrow.