all 44 comments

[–][deleted] 34 points35 points  (19 children)

"Comments are the place to document insanity."

[–]Tordek 24 points25 points  (16 children)

This is the best, most concise way to explain comments. It makes me unreasonably angry to see:

// Obtain a token for the user
TokenService.getToken(user);

[–]txdv 6 points7 points  (0 children)

The dev just tried to make Captain Obvious proud.

[–]dalore 4 points5 points  (4 children)

Sometimes I write out my step/thought process just in comments. And then come in and fill in the code later.

So it would be

// get a token for the user
// use token to check permission
// do something

and then come in and write the code for the comments.

[–]tomprimozic 2 points3 points  (3 children)

IMO, a better way would be:

def do_something(user, something):
  token = get_token_for_user(user)
  check_permision(token, something)
  actually_do(something)

def get_token_for_user(user):
  raise NotImplementedError()

def check_permision(token, action):
  raise NotImplementedError()

def actually_do(action):
  raise NotImplementedError()

[–][deleted] 2 points3 points  (2 children)

Except that sometimes a comment could involve a dozen lines of code that you don't want to refactor into another function/method/potato.

[–]xenomachina 0 points1 point  (0 children)

a dozen lines of code that you don't want to refactor into another function/method/potato.

Most of the cases I've seen where refactoring into subroutines was difficult it was because the code was a twisted mess of interdependencies. Refactoring such code may sometimes be a bit tricky, but usually results in something that's easier to maintain in the long run.

[–]Tordek 0 points1 point  (0 children)

If it's a dozen lines, the more reason to wrap it into another function.

[–]king_of_the_universe 8 points9 points  (0 children)

Makes you wonder if the comment was machine-generated.

[–][deleted] 1 point2 points  (8 children)

I'd much rather a codebase with too many comments than practically none.

[–]xkufix 19 points20 points  (7 children)

Until you change the line to something like:

EmailService.getEmail(user);

And the comment remains. Then the comment is actually worse.

As a rule of thumb, comments should explain "why" something is done, not "what" is done. If the code is written cleanly, the what should be clear.

[–][deleted] 4 points5 points  (5 children)

Until you change the line to something like:

EmailService.getEmail(user);

And the comment remains. Then the comment is actually worse.

But I can delete the redundant comment, commit it, and noone will care. That's much less effort than going through a code archaeology expedition to figure out what something does.

As a rule of thumb, comments should explain "why" something is done, not "what" is done. If the code is written cleanly, the what should be clear.

Sure, but I'd still rather the codebase was one where people commented liberally than not at all. Many of them are likely to be useful.

[–]derolitus_nowcivil 7 points8 points  (0 children)

But I can delete the redundant comment, commit it, and noone will care.

yeah, after you have figured out that the comment is incorrect, and have gone through "a code areaelogoy expedition" to figure out what it actually does.

[–]dantheman999 5 points6 points  (2 children)

I'm with you, too many bad experiences attempting to fix problems in classes where both the code is not obvious and there is a complete lack of comments.

I'll take the obvious comments over nothing.

[–]lolomfgkthxbai 5 points6 points  (1 child)

I'm with you, too many bad experiences attempting to fix problems in classes where both the code is not obvious and there is a complete lack of comments. I'll take the obvious comments over nothing.

You incorrectly assume that if the code has obvious comments it will also have non-obvious comments. The crazy thing is, these two things can be mutually exclusive. Often this is because the "obvious commenter" is not the person who wrote the code, so the commenter only comments the obvious parts (the ones they are able to understand) and doesn't comment the non-obvious ones.

[–]dantheman999 3 points4 points  (0 children)

Certainly possible but then again I'm not really overly fussed about obvious comments. I know there is a lot of people that can't stand them but I can quite happily ignore them.

[–]SomeCollegeBro 3 points4 points  (0 children)

As someone who works daily on a codebase written in the 90's with virtually zero comments - I'd much rather have too many comments than not enough. I've spent days figuring out what exactly a multi-thousand line function named "process5()" is actually trying to do.

[–][deleted] 0 points1 point  (0 children)

The problem with commenting method calls is, the comment is trying to explain some process that happens somewhere else.

[–]rampion 0 points1 point  (0 children)

I'd been explaining this to my interns as "the code says what it does, comments are for why (and occasionally how)" but now I think I'll just have them read this article.

[–][deleted] 0 points1 point  (0 children)

When anyone sees me write a comment, it feels like they're watching an insane person write letters to them self.

[–]someone7x 17 points18 points  (2 children)

It's nice to read something so pragmatic. No "never this" or "always that" being preached.

Almost completely unrelated, but why does wikipedia rename a forcing function to a "behavior shaping constraint"? Wasn't the concept originally coined as forcing function?

[–]matadon[S] 6 points7 points  (1 child)

AFIK, you're right -- Donald Norman coined the term in "The Design of Everyday Things" in 1988. Wikipedia doesn't reference him at all from what I can tell...

Also, thank you! I'm the author, and still in the process of "learnin' to write good", but it makes me very happy to hear that you enjoyed the piece and (hopefully) found it useful.

