21

Isn't it interesting to find a misplaced and useless comment in the code?

How can you write very few comments while still keeping the code easy to understand?

One major way is to make the code self-documenting. When code is self-documenting, there is no need for comments to explain its function or purpose, and it also makes the code very easy to maintain.

In this article, I will provide some ways to make your code self-documenting. Below are three basic methods for making code self-documenting:

  • Naming: Use names to explain the purpose of variables, functions, etc.
  • Encapsulating functions: Encapsulate code with specific functionality into a function to clarify its purpose.
  • Introducing variables: Put expressions into dedicated variables.

This may seem simple, but in practice it can be a bit tricky. First, you need to understand where the problems are and where these methods apply.

In addition, besides the above three, there are some more widely applied approaches:

  • Class and module interfaces: Expose the functions in classes and modules to make the code clearer.
  • Code grouping: Use groups to distinguish different code segments.

Next, we will use examples to specifically discuss how to apply the above 5 methods in practical use.

1. Naming

First, look at a few examples of how code becomes clear and self-documenting when naming is used.

1. Renaming functions

Naming functions is not very difficult; you can follow these rules:

  • Avoid vague words such as 'handle' or 'manage' — handleLinks, manageObjects.
  • Use active verbs — cutGrass, sendFile — to indicate that the function actively performs an action.
  • Specify the return value type — getMagicBullet, READFILE. Strongly typed languages can also use type identifiers to indicate the function's return type.

2. Renaming variables

  • Specify units — if there are numeric parameters, you can add their units. For example, use widthPx instead of width to specify that the unit of width is pixels.
  • Do not use shortcuts — a and b should not be used as parameter names.

2. Function Encapsulation

Next, look at a few examples of how to encapsulate code into functions. One advantage of encapsulating functions is avoiding code repetition, or improving the code structure.

1. Encapsulate code into functions

This is the most basic: encapsulate code into a function to clarify its purpose. Guess what the following line of code does:

var width = (value - 0.5) * 16;

It may not be very clear; of course, with a comment it would be perfectly clear, but we can simply encapsulate it into a function to achieve self-documentation...

var width = emToPixels(value);
 
function emToPixels(ems) {
    return (ems - 0.5) * 16;
}

The only change is that the computation process has been moved into a function. The function name clearly expresses what it does, so there is no need to write comments. Moreover, if needed later, this function can be called directly, killing two birds with one stone and reducing redundant work.

2. Replace conditional expressions with functions

If statements containing multiple operands are harder to understand without comments.

if(!el.offsetWidth || !el.offsetHeight) {
}

Do you know the purpose of the code above?

function isVisible(el) {
    return el.offsetWidth && el.offsetHeight;
}
 
if(!isVisible(el)) {
}

3. Introducing Variables

Finally, let's talk about how to introduce variables. Compared with the above two methods, this one may be less useful, but in any case, knowing it is better than not knowing it.

1. Replace expressions with variables

Look at this example

if(!el.offsetWidth || !el.offsetHeight) {
}

This time, instead of encapsulating into a function, replace it by introducing variables.

var isVisible = el.offsetWidth && el.offsetHeight;
if(!isVisible) {
}

2. Use variables instead of equations

We can also use it to clearly explain complex formulas:

return a * b + (c / d);

Replace them with variables.

var divisor = c / d;
var multiplier = a * b;
return multiplier + divisor;

4. Class and Module Interfaces

The interfaces of classes and modules — that is, public-facing methods and properties — are somewhat like documentation explaining how to use them.

Look at the following example:

class Box {
    public function setState(state) {
        this.state = state;
    }
 
    public function getState() {
        return this.state;
    }
}

This class could also contain other code. I deliberately chose this example to illustrate how the public interface can be self-documenting.

Can you tell how this class is called? Obviously, it is not obvious.

Both of these functions should be given reasonable names to express their purposes. But even if that is done, we still are not very clear about how to use them. Then we need to read more code or consult documentation.

But what if we change it like this...

class Box {
    public function open() {
        this.state = open;
    }
 
    public function close() {
        this.state = closed;
    }
 
    public function isOpen() {
        return this.state == open;
    }
}

Isn't it much clearer? Note: we only changed the public interface; its internal representation is the same as the original this.state state.

5. Code Grouping

Using groups to distinguish different code segments is also a form of self-documentation. For example, as stated in this article, we should define variables as close as possible to where they are used, and classify variables as much as possible. This can also be used to specify the relationships between different code groups, making it more convenient for others to know which code groups they still need to understand.

Look at the following example:

var foo = 1;
 
blah()
xyz();
 
bar(foo);
baz(1337);
quux(foo);

Compared with the following:

var foo = 1;
bar(foo);
quux(foo);
 
blah()
xyz();
 
baz(1337);

Group all uses of foo together, and you can see the relationships at a glance. But sometimes we have to call some other functions in between. So if possible, try to use code grouping; if not, don't force it.

6. Other Suggestions

  • Don't use strange markers; the following two are equivalent
imTricky && doMagic();
if(imTricky) {
    doMagic();
}

Obviously the latter is better. Syntactic tricks do not bring any benefit.

  • Name constants: if there are some special values in the code, it is best to name them. var PURPOSE_OF_LIFE = 42;
  • Establish rules: It is best to follow consistent naming rules. This way, readers can correctly guess the meaning of various things by referencing other code.

Source: http://www.ido321.com/1360.html