No Comment

No Comment

Back in 2004, I was fresh out of a French Engineering school and decided to move to London to start my career as a software developer. I joined a start-up that had slightly slimmed down after the dot-com bubble.

On my first day at work we picked a User Story and started pair programming, they called it Extreme Programming and since I was new to the real world of enterprise I thought this was the normal way of working. It was much later that I realised this was not the normal way of working and this company invented the concept of User Story that we know today.

I started to get familiar at the code of the application, one comment puzzle me:

/*
 * Comments are Bad
 */

And this was the only single comment in the entire application codebase.

Are Comments Bad?

How can an entire application be developed and supported without any comment in the code?

The answer came after a few weeks working with the application codebase. Not using comment in code was in fact a consequence of excellent engineering practice. The code didn't need to be commented because reading the code itself should be enough to understand it. If the code wasn't clear we would refactor it mercilessly thanks to the safety net provided by exceptional test coverage. In turn those tests provide a good alternative to comment to explain what the code does.

In other word I am not saying "comment are bad" but "comment are bad because it highlights an insufficient quality and readability in the code". To improve code quality we have one tool: Refactoring.

Uncle Bob was even more opinionated and considered that comment is an apology from the developer. An apology for writing "clever code", not using good variable name, not completing a task or not writing clean code.

Avoid duplication

Commenting code is effectively duplication, comment are a different way to express what is written in code. Since there is duplication there is a risk that comment don't get maintain when the code change and become stale.

What we will see is a simple strategy to maintain clean code and leverage the compiler to document our code.

Don't comment the obvious

Assume the person that will be reading your code knows well the language you are using.

int i = s.indexOf("/"); // find the first index of /

Any normal Java developer should know what the indexOf method does. Your production code shouldn't be used to teach programming for other team member.

Name variable appropriately

The following code:

double m = km * 0.621371; // convert km in miles

Can be made more expressive using good variable name and appropriate constant.

double miles = kilometre * KILOMETRE_TO_MILES;

Good method name

The next simple refactoring could be through method extraction.

double miles = convertToMiles(kilometer);

Using the following method:

public double convertToMiles(double kilometers){
  return kilometers * KILOMETRE_TO_MILES;
}

Value Class

At that point we could make the good more explicit by wrapping the kilometer into its own class which would show at compile time misused of the method.


public class Convertor {
  public static Miles convertToMiles(Kilometer kilometers){
    return Miles.of(kilometers.value() * KILOMETRE_TO_MILES);
  }
}

As you start extracting all those methods you should see emerging a clean and logical grouping structure within class and package. This will in turn encourage further eradication of duplication and promote reuse.

Use Object representation

Or instead we wrapped all our logic into its own class:

public class Distance {

    public static final double KILOMETER_TO_MILES = 0.621371;
    private final double kilometre;

    private Distance(double kilometre) {
        this.kilometre = kilometre;
    }
    
    public static Distance ofKilometre(double kilometre){
        return new Distance(kilometre);
    }

    public double toMiles(){
        return kilometre * KILOMETER_TO_MILES;
    }

}

Edge case situation

We sometimes feel that comment are necessary to describe edge case scenario.

private void Distance(double kilometre) {
    if (kilometre < 0) { // Distance should be positive
        throw new IllegalArgumentException("Distance is negative");
    }
    this.kilometre = kilometre;
}

In this situation we have two options: extract a method or write a unit test (or both).

Extract method:

private Distance(double kilometre) {
    assertDistanceIsPositive(kilometre);
    this.kilometre = kilometre;
}

private void assertDistanceIsPositive(double kilometre) {
    if (kilometre < 0) {
        throw new IllegalArgumentException();
    }
}

Document using a Unit Test:

public class DistanceTest {

    @Test(expected = IllegalArgumentException.class)
    @DisplayName("Distance should be positive")
    public void distanceShouldBePositive() {
        Distance.ofKilometre(-100);
    }
}

Complex method

If you see a long and complex method a good strategy that usually work is divide and conquer: break the complexity in smaller method.

  • Extract block of code in their own method.
  • Extract an inner loop in their own method
  • Repeat until the code is clear, concise and unambiguous

Collective code ownership

A concept that became popular with Extreme Programming (XP) is Collective Code Ownership. It states that there isn't a single line of code that belong to a single person and that the entire codebase is collectively own by the team.

In this context it's your right or even responsibility to rewrite or refactor code that you feel is not clear enough or require comment to be understood.

Refactoring should always be done with a safety net: a set of unit, functional test you will be able after completing the work. If you are missing tests you should write them first before attempting refactoring.

The general programming lifecycle proposed by the XP methodology look like this:

Collective code ownership

Exception in using comment

Comment can be used at a method level to describe the method behavior in a JavaDocs style especially when publishing the code as a library. In that case the comment are not used to explain the code.

Using comment to explain the code can be used exceptionally when complex optimisation are taking place, and we need to help the reader follow the logic. Unless you are working on some cutting edge technology like writing a compiler or embedded application you should favor code readability over its performance (to some degree). Trust me, the compiler will usually do a better job than you to optimise your code. If you still believe in performance optimisation you should only do it with performance metrics that highlight an area that need optimisation.

Final comment

Not commenting code can feel unnatural and scary. If you feel your code need commenting, do it, but ask yourself the question: what could be done to make your code so clear it can be read ... without comment.

Program for People. Programs must be understood by both man and machine.

Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live- John Woods

To be continued

This article follow the article from Yves Caseau "Software-Defined Excellence for Michelin" on driving the Company through Software. We will continue on this subject and explore how we can apply a Software culture to our data product.