×
Community Blog The Art of Code Comments: Does Good Code Need Comments?

The Art of Code Comments: Does Good Code Need Comments?

This article discusses code comments, including their importance, significance, and controversies.

By Xiaolong Nie (Shuaige)

1. Preface

Writing clear comments for bad code is the last thing we want to do. The most reasonable thing to do is usually to optimize it. If we make good code, do we still need comments?

2. The Significance of Comments

; **************************************************************************
; * RAMinit Release 2.0 *
; * Copyright (c) 1989-1994 by Yellow Rose Software Co. *
; * Written by Mr. Leijun *
; * Press HotKey to remove all TSR program after this program *
; **************************************************************************
; Removed Softwares by RI:
; SPDOS v6.0F, WPS v3.0F
; Game Busters III, IV
; NETX ( Novell 3.11 )
; PC-CACHE
; Norton Cache
; Microsoft SmartDrv
; SideKick 1.56A
; MOUSE Driver
; Crazy (Monochrome simulate CGA program)
; RAMBIOS v2.0
; 386MAX Version 6.01

Comments are used to explain and describe the code. The essential purpose is to enhance the readability and interpretability of programs. Comments are put in the preprocessor or compiler with the source code and then removed after processing. This is the assembly code of MASM written by Lei Jun in 1994. The comments and the overall structure of the code are very clear. If the code is for the machine to understand our instructions, the comments are for us to understand the instructions we have given.

3. Controversies and Disagreements

Comments are created so early that we can't look up the origin. Now, any language or text format supports a wide variety of comments.

However, how to use comments has always been a controversial topic. When we take over someone else's code, we may feel uncomfortable about not having comments or not enough comments. Some people believe code with comments must have some flaws; thus, perfect code does not need comments.

4. Lifesavers for Bad Code

“The proper use of comments is to compensate for our failure to express ourself in code.”
By Robert C. Martin (Author of Clean Code)

Robert C. Martin (Author of Clean Code) is a strong denier of comments. He believes that comments are a failure, that comments are only used when we cannot find a way to express ourselves without comments, and that we should realize that it is a failure in our ability to express ourselves at any time we use comments.

Peter Vogel (Architect and Head of PH&V) is also a staunch denier of comments. He has published an article entitled Why Commenting Code is Still a Bad Idea to state that commenting may be necessary to some extent, but it is really worthless.

4.1 No Comments

1

The last straw that crushes a programmer is often no comments. There can be no documents or design, but without comments, every time we read the code is a disaster. When we complain about no comments, we are actually complaining about the difficulty in understanding the code. Comments are the direct cause, but the root cause is the code.

4.2 Useless Comments

/**
 * returns the last day of the month
 * @return the last day of the month
 */
public Date getLastDayOfMonth(Date date) {
    Calendar calendar = new GregorianCalendar();
    calendar.setTime(date);
    calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
    return calendar.getTime();
}

This is typical nonsense comments because the code can express the specific meaning very well. We don't need to read the comments at all, and it cannot provide more effective information. Useless comments may be the other extreme of no comments. We worry that the code we write will be ridiculed, so we try our best to complete the comments. When you write a comment – get last day of month – for getLastDayOfMonth(), congratulations, you get double the code.

4.3 Better Code Than Comments

"Comments do not make up for bad code.”
By Robert C. Martin (Author of Clean Code)

When the code needs comments, it shows that it cannot express the intent well, and then we write comments for the bad code. Robert C. Martin makes a point in Clean Code, “Comments do not make up for bad code. What can be expressed in code should be expressed directly in code, and for what cannot, think about how to express it in code.”

Complex code is not intuitive and difficult to understand. Although comments can make it clear, would you rather read this complex code or this simple code? The complex code is listed below:

// Determine if a customer is active.
if((customer.getLastLoginTime().after(dateUtils.minusDays(new Date(),15)) && customer.getCommentsLast30Days() > 5) 
    || orderService.countRecentDaysByCustomer(customer,30) > 1)

The simple code is listed below:

if(customer.isActive())

Bad code is usually one of our common motivations for writing comments, which even can be seen in JDK.

public synchronized void setFormatter(Formatter newFormatter) {
    checkPermission();
    // Check for a null pointer
    newFormatter.getClass();
    formatter = newFormatter;
}

The code above is the method of setFormatter() method from JDK java.util.logging.Handler class. In order to avoid the null pointer exception, the programmer does a check for a null pointer in advance. Without this comment, we have no idea what newFormatter.getClass() is for. The programmer knows the difficulty in understanding the code, so he adds this comment, but we can replace it with Objects.requireNonNull(). Although the two pieces of code produce the same result, the readability and understandability are very different. This code in JDK is really regrettable.

4.4 Comment Negation

“If our programming languages were expressive enough, or if we had the talent to subtly wield those languages to express our intent, we would not need comments very much-perhaps not at all.”
By Robert C. Martin (Author of Clean Code)

