The practical guide to write useful comments

The practical guide to write useful comments

This is a practical guide for writing good & helpful comments, the ability to tag & navigate them and how to automate them.

ยท

7 min read

1. The Need

I don't think anyone will agree that writing comments in your code are a waste of time and effort. Then Why do most people don't really write good & useful comments? Why do they willingly or unwillingly make their own life & experience difficult?

image.png You all must have seen this meme & then laugh & then move on. This is a very serious problem. Hers is one more,

image.png So why do we fall into such a pitfall even knowing pretty well that there will be a pitfall ahead? Here are some of the reasons that I think are primary contributors:

  • The comments don't go hand in hand with the code & look out of the place.
  • In an already massive code base, it becomes even more difficult to navigate them.
  • The rush to commit the code.
  • Some unspoken but reality of human behaviour like, "I wrote the code so beautifully that it is self-explanatory" (Yeah, maybe to yourself but not so obvious to others). OR "If I am just going to write the comments then when I will write the actual code" (writing comments is not an afterthought but you should write them along with the code)
  • Finally, some people are just lazy. Nothing can be done about them. As they are already on the path which will surely make their life difficult in future.

So not writing comments is more of a philosophical or even behavioural problem rather than technical or knowledge problem.

2. How to write good comments

There are many good blogs out there (this one is very good) which explains what is good content for comments. My focus is on the practicality & ease of access. Following are the steps that you must follow for writing good comments.

2.1 Philosophical change

This can be either very easy or very difficult to adapt. It's upon you. The ideal way would be to have comments more than the actual lines of code. One more thing that will help is while writing a code think that you are not writing this for yourself but for others.

2.2 Tagging comments

Writing just the comments (even if their contents is good) usually is not that helpful because navigating them becomes difficult. So for this, I have come up with a system of 'Tagging Comments'. It is nothing fancy, you just have to start a comment with its type. Here are the type that I use,

  • ANCHOR - Used to indicate a section in your file
  • TODO - An item that is awaiting completion, address something, etc in future
  • FIXME - An item that requires an immediate bugfix
  • NOTE - An important note for a specific code section to fetch the attention of a fellow developer
  • REVIEW - An item that requires additional review, very useful during pull requests or merges
  • DEPRECATED - An item which is no longer being used & will be removed in future
  • WARNING - An warring showing for the following item, if not followed respective bad thing can happen
  • SECTION - Used to define a region
  • LINK - Used to link to a file that can be opened within the editor (See 'Link Anchors')
  • EG - An example of what we should expect in the following item

Let me show some practical examples of how they should be used

# 1. Without comments
with open(file_path) as data_file:
    yield from reader(data_file)

# 2. With comments
# TODO - which encoding to use?
with open(file_path) as data_file:
    # spiting out only unit data lazily
     yield from reader(data_file)

Any linter will give a warning in the above example as no encoding was provided. But I didn't know the answer straight away, so I used a TODO tag to resolve it once I'll know the answer.

# saving the current iteration's payload
# FIXME - Input request is also sending a set in the payload, which it should not as set cannot to
# save as json. Temporary fix is to save it as a text file
DataWriter.str_to_txt(
    str(req_body),
    f"/research/sessions/{uuid}/payload.txt",
)

The above code snippet will still work, but its behaviour is incorrect & it must be changed. That's why I used a FIXME tag. Now how it's different from the TODO tag? So FIXME should be used when you know for certain that the following item will definitely turn into a potential bug & TODO should be used for a wide variety which may not be necessary bugs or code-breaking items.

# to let joblib release all workers gracefully from memory. NOTE - It's only needed here # because the same joblib workers from cleaning ops are used by entity extraction ops & RQ does not need
# joblib's multiprocessing
time.sleep(5)

A NOTE tag is used to bring the attention of fellow developers to the following item. This type of comment has more importance than a regular comment.

SECTION tag should use to group certain business logics that can be put under the common bucket. This becomes very helpful in the case of a pipeline.

# NOTE - Following ETL pipeline will only work with prod configuration
# SECTION - Part A: Extract 
collect_data(source)
clean_data(raw_data)
dump_clean_data(clean_data)

# SECTION - Part B: Transform
read_clean_data(clean_data)
transform_data(data)

# SECTION - Part C: Load
load_data_to_db(transformed_data)

# SECTION - Part D: Post-processing
clean_environment()

