How to write a good Clean code? – Tips for Developers with Examples

Imagine a room with clothes, books, and other stuff lying around everywhere. It would be hard to find anything in that mess, right?

It’s the same when it comes to writing code. Think about it this way: It would be much harder to deal with messy code, right?

On the other hand, a clean code is much like an organized room.

So let’s get started by looking at this little graph here: it shows two different ways of writing code and how they affect the time it takes to add more lines:

⚠️ Quick & Dirty Code (Red line): This happens when you write code quickly without planning or organizing it properly. It might feel faster at first, but as the code grows, it becomes harder to read and fix. Over time, adding new features or changes takes much longer.

⚡ Thoughtful & Clean Code (Blue line): This is when you write code carefully, making it easy to understand and change. At first, it might take a bit longer, but it remains easy to work with over time. This way, adding new lines doesn’t become more difficult.

Writing clean code may feel slower at first, but in the long run, it saves time and makes your work much easier. Clean code leads to more reliable software and better results.

Professional developers focus on writing clean code because it reflects their commitment to quality and a strong work ethic. In this article, I’ll share some best practices to help you keep your code clean and maintainable.

Here are 10 practical tips to help you start on your clean code journey: Keep your code readable, organized, and efficient.

1. Use Meaningful Names

How Can Using Meaningful Names Help You Write Better Code?

Writing clean, readable, and maintainable code is a key component of productivity for developers. One of the most important practices that can significantly improve your code is using meaningful names for your variables, functions, classes, and other elements. This not only makes your code more understandable but also easier to maintain and scale. In this article, we’ll explore how using meaningful names can help you become a more productive and efficient coder.

a) Improves Code Readability

Using descriptive names for variables, functions, and classes makes your code much easier to read. When you write code, it’s crucial to make it understandable for both yourself and other developers who may be working on the project. Meaningful names provide immediate clarity, saving you time in the future when you revisit your code.

Example:

# Bad naming
def calculate(x, y):
    return x + y

# Good naming
def calculateTotalPrice(itemPrice, tax):
    return itemPrice + tax

In the “Good naming” example, it’s immediately clear what the function does and what parameters are involved, making the code much easier to read and maintain.

b) Facilitates Collaboration

When working in a team, clear and meaningful names ensure that everyone can quickly grasp the purpose of the code. This reduces the need for explanations or excessive documentation. It also prevents errors, increases efficiency, and helps team members quickly onboard new parts of the project.

Example:

// Meaningful names for variables
let orderTotal = 150;  // Total price after discount
let userLocation = 'New York';  // Customer's shipping address

Using descriptive names like orderTotal and userLocation makes it clear what each variable represents, which is crucial for teamwork and collaboration.

c) Helps with Debugging and Maintenance

Debugging and maintaining code is often the most time-consuming part of development. Meaningful names can help you spot issues faster and understand the code’s behavior at a glance. By avoiding ambiguous or cryptic names, you can reduce the mental effort needed to track down bugs or make improvements in the future.

Example:

// Bad: unclear function purpose
bool validateData(int a, int b) {
    if(a == b) return false;
    return true;
}

// Good: function name explains its purpose
bool areValuesEqual(int firstValue, int secondValue) {
    if(firstValue == secondValue) return false;
    return true;
}

In the “Good” example, the function name areValuesEqual makes it clear that the purpose is to compare two values, improving readability and maintainability.

d) Enhances Code Flexibility

When your code is written with meaningful names, it is much easier to extend or refactor in the future. If a variable or function name clearly communicates its purpose, you’ll spend less time trying to understand how things work when you need to modify or extend them.

e) Promotes Self-Documentation

One of the key benefits of meaningful naming is that it often eliminates the need for extensive comments. Good names act as self-documenting elements of your code, making it clear what each piece of code is doing. While comments are still important for explaining why something is done, the what can often be communicated through names alone.

Example:

# Bad: unclear function name
def calc(a, b):
    return a * b

# Good: clear function name
def calculateAreaOfRectangle(length, width):
    return length * width

In the “Good” example, the function name calculateAreaOfRectangle immediately indicates what the function does, so there’s no need for extra comments to explain it.

f) Improves Scalability

As your project grows in size, the complexity of your codebase will increase. Using meaningful names makes it easier to scale your code without confusion. When your code is well-organized and the names are clear, you can confidently add new features or refactor existing code without introducing bugs or making the system harder to maintain.

g) Reduces Dependency on Comments

While comments are useful for explaining why certain decisions were made, clear naming reduces the need for excessive comments. When variable and function names clearly describe their purpose, you can rely less on writing long comment blocks, which in turn keeps your codebase clean and efficient.

Example:

// Bad: unclear variable names
let t = 500;
let d = '2024-12-11';
let c = 'NYC';

// Good: descriptive names
let totalAmount = 500;
let orderDate = '2024-12-11';
let customerLocation = 'NYC';

In the “Good” example, totalAmount, orderDate, and customerLocation all clearly communicate their purpose, making additional comments unnecessary.

Using meaningful names is one of the easiest and most effective ways to write better, more maintainable code. By following the practices outlined in this article, you’ll improve the readability, maintainability, and scalability of your code, making it easier to debug and extend in the future. Clear, descriptive names will not only improve your personal productivity but also make collaboration with teammates more efficient.

By focusing on writing code that speaks for itself, you’ll be able to build more robust, scalable, and efficient software that is easier to maintain in the long run. With the right naming conventions, your code will be cleaner, more consistent, and ultimately more enjoyable to work with.

2.Follow the Single Responsibility Principle (SRP)

In the previous section, we discussed how meaningful names can improve your code. Now, let’s talk about another essential principle that will help you write cleaner, more maintainable code: the Single Responsibility Principle (SRP).

The Single Responsibility Principle is one of the most effective ways to reduce complexity and improve the readability of your code. In simple terms, SRP states that a class or function should have only one reason to change—in other words, it should only handle one responsibility.

By following SRP, you ensure that your code is modular, flexible, and easier to maintain in the long run. Let’s dive into how SRP can make you a more productive developer and improve the quality of your projects.

Why SRP is Critical for Writing Better Code

When you follow SRP, your code becomes simpler, cleaner, and easier to modify. But the benefits don’t stop there. Here’s how SRP helps you write better code:

a) Easier to Maintain and Update

Code that adheres to SRP is much easier to maintain. Since each class or function has only one responsibility, any changes to that responsibility are isolated to that specific module. This makes your code less fragile and more resilient to bugs when modifications are made.

Example:

Imagine you have a class that handles both user input and saving data to the database. If you need to update the input logic, you don’t want to accidentally affect the database logic. By separating these responsibilities, you ensure that changes in one area don’t impact the others.


b) Simpler to Test and Debug

When classes or functions follow SRP, they are easier to test because they only do one thing. You can write unit tests to verify each part of your system in isolation, making it easier to spot bugs and understand where issues arise. With a clear focus on one responsibility, debugging becomes a faster and less painful process.

Example:

pythonCopier le code# Bad Example: A class handling multiple responsibilities
class Order:
    def __init__(self, items):
        self.items = items

    def calculate_total(self):
        total = 0
        for item in self.items:
            total += item.price
        return total

    def apply_discount(self):
        # Discount logic
        pass

    def print_order(self):
        # Printing order details
        pass

# Good Example: Following SRP by splitting responsibilities
class Order:
    def __init__(self, items):
        self.items = items

class PriceCalculator:
    def calculate_total(self, order):
        total = sum(item.price for item in order.items)
        return total

class OrderPrinter:
    def print_order(self, order):
        # Code to print the order
        pass

In the good example, each class has one responsibility: Order holds the data, PriceCalculator computes the total, and OrderPrinter prints the order. This makes it much easier to test each component independently.