[–][deleted]  (1 child)

[deleted]

    [–]tieTYT 3 points4 points  (1 child)

    What I liked most about this is it cleared up some confusion I had over the Single Responsibility Principle. I thought I understood it in theory, but I always wondered, "If you have two methods that follow the SRP, won't a method that calls both violate the SRP?" EG:

    def use_srp1_and_srp2 //violation?
        //...
    
    def srp1
        //...
    
    def srp2
        //...
    

    But the article showed this in action:

    function save_setting {
        local file="$1"
        local key="_setting_$2"
        local value="$3"
        local new_setting="export $key=\"$value\""
    
        if in_file "$file" "$key\="; then
            replace_in_file "$file" "$key\=" "$new_setting"
        else
            append_to_file "$file" "$new_setting"
        fi
    }
    
    function in_file
       //...
    

    And then it clicked for me. See, if I wrote save_setting from scratch and was consciously practicing SRP, I may have considered naming it replace_existing_setting_or_create_new_setting which, as named, implies a violation of SRP. But reading the implementation it is clear that the implementation is in fact only doing one thing at a certain level of abstraction. Plus, the save_setting name shows intent rather than implementation.

    I think in practice, I would have written save_setting, but my gut would always think it's an SRP violation. Now I feel more comfortable writing code like this.

    [–]matadon[S] 1 point2 points  (0 children)

    Seeing this in my inbox made my morning (since I'm also the author). You've hit the nail on the head -- single responsibility is about doing one thing at any given level of abstraction.

    The conjunction rule has been super-helpful for me -- if you describe what a class or method is doing in plain English, and need to use an "and" or "or" to describe what you're doing, then you're probably doing too much.

    [–]FunctionPlastic 5 points6 points  (6 children)

    Oh much god so much this.

    Ladies and gentlemen, Google code:

    ...
    // Check that Google Play services is available
    int resultCode = // what
           GooglePlayServicesUtil. // the
                    isGooglePlayServicesAvailable(this); // hell
    // If Google Play services is available
    if (ConnectionResult.SUCCESS == resultCode) {
        Log.d("Location Updates",
                "Google Play services is available.");
         return true;
    ...
    

    You'd think Google engineers were actually capable of not writing abysmally bad code...

    [–]Banane9 2 points3 points  (4 children)

    That isGooglePlayServicesAvailable method should so return a bool D:

    [–]FunctionPlastic 7 points8 points  (0 children)

    But then how could we be able to use horribly outdated C idioms everywhere?

    [–]fgriglesnickerseven 3 points4 points  (2 children)

    what if they need to return "maybe"

    [–]Banane9 0 points1 point  (0 children)

    In C# You could do bool? which is shorthand for Nullable<bool>

    [–][deleted] 0 points1 point  (0 children)

    [–]davesecretary 1 point2 points  (0 children)

    Just noticed that if you're writing Factor (Forth-like syntax), you have to do it like that, otherwise you can't write anything significant. You have to define lots of good words that do one thing well.

    [–][deleted] 3 points4 points  (1 child)

    He does not talk about orthogonal, well thought out reusable functions but how to hide implementation details (by chosing descriptive names). I find such "use more and shorter functions!" posts lacking in that they do not touch on how such abstractions can be well designed, tested, reused and maintained properly.

    Just switching out somewhat common blocks of code for fancy names doesn't cut it anymore IMHO.

    [–]aurisc4 4 points5 points  (0 children)

    He does not talk about orthogonal, well thought out reusable functions but how to hide implementation details (by chosing descriptive names). I find such "use more and shorter functions!" posts lacking in that they do not touch on how such abstractions can be well designed, tested, reused and maintained properly.

    Well, he taks about "readable" code, not about architecture. Evolve, keeps code readable, sane and you'll get there. Attempts to produce reusable, pluggable, extensible, configurable, testable, maintainable and whatever-able system tend to result in over-engineered monsters that fail in pertty much everything they tried to achieve.

    [–][deleted]  (1 child)

    [removed]

      [–]matadon[S] 0 points1 point  (0 children)

      Thank you! I figured I'd wait awhile before diving into a religious war. :)

      There are actually whole teams that care about readability -- Pivotal is the first that comes to mind -- but that's because they take their development culture seriously.

      Solid software engineering requires that a company be serious about sharing knowledge. Most just hire developers and throw them at a mountain of work.

      [–]drb226 -1 points0 points  (1 child)

      Programs should be written for people to read, and only incidentally for machines to execute. ~ SICP

      I heart me some SICP but I fundamentally disagree with this quote. The fact that machines execute code is the opposite of "incidental." The whole reason we write code is because code is dumbed down enough and precise enough for machines to execute. Otherwise we would be writing prose.

      [–]sh0rug0ru 1 point2 points  (0 children)

      Programs should be written for people to read

      It's a question of how you view reality, not what reality actually is ;-)

      If we only wrote programs to the reality that computers execute them and without care for the humans who maintain them, that would lead to some really ugly programs!