Everyone has the habit of commenting out certain business logic or some code snippet. And this is not wrong. There can be some valid reason to still keep the commented code snippet. But this becomes extremely confusing for others as they might not be aware of the reason. Ultimately, this leads to difficulty in maintaining the code. So it's better to add a DEPRECATED along with the reason.

# DEPRECATED - blob storage as a source is not required for now & maybe removed completely in future.
#blob_data_source = [unit_source for unit_source in data if unit_source.is_present()]

For EG tag I follow a couple of rules of thumb,

  1. If you think certain logic is not clear, then add an EG comment displaying what can be the potential value will be.
  2. For every nested loop I write an example regarding what to expect in the next level.
# EG - "Random123 hello @#" will become "andom hello"
regex_pattern = "[^a-z]"
result = re.sub(regex_pattern, "", text)

for key,val in random_dict.items():
    # EG - another_random_dict[val] = 'some str'
    for val in another_random_dict:
        if isinstance(another_random_dict[val], str):
            some_list.append(another_random_dict[val])

2.3 Navigate & automate comments

One of the reasons that I mentioned above is why people don't want to write comments is difficult in navigating them. If I don't have a solution for this problem then there is no point of this blog ๐Ÿ˜…. So let me introduce you to an amazing VS Code extension - Comment Anchors which I used very heavily for the above use case.

  • It searches for the tag & creates a bookmark. So that using its tree structure you can quickly jump to it. Also, it can act as a one-stop to track all important tags like FIXME, TODO, REVIEW, etc. Go through its docs for more info.

image.png

  • The extension comes with some default tags/strings to bookmark but with the ability to customize it. The following is the one that I am using (You can refer to their docs on how to customize it according to your specific needs)
"commentAnchors.tags.list": [
    {
        "tag": "ANCHOR",
        "iconColor": "default",
        "highlightColor": "#A8C023",
        "scope": "file"
    },
    {
        "tag": "TODO",
        "iconColor": "blue",
        "highlightColor": "#3ea8ff",
        "scope": "workspace"
    },
    {
        "tag": "FIXME",
        "iconColor": "red",
        "highlightColor": "#F44336",
        "scope": "workspace",
        "isBold": true
    },
    {
        "tag": "NOTE",
        "iconColor": "orange",
        "highlightColor": "#FFB300",
        "scope": "file",
        "styleComment": true
    },
    {
        "tag": "REVIEW",
        "iconColor": "green",
        "highlightColor": "#64DD17",
        "scope": "workspace"
    },
    {
        "tag": "SECTION",
        "iconColor": "blurple",
        "highlightColor": "#896afc",
        "scope": "workspace",
        "behavior": "region"
    },
    {
        "tag": "LINK",
        "iconColor": "#2ecc71",
        "highlightColor": "#2ecc71",
        "scope": "workspace",
        "behavior": "link"
    },
    {
        "tag": "DEPRECATED",
        "iconColor": "#B22222",
        "highlightColor": "#B22222",
        "scope": "workspace",
        "behavior": "anchor",
        "isBold": true
    },
    {
        "tag": "WARNING",
        "iconColor": "#B22222",
        "highlightColor": "#B22222",
        "scope": "workspace",
        "behavior": "anchor",
        "isBold": true
    },
    {
        "tag": "EG",
        "iconColor": "#00FFFF",
        "highlightColor": "#eb667d",
        "backgroundColor": "rgba(49, 184, 79, 0.2)",
        "borderStyle": "1px solid #23b2ea",
        "borderRadius": 6,
        "scope": "workspace",
        "styleComment": true
    }
],

You can add this to your VS code settings.json file.

3. How to write a good message commit message

  • Writing a good commit message is also extremely important. The same pitfalls as above are true here too.
  • Just writing something like updated xyz.py or deleted abc.txt or moved some_file.js or bug fix etc is very bad practice. It does not provide any context & becomes difficult to track changes using git blame.
  • How to follow common standards across your team? What should be these standards? No need to reinvent the wheel and just use Conventional Commits.
  • It is based on excellent Conventional Commits 1.0.0 spec. You can go through the spec (it is definitely a good read).
  • It's very easy, intuitive & fun (as it also supports gitmoji ๐Ÿคฉ) to use, trust me. Follow its doc to understand more.

image.png

Did you find this article valuable?

Support import idea by becoming a sponsor. Any amount is appreciated!

ย