Expressing through code is the core idea of comment negation. Instead of spending time thinking about how to write comments to make the code more expressive, we should refactor the code to explain our intent directly. Each comment is a poor evaluation of our code expression ability. It is better to solve problems through better induction, expression, and interpretation rather than comments. When the code is good enough, comments are unnecessary. As requirements evolve, the code will change, but the comments may not. When the comments do not match the code, it will be a greater disaster.

5. The Utopia of Software Design

5.1 Good Code

Once, I delved into good code. If my code could not be understood by others, I would think my expression is not accurate enough, and the abstraction is not reasonable enough. So, I went to refactor and improve it.

Once my manager reviewed my code. He said, “Your code lacks comments.” I said that my code is self-documenting without comments. So, he read a piece of code on the spot, query-customer-list, transfer-customer-to-sales, and check-sales-capacity. When each class and function was read aloud word by word, he found that he could really understand it, so he replied, “Alright.”

5.2 A Beautiful Utopia

“Good code is self-documenting. It is a delicious myth.”
By John Ousterhout (Author of A Philosophy of Software Design)

In A Philosophy of Software Design, Professor John Ousterhout stated, “Good code is self-documenting” – is a beautiful lie.

We can reduce comments by choosing better variable names, more accurate classes and methods, and more reasonable inheritance and derivation, but we still have a lot of information that cannot be expressed directly through the code. The information here may not just be about business logic and technical design but may also include our perception, experience, acceptance, and primacy effect.

6. The Best Wingman for Good Code

“You might think the purpose of commenting is to 'explain what the code does,' but that is just a small part of it. The purpose of commenting is to help the reader know as much as the writer did.”
By Dustin Boswell (Author of The Art of Readable Code)

Like Professor John Ousterhout, Dustin Boswell is a strong proponent of comments. Like Robert C. Martin, Dustin Boswell believes that we should not write comments for facts that can be quickly inferred from code. He also opposes crutch comments and believes that comments do not make up for bad code.

According to Dustin Boswell, the purpose of commenting is not only to explain what the code does, which is just a small part of it. The most important purpose is to help the reader understand as much as the author. When commenting, we need to stand in the reader's perspective and think about what they know, which is the core of commenting. There are a lot of points when code is difficult or impossible to explain. So, code with comments is not bad code, but sometimes, comments are the best wingman for good code.

6.1 A More Accurate Expression

“There are only two hard things in Computer Science: cache invalidation and naming things.”
By Phil Karlton

In TwoHardThings, Martin Fowler quotes Phil Karlton, saying naming has always been very difficult because we need to condense all the meaning into a few words. When I learned Java a long time ago, I learned a long class name, ClassPathXmlApplicationContext. Some people may think that as long as the meaning can be accurately expressed, it doesn't matter if the name is long. If we need to deal with the content of the Belt and Road Initiative, our code might look like this:

public class TheSilkRoadEconomicBeltAndThe21stCenturyMaritimeSilkRoad {

}

The code expresses the meaning accurately, but this is not the code we expected. However, the code will be very clear if we supplement it with a simple comment, which explains the short name and the full meaning, making the expression more accurate.

/**
 * The Belt and Road Initiative
 * The Silk Road Economic Belt and the 21st-century Maritime Silk Road
 */
public class OneBeltOneRoad {

}

6.2 Code Layering

Function extraction is one of the most frequently used and least costly refactoring methods, but it is not a silver bullet. Functions are not supposed to be extracted as finely as possible. Just like in a distributed system, it is not that the less data each machine processes through infinitely adding machines, the faster the whole process will be. Too deep nested encapsulation will increase our code reading cost. Sometimes, we only need a certain layer and structure to help us understand. Blind extraction and encapsulation are meaningless.

/**
 * Query customer list.
 */
public List queryCustomerList(){
    // Prepare query parameters.
    UserInfo userInfo = context.getLoginContext().getUserInfo();
    if(userInfo == null || StringUtils.isBlank(userInfo.getUserId())){
        return Collections.emptyList();
    }
    LoginDTO loginDTO = userInfoConvertor.convertUserInfo2LoginDTO(userInfo);
    // Query customer information.
    List<CustomerSearchVO> customerSearchList = customerRemoteQueryService.query(loginDTO);
    Iterable<CustomerSearchVO> it = customerSearchList.iterator();
    // Exclude non-compliant customers.
    while(it.hasNext()){
        CustomerSearchVO customerSearchVO = it.next(); 
        if(isInBlackList(customerSearchVO) || isLowQuality(customerSearchVO)){
            it.remove();
        }
    }
    // Add other attributes of a customer.
    batchFillCustomerPositionInfo(customerSearchList);
    batchFillCustomerAddressInfo(customerSearchList);
}

Every piece of code is easy to understand. However, if it is not commented on, we may have trouble reading it. The lack of structure and stratification makes it very complicated, and we need to understand multiple contents at one time. Layering the code by using comments is a division of abstraction levels. At the same time, we do not recommend abstracting private methods. Since the code will become very fragmented, the background logic of the context, the passing of parameters, etc., will bring additional trouble.

7. The True Attribution of Comments

7.1 Complex Business Logic

// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
    throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
    // Not found -> check parent.
    String nameToLookup = originalBeanName(name);
    if (args != null) {
        // Delegation to parent with explicit args.
        return parentBeanFactory.getBean(nameToLookup, args);
    }
    else {
        // No args -> delegate to standard getBean method.
        return parentBeanFactory.getBean(nameToLookup, requiredType);
    }
}

This is the code in Spring to obtain bean. As a management container, Spring requires complex logic to obtain bean. We can better understand complex business scenarios and implementation logic with some necessary comments.

From: org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

7.2 Obscure Algorithm Formulas

/**
 * Returns the value obtained by reversing the order of the bits in the
 * two's complement binary representation of the specified {@code long}
 * value.
 */
public static long reverse(long i) {
    // HD, Figure 7-1
    i = (i & 0x5555555555555555L) << 1 | (i >>> 1) & 0x5555555555555555L;
    i = (i & 0x3333333333333333L) << 2 | (i >>> 2) & 0x3333333333333333L;
    i = (i & 0x0f0f0f0f0f0f0f0fL) << 4 | (i >>> 4) & 0x0f0f0f0f0f0f0f0fL;
    i = (i & 0x00ff00ff00ff00ffL) << 8 | (i >>> 8) & 0x00ff00ff00ff00ffL;
    i = (i << 48) | ((i & 0xffff0000L) << 16) |
        ((i >>> 16) & 0xffff0000L) | (i >>> 48);
    return i;
}

This is a method in the Long class in JDK that adds enough comments for the reverse() method. For low-level code that has little change and is used frequently, performance takes precedence over readability. Comments make up for the poor readability while ensuring high efficiency.

From: java.lang.Long#reverse

7.3 Unknown Constants

/**
 * The bin count threshold for using a tree rather than list for a
 * bin.  Bins are converted to trees when adding an element to a
 * bin with at least this many nodes. The value must be greater
 * than 2 and should be at least 8 to mesh with assumptions in
 * tree removal about conversion back to plain bins upon
 * shrinkage.
 */
static final int TREEIFY_THRESHOLD = 8;

This is a constant factor of HashMap in JDK, which records the length threshold of the linked list to the red-black tree. If the length exceeds this threshold, the linked list is converted to the red-black tree. An 8 is recorded here, which records the purpose of the constant and why we define this value. We often find that there is a constant that equals 3 or 4 in the code. Sometimes we don't know what 3 and 4 mean, and sometimes, we don't know why the constant equals 3 or 4.

From: java.util.HashMap#TREEIFY_THRESHOLD

7.4 Unexpected Behavior

for (int i = 0; i < 3; i++) {
    // if task running, invoke only check result ready or not
    Result result = bigDataQueryService.queryBySQL(sql, token);
    if (SUCCESS.equals(result.getStatus())) {
        return result.getValue();
    }
    Thread.sleep(5000);
}

The code above and the comment show that every five seconds, check whether there is a result returned, and the remote service puts invoking and obtaining in an interface. Without the comment, we may think that there is something wrong with the code. The meaning of the code is more like calling every five seconds rather than checking every five seconds. Adding comments for unexpected behavior can reduce the misinterpretation of the code and explain the necessary background and logical information to the reader.

7.5 External API

/**
 * <p>Checks if a CharSequence is empty (""), null or whitespace only.</p>
 * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
 * StringUtils.isBlank(null)      = true
 * StringUtils.isBlank("")        = true
 * StringUtils.isBlank(" ")       = true
 * StringUtils.isBlank("bob")     = false
 * StringUtils.isBlank("  bob  ") = false
 *
 * @param cs  the CharSequence to check, may be null
 * @return {@code true} if the CharSequence is null, empty or whitespace only
 */
public static boolean isBlank(final CharSequence cs) {
    final int strLen = length(cs);
    if (strLen == 0) {
        return true;
    }
    for (int i = 0; i < strLen; i++) {
        if (!Character.isWhitespace(cs.charAt(i))) {
            return false;
        }
    }
    return true;
}

The isBlank() method in the StringUtils tool class that we often use has very detailed comments, including the logic of the method, the meaning of the input parameters, and specific samples. The interface definitions of HSF and HTTP in the second-party library we usually define also need clear and detailed comments, and the comments here are often even more than code. From: org.apache.commons.lang3.StringUtils#isBlank

7.6 Legal Documents

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

Comments related to the law are more often seen in open-source software libraries. When it comes to some copyright and copyright claims, we need to put legal comments at the top of the source file. We don't need to write all the legal information in the comments. In this example, putting a link to jump to a standard external document is a better choice.

8. Summary

Comments don't prevent us from writing elegant and concise code. They are just an inherent part of the program. We don't need to care too much about whether our code can be separated from comments, nor do we need to emphasize that because our code conforms to some principles and conventions. The code is excellent, and the comments are redundant.

References

[1] A Philosophy of Software Design by John Ousterhout

[2] Clean Code by Robert C. Martin

[3] The Art of Readable Code by Dustin Boswell and Trevor Foucher

1 2 1
Share on

Alibaba Cloud Community

879 posts | 198 followers

You may also like

Comments

Dikky Ryan Pratama July 13, 2023 at 1:33 am

Awesome!