A Guide to Clean Code Principles 

What is Clean Code? 

Robert C. Martin introduced the ‘Clean Code’ concept in his book ‘Clean Code: A Handbook of Agile Software Craftsmanship’. He defined clean code as: 

“A code that has been taken care of. Someone has taken the time to keep it simple and orderly. They have laid appropriate attention to details. They have cared.”

Clean code is easy to read, understand, and maintain. It is well structured and free of unnecessary complexity, code smell, and anti-patterns. 

Key Characteristics that Define Clean Code

  • The code is easy to read and understand. The names are descriptive of variables, functions, and classes, and the code is structured for a clear purpose. 
  • The code is simple and doesn’t include any unnecessary complexity. 
  • The code is consistent in naming conventions, formatting, and organization to help maintain readability. 
  • The code is easy to test and free from bugs and errors. 
  • The code is easy to update and modify. 
  • Clean code is regularly refactored and free from redundancy. 

Clean Code Principles 

Single Responsibility Principle 

This principle states that each module or function should have a defined responsibility and one reason to change. Otherwise, it can result in bloated and hard-to-maintain code. 

Example: the code’s responsibilities are separated into three distinct classes: User, Authentication, and EmailService. This makes the code more modular, easier to test, and easier to maintain.

class User {

  constructor(name, email, password) {

    this.name = name;

    this.email = email;

    this.password = password;

  }

}

class Authentication {

  login(user, password) {

    // ... login logic

  }

  register(user, password) {

    // ... registration logic

  }

}

class EmailService {

  sendVerificationEmail(email) {

    // ... email sending logic

  }

}

DRY Principle (Don’t Repeat Yourself) 

The DRY Principle states that unnecessary duplication and repetition of code must be avoided. If not followed, it can increase the risk of inconsistency and redundancy. Instead, you can abstract common functionality into reusable functions, classes, or modules.

Example: The common greeting formatting logic is extracted into a reusable formatGreeting function, which makes the code DRY and easier to maintain.

function formatGreeting(name, message) {

  return message + ", " + name + "!";

}

function greetUser(name) {

  console.log(formatGreeting(name, "Hello"));

}

function sayGoodbye(name) {

  console.log(formatGreeting(name, "Goodbye"));

}

YAGNI – you aren’t gonna need it

YAGNI is an extreme programming practice that states “Always implement things when you actually need them, never when you just foresee that you need them.” 

It doesn’t mean avoiding flexibility in code but rather not overengineer everything based on assumptions about future needs. The principle means delivering the most critical features on time and prioritizing them based on necessity. 

Kiss - Keep it Simple, Stupid 

This principle states that the code must be simple over complex to enhance comprehensibility, usability, and maintainability. Direct and clear code is better to avoid making it bloated or confusing. 

Example: The function directly multiplies the length and width to calculate the area and there are no extra steps or conditions that might confuse or complicate the code.

def calculate_area(length, width):

    return length * width

The Boy Scout Rule 

According to ‘The Boy Scout Rule’, always leave the code in a better state than you found it. In other words, make continuous, small enhancements whenever engaging with the codebase. It could be either adding a feature or fixing a bug. It encourages continuous improvement and maintains a high-quality codebase over time. 

Example: The original code had unnecessary complexity due to the redundant variable and nested conditional. The cleaned-up code is more concise and easier to understand.

Before: 

def factorial(n):

    if n == 0:

        return 1

    else:

        return n * factorial(n - 1)

# Before:

result = factorial(5)

print(result)

# After:

print(factorial(5))

After: 

def factorial(n):

    return 1 if n == 0 else n * factorial(n - 1)

Fail Fast

This principle indicates that the code must fail as early as possible. This limits the bugs that make it into production and promptly addresses errors. This ensures the code remains clean, reliable, and usable. 

Open/Closed Principle 

As per the Open/Closed Principle, the software entities should be open to extension but closed to modification. This means that team members must add new functionalities to an existing software system without changing the existing code. 

Example: The Open/Closed Principle allows adding new employee types (like "intern" or "contractor") without modifying the existing calculate_salary function. This makes the system more flexible and maintainable.

Without the Open/Closed Principle 

def calculate_salary(employee_type):

    if employee_type == "regular":

        return base_salary

    elif employee_type == "manager":

        return base_salary * 1.5

    elif employee_type == "executive":

        return base_salary * 2

    else:

        raise ValueError("Invalid employee type")

With the Open/Closed Principle 

class Employee:

    def calculate_salary(self):

        raise NotImplementedError()

class RegularEmployee(Employee):

    def calculate_salary(self):

        return base_salary

class Manager(Employee):

    def calculate_salary(self):

        return base_salary * 1.5

class Executive(Employee):

    def calculate_salary(self):

        return base_salary * 2

Practice Consistently 

When you choose to approach something in a specific way, ensure maintaining consistency throughout the entire project. This includes consistent naming conventions, coding styles, and formatting. It also ensures that the code aligns with team standards, to make it easier for others to understand and work with. Consistent practice also allows you to identify areas for improvement and learn new techniques.

Favor composition over inheritance

This means to use ‘has-a’ relationships (containing instances of other classes) instead of ‘is-a’ relationships (inheriting from a superclass). This makes the code more flexible and maintainable.

Example: In this example, the SportsCar class has a Car object as a member, and it can also have additional components like a spoiler. This makes it more flexible, as we can easily create different types of cars with different combinations of components.

class Engine:

    def start(self):

        pass

class Car:

    def __init__(self, engine):

        self.engine = engine

class SportsCar(Car):

    def __init__(self, engine, spoiler):

        super().__init__(engine)

        self.spoiler = spoiler

Avoid Hard-Coded Number

Avoid hardcoded numbers, rather use named constants or variables to make the code more readable and maintainable.

Example: 

Instead of: 

discount_rate = 0.2

Use: 

DISCOUNT_RATE = 0.2

This makes the code more readable and easier to modify if the discount rate needs to be changed.

Typo - An Automated Code Review Tool

Typo’s automated code review tool enables developers to catch issues related to code issues and detect code smells and potential bugs promptly. 

With automated code reviews, auto-generated fixes, and highlighted hotspots, Typo streamlines the process of merging clean, secure, and high-quality code. It automatically scans your codebase and pull requests for issues, generating safe fixes before merging to master. Hence, ensuring your code stays efficient and error-free.

The ‘Goals’ feature empowers engineering leaders to set specific objectives for their tech teams that directly support writing clean code. By tracking progress and providing performance insights, Typo helps align teams with best practices, making it easier to maintain clean, efficient code. The goals are fully customizable, allowing you to set tailored objectives for different teams simultaneously.

Conclusion 

Writing clean code isn’t just a crucial skill for developers. It is an important way to sustain software development projects.

By following the above-mentioned principles, you can develop a habit of writing clean code. It will take time but it will be worth it in the end.