c) Enhances Code Flexibility and Scalability

As your project grows, SRP helps ensure that new features or changes don’t cause unintended side effects. Because responsibilities are clearly separated, it’s easier to extend or update specific parts of your application without breaking the rest of the system. This scalability is crucial for projects that evolve over time.

For instance, if you need to add new payment methods to your system, you can introduce a new class to handle payment processing, leaving the existing code intact. This modular approach allows for easy growth.


d) Improves Collaboration Across Teams

When working in a team, following SRP makes it easier for multiple developers to work on the same project without stepping on each other’s toes. If each class or function handles one responsibility, team members can work on separate modules independently, reducing the risk of conflicts and speeding up development.

For example, one developer can work on the UI layer, while another works on the database layer, and they won’t need to worry about messing with each other’s code.


e) Reduces Complexity and Improves Readability

Code that adheres to SRP is simpler and easier to understand. You avoid large, complex classes that try to do too much, making the codebase more approachable for new developers and future you. Reduced complexity leads to cleaner code, which is much easier to debug, maintain, and extend over time.


How to Apply SRP in Your Code

Now that we know the benefits of SRP, how can you apply this principle in your own code? Here are some simple tips:

  1. Identify Responsibilities: When you write a class or function, ask yourself: What is this class responsible for? If it has more than one responsibility, split it up into smaller classes.
  2. Separate Concerns: Don’t let one module handle unrelated tasks. For example, separate business logic from database access, or user input from output logic. Each module should focus on a single concern.
  3. Keep Functions Small: Functions should do one thing and do it well. If a function starts growing too large, break it down into smaller, more manageable functions that each handle a specific task.
  4. Refactor When Necessary: As your code evolves, revisit existing classes or functions to ensure they still follow SRP. Refactor them if they start accumulating unrelated responsibilities.
  5. Use Clear Naming: When applying SRP, make sure your class and function names clearly reflect their responsibility. This makes your code easier to understand and maintain.

Conclusion: SRP for Better, More Maintainable Code

The Single Responsibility Principle (SRP) is a key best practice that every developer should follow to write cleaner, more maintainable code. By ensuring that each class or function has only one responsibility, you make your code easier to maintain, easier to test, and easier to scale.

By integrating SRP into your development process, you not only improve the quality of your code but also your productivity as a developer. SRP leads to modular, flexible, and scalable software that can evolve over time without becoming tangled in complexity.

Incorporating SRP alongside other best practices, such as using meaningful names and focusing on modularity, will significantly improve your coding habits and project outcomes.

3. Avoid Unnecessary Comments

In the previous sections, we’ve discussed the importance of meaningful names and following the Single Responsibility Principle (SRP) to write cleaner, more maintainable code. Now, let’s talk about another essential practice that can significantly improve the clarity of your code: avoiding unnecessary comments.

While comments can be helpful when explaining complex logic or providing context, excessive or redundant comments often clutter the code, making it harder to read and maintain. The goal should always be to write self-explanatory code—code that clearly conveys its intent without needing an abundance of comments.

Let’s explore why unnecessary comments should be avoided and how you can write code that speaks for itself.


Why Avoiding Unnecessary Comments is Important

In an ideal world, code should be so clear and well-structured that it doesn’t need constant explanations. Over-commenting can lead to several issues:

  1. Clutter and Confusion: Too many comments can overwhelm the reader, making it difficult to focus on the code itself.
  2. Outdated Comments: If the code changes but the comments don’t, outdated comments can mislead developers, creating more confusion.
  3. Decreased Readability: Comments that state the obvious or simply restate what the code does make the code less readable and more difficult to maintain.

a) Write Self-Explanatory Code

The best way to avoid unnecessary comments is to write self-explanatory code. This means using clear variable names, properly structured logic, and modular functions that are easy to follow. Code should be written in such a way that someone unfamiliar with it can understand what it’s doing by simply reading the code itself.

