Comments

Comments in source code are often overused. They are commonly used to describe what the code does or to mark different code sections. However, when following clean code principles the code itself should be expressive enough so that comments that describe what the code does or how it does something should not be necessary. Instead, if comments have to be used at all they should be used to explain why code is doing something.

The proper use of comments is to compensate for our failure to express ourself in code. Note the I used the word failure. I meant it. Comments are always failures.
- Robert C. Martin

Comments can lie

A good reason why comments should be avoided is that they can often be wrong or misleading, for example when a developer changes something in the code, but forgets to update a comment that explains what the code previously did.

The example below demonstrates how a simple typing error can make the documentation comment of a function misleading. When reading the documentation comment of the function, one could believe that the code will load configuration from a file called defaults if it can’t load it from the provided file path. However, the code tells us that the file is actually called .defaults.

def load_config(parser: ConfigParser, config_path: str) -> list[str]:
    """
    Loads configuration from a config file.
    Falls back to the defaults file on fail.
    """
    config = parser.read(config_path)
    if not config:
        config = parser.read(".defaults")

    return config

Express yourself in code

Whenever you encounter a comment that describes what the following code section does, this code section can most likely be converted to a function.

Do Not!
input_number = input("Enter a number")

# check if input_number is a prime number
is_prime = True
for i in range(input_number):
    if input_number % i == 0:
        is_prime = False
        break

print(input_number, "is a prime:", is_prime)
Do
def is_prime(number):
    for i in range(input_number):
        if input_number % i == 0:
            return False

    return True

input_number = input("Enter a number")
print(input_number, "is a prime:", is_prime(input_number))

Avoid comments that don’t add value

Often comments are used, because their usage is mandated or simply because the author of the code felt the need to describe something. But often theses comments don’t add any actual value to the code. Avoid comments like this whenever you can.

Do Not!
class Container:
    def __init__(self, logger):
        # The logger associated with this container
        self.logger = logger
Do Not!
class MyClass:
    # ========== CONSTRUCTOR ==========
    def __init__(self):
        self.value = 0

    # ========== SETTER ==========
    def set_value(self, value):
        self.value = value

Documentation comments

Most programming support some form of comments for functions that will be shown with the autocompletion of our editors or that can be used to generate documentation. Often organizations mandate that these comments are used on every function in a code base. But especially in private functions these comments often don’t add any value. However, in functions intended to be used by other people, these comments can be helpful by providing additional information about them.

Do Not!
def _is_sad(maybe_sad: str) -> bool:
    """Checks if string is sad

    Args:
        maybe_sad(str): a string

    Returns:
        bool
    """
    return ":(" in maybe_sad or "T_T" in maybe_sad


def make_happy(sad_string: str) -> str:
    """Makes a sad string happy

    Args:
        sad_string(str): A string with sad smileys

    Returns:
        str
    """
    if not _is_sad(sad_string):
        return sad_string

    return sad_string.replace(":(", ":)").replace("T_T", "^_^")

The _is_sad function in the code sample above only serves as a utility used by the make_happy function and will most likely never be used anywhere else. Adding a documentation comment there does not add any real value, but rather just adds another comment the developer has to keep up to date as the code base evolves over time. It is therefore safe to remove it.

Do Not!
def _is_sad(maybe_sad: str) -> bool:
    return ":(" in maybe_sad or "T_T" in maybe_sad


def make_happy(sad_string: str) -> str:
    """Makes a sad string happy

    Args:
        sad_string(str): A string with sad smileys

    Returns:
        str
    """
    if not _is_sad(sad_string):
        return sad_string

    return sad_string.replace(":(", ":)").replace("T_T", "^_^")

Good comments

There are of course also situations where the use of comments is perfectly fine. For example comments that why something is done the way it is or amplification comments that highlight the importance of a certain code section. Another good use of comments is to warn developers of possible bugs that may not have been solved yet. Finally, todo comments are also fine to use to a certain extent. Of course the developers have to make sure that the todos don’t pile up and actually get worked on.