Example:

pythonCopier le code# Bad: The comment doesn't add any value
count = 0  # Initialize count to 0

# Good: The code is clear without the need for a comment
items_in_cart = 0  # Clear and descriptive variable name

In the bad example, the comment doesn’t provide any additional value because the variable name count is vague. In the good example, items_in_cart clearly describes the purpose of the variable, making the comment redundant. When you choose descriptive names for your variables and functions, comments become less necessary.


b) Remove Redundant Comments

Avoid comments that explain what the code is doing when it’s already obvious from the code itself. Redundant comments are often repetitive and add unnecessary clutter. For example, a comment like “increment i by 1” next to the line i += 1 is not helpful because it simply restates the obvious.

Example:

pythonCopier le code# Bad: Redundant comment explaining obvious code
i += 1  # Increment i by 1

# Good: No need for a comment
i += 1

In the good example, the code is self-explanatory, and no comment is needed. Comments should be used to explain why something is done, not what is being done—unless the logic is particularly complex.


c) Use Comments to Explain “Why,” Not “What”

While avoiding unnecessary comments is important, there are cases where comments can add value—especially when explaining why certain decisions were made or outlining complex logic. If the code is doing something non-obvious or there is a specific reason for a certain implementation, it can be helpful to add a comment to clarify that decision.

Example:

pythonCopier le code# Bad: Explaining simple logic
total = price * quantity  # Multiply price and quantity

# Good: Explaining the reasoning behind a decision
# Applying discount based on customer loyalty level
if loyalty_level == 'gold':
    total = price * quantity * 0.8  # 20% discount for gold members

In the good example, the comment explains why the 20% discount is applied, which is important information for future developers working with this code. The comment adds value and provides context, which is often missing in straightforward, self-explanatory code.


d) Keep Comments Relevant and Up-to-Date

One of the most common issues with comments is that they can become outdated as the code changes. A comment that was once accurate might no longer reflect the current functionality of the code. Outdated comments are misleading and can lead to bugs or confusion.

Always ensure that comments are kept up-to-date. If you refactor or change the code, update the corresponding comments, or, better yet, remove them if they’re no longer necessary.


e) Avoid Commenting “Bad” Code

Sometimes developers comment out code that is considered “bad” or “temporary,” such as debugging code or workarounds. Instead of leaving such code in place with a comment explaining why it’s there, remove it if possible. Commenting out “bad” code creates unnecessary clutter and can be confusing for other developers.

Example:

pythonCopier le code# Bad: Commenting out work-in-progress code
# temp_fix = calculate_total(items)  # Temp fix until bug is resolved

# Good: Clean code, with unnecessary comments removed
calculate_total(items)  # Proper solution applied

The comment about a “temporary fix” is unnecessary once the issue is resolved. It’s better to remove commented-out code and keep the codebase clean.


f) Focus on Clean, Readable Code Design

A big part of avoiding unnecessary comments is having a good overall code design. If your code is structured well—following best practices like modularization, clear naming conventions, and the Single Responsibility Principle (SRP)—it will be easier to read and require fewer comments.

By focusing on writing code that clearly expresses intent, you minimize the need for comments. This leads to a cleaner, more maintainable codebase that’s easier for you and your team to understand and modify.


Conclusion: Let Your Code Speak for Itself

Avoiding unnecessary comments is one of the easiest ways to improve the readability and maintainability of your code. The goal should be to write self-explanatory code that doesn’t require constant explanations. Use comments strategically to explain why something is done, not what is being done, and ensure that comments are kept relevant and up-to-date.

By following this practice, you’ll find that your code becomes much easier to understand, even for developers unfamiliar with it. Less clutter means better clarity, which leads to more productive teams and fewer errors. So, focus on writing clean, well-structured code and let your code do the talking.

Leave a Comment

Your email address will not be published. Required fields are marked *