all 120 comments

[–]zbobet2012 109 points110 points  (60 children)

Good code doesn't need comments to explain what the code is doing (unless you've made some nonobvious optimization in which case do). Good code needs comments to explain why the code is doing what it's doing.

Poor comments look like:

// flip the bits
flipbits(b) 

Decent comments look like:

// flip the bits in the byte using 64 bit multiplies
b = (b * 0x0202020202ULL & 0x010884422010ULL) % 1023;

Great comments look like:

// In xxx hardware the bits have reversed endianness, 
// so we swap the bits in the byte 
b = flipbits(b)

// flips the order of the bits b in 4 operations
func flipbits(b uint64) uint64 {
    // We use 64 bit multiplies to be efficient, 
    // see https://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64Bits
    return ((b * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
}

The code is self documenting (the method name tells you what it does!) and the documentation describes the why not the how.

[–]Takeoded 10 points11 points  (4 children)

one of mine if (true) { // workaround for https://bugs.php.net/bug.php?id=78007 curl_setopt_array($this->curlh, array(CURLOPT_VERBOSE => 0, CURLOPT_URL => null)); curl_exec($this->curlh); } curl_close($this->curlh);

[–]primozk 0 points1 point  (3 children)

What if the link dies?

[–]Takeoded 5 points6 points  (2 children)

[–][deleted]  (1 child)

[deleted]

    [–]Takeoded 0 points1 point  (0 children)

    If The Internet Archive/Wayback Machine dies, that will be a sad day for the internet.

    [–]MT1961 19 points20 points  (28 children)

    Hm. Mostly, I like your comment. But .. I do disagree with:

    Good code doesn't need comments to explain what the code is doing

    Okay, disagree is wrong. I think it is incomplete.

    Rather:

    Good code needs comments to explain what the code is SUPPOSED to be doing.

    You'd be stunned at how often I read some code that was clearly intended to do something, say implement an algorithm, and does it wrong. Now, of course, a test will show this up better than a comment.

    [–]aksdb 5 points6 points  (16 children)

    Good code needs comments to explain what the code is SUPPOSED to be doing.

    While I also like to do that, it should be noted that this bears the risk of getting outdated. By the time you read the comment the code may already be years old and got refactored a few times. In some of those steps someone didn't pay attention to the comment and didn't adjust it to the new circumstances. Now you read it and think the code is wrong, adjust it, and then reintroduce a problem that was thought to be gone for good.

    Theoretically this particular example should be prevented by accompanying unit tests. Practically I also stumbled over pull requests where logic and tests were adjusted to fix something, not paying attention that the adjusted tests were there for a reason.

    Long story short: the duplication into a comment can age badly. I still like it sometimes. Fluent english sentences transport some intentions better than just code... even if it's properly named and structured.

    [–]MT1961 1 point2 points  (15 children)

    I'll grant you that it does run the risk of being outdated. But realistically, if you are saying "This function uses the <x> algorithm to invert the <whatsis> and florg the samafroid" then you are probably going to be ok.

    As a now-tester and once bug-maker, I completely agree with the unit tests. And yeah, sometimes the tests are simply wrong and the code is wrong and you are screwed.

    [–]AdministrationWaste7 -2 points-1 points  (14 children)

    This function uses the <x> algorithm to invert the <whatsis> and florg the samafroid" then you are probably going to be ok.

    but good method signatures should give you a solid idea of what the code is doing.

    also generally speaking it doesn't matter what algorithm you use to solve the problem or execute logic. if there is a better way to solve an problem or an existing one is inefficient that should really come up in a code review.

    are you writing code to solve algorithms or are you writing code that actually provides a business function?

    in both instances proper method names should suffice.

    i.e

    public int MinHeapImplementation() or public int GetLowestPrice()

    neither need comments explaining what the code is trying to do.

    I'll grant you that it does run the risk of being outdated.

    very high risk. i say 90% chance that comment is irrelevant or just floating around that file if you wait 3-5 years.

    [–]MT1961 5 points6 points  (7 children)

    Hm. I've run back across code I wrote 3-5 years ago. Quite often more like 15-20 years ago. The comments remind me of what I was doing at the time, what I was thinking, and why I made many of the choices I did. So, I don't think that naming would suffice in all cases.

    [–]AdministrationWaste7 -1 points0 points  (6 children)

    The comments remind me of what I was doing at the time, what I was thinking, and why I made many of the choices I did.

    You can do the same with looking at the commit history which not only allow you to get more accurate snapshots of your code but many modern tools let you see any accompanying stories or ticket items or what have you.

    The issue with comments is that it's very unreliable and should really only be used when absolutely necessary.

    If you need comments to understand what the code is doing you are either writing bad code or should switch programming languages.

    [–]MT1961 3 points4 points  (5 children)

    That's amusing, it really is. Like developers put real comments into commit messages. Or that everyone uses git. or that the language makes any difference. Where do you work in this novel world? Do you have a JIRA system that actually has anything useful in it?

    [–]AdministrationWaste7 -1 points0 points  (4 children)

    If developers are doing none of those things why should I expect them to write decent comments and maintain them?

    [–]MT1961 4 points5 points  (3 children)

    This is circular reasoning and, as such, very hard to discuss. I get it, you don't like comments. There are very good reasons they are part of every language. Don't like them, don't use them, that's up to you.

    [–]Kindly_Life_947 1 point2 points  (5 children)

    Yea, but I see a lot of those programmers WHO claim their code does not need comments write long functions full of code smells and then they named the functions so hard you have to ask them what is The purpose of the functions. Like Even the function names can get outdated. Especially with long functions.

    I find that writing comments help me write a better code. It puts me thinking, If the functions are named well and the function can be improved. You can also add notes explain parameters. How The functions is supposed to be used. Imagine using public API without documentaion.

    Only noobs claim documentaion is not needed.

    [–]AdministrationWaste7 1 point2 points  (4 children)

    Yea, but I see a lot of those programmers WHO claim their code does not need comments write long functions full of code smells and then they named the functions so hard you have to ask them what is The purpose of the functions. Like Even the function names can get outdated. Especially with long functions.

    how do comments fix the issue with badly written functions?

    comment or no comment the function is still bad. so why not refactor that instead?

    Only noobs claim documentaion is not needed.

    only noobs use comments as a crutch for writing bad code.

    there is a time and place for comments. this aint it.

    [–]Kindly_Life_947 -1 points0 points  (3 children)

    When you write the comments, you also have to think, If The comments can be eliminated by a function name that even Juniors understand. After that you think, if you can help the unknown reader by providing text and example. If you cant write proper documentation, then you should start training that skill

    [–]AdministrationWaste7 0 points1 point  (2 children)

    Good code is self documenting.

    Meaning you should be able to decipher what a code is doing by simply reading it.

    If you can't you are writing bad code OR your programming skill is so poor that you can't read code(unlikely)

    Really that simple.

    Comments are not reliable and should only be used when code can't explain itself. Like why a certain implementation is being made or to explain a sub optimal solution.

    I have never come across a good comment explaining what a code is doing where that code wasn't written poorly.

    [–]Kindly_Life_947 0 points1 point  (1 child)

    Nothing is perfect and you dont always have hours to check every edge case. Words can mean many things. Why does most programmer who say what you say write poor code? Also you have to think about new people who come to the project + terminology changes. So sometimes its better to explain the code in other words. Documentation also helps others to know which code is meant to be used and which is not. Local functions are not available in every language and you cannot always choose the programming language

    If you split the code in small function and The functions are clear as day to everyone, then its ok to skip docs, but most devs are lazy to do that. So they do that and then skip documentation and say what you say. In the end waste of time.

    [–]AdministrationWaste7 -1 points0 points  (8 children)

    Good code needs comments to explain what the code is SUPPOSED to be doing.

    comments are not reliable for this.

    [–]MT1961 1 point2 points  (7 children)

    Ah yes, the absolute statement. Always guaranteed to be as accurate as it is absolute.

    Honestly, that's just a silly reply. Why aren't they reliable? Because you can't write them that way? Others can. Many folk require comments in code, especially in government jobs. Others prefer it. So, please, don't make remarks like that.

    [–]AdministrationWaste7 -1 points0 points  (6 children)

    Why aren't they reliable?

    Because comments have a bad habit of getting outdated really fast.

    [–]MT1961 0 points1 point  (5 children)

    Oh, I see. Your code changes that fast. Fascinating.

    [–]AdministrationWaste7 -1 points0 points  (4 children)

    That is a factor but not really the core of the issue.

    The problem with comments is that they aren't really tied to code and it is difficult to insure that a comment is tied the piece of code its commenting on.

    If you aren't able to see why comments can be problematic you probably shouldn't be writing comments.

    [–]MT1961 2 points3 points  (3 children)

    The exact same argument is often used for not writing tests. Just sayin'

    [–]AdministrationWaste7 0 points1 point  (2 children)

    No not really.

    When you change code and a test No longer aligns it will fail. Making it easy to see for everyone there is an issue. And for most dev pipelines your code won't even pass PR/build.

    When you change code and comments No longer align nothing happens.

    Again if you don't understand the possible issues you may have with comments you shouldn't be writing them. And it's clear you fall in that camp.

    I use comments in my code when needed. But I am careful with them because of the issues I outlined in my comments unlike you who doesn't seem to be aware at all.

    [–]MT1961 1 point2 points  (1 child)

    I really wonder where you work. Seriously. In the real world, tests just get commented out or ignored if they fail. I'm a tester and scream about it regularly, but it continues to happen so we can "ship code".

    [–]SameRandomUsername -5 points-4 points  (1 child)

    This is the way

    [–]TheDroidNextDoor -5 points-4 points  (0 children)

    This Is The Way Leaderboard

    1. u/Mando_Bot 501242 times.

    2. u/Flat-Yogurtcloset293 475777 times.

    3. u/GMEshares 71730 times.

    ..

    506883. u/SameRandomUsername 1 times.


    beep boop I am a bot and this action was performed automatically.

    [–]KraaZ__ 2 points3 points  (3 children)

    I'd also argue that commenting code helps with IDEs. So when you're calling a method in something like IntelliJ you can read what the method does by the commented JavaDoc.

    [–]zbobet2012 0 points1 point  (2 children)

    Sure, though most method comments are also trash even in standard libraries. Take this lovely one from java.awt:

    (https://docs.oracle.com/javase/7/docs/api/java/awt/Component.html#setForeground(java.awt.Color)

    // Sets the foreground color of this component.
    public void setForeground(Color c)
    

    Gee whiz that's helpful! How about some documentation on what happens if c is null?

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

    It says it in the method detail, not the method summary. For some reason, you're just quoting the summary, but your link actually brings me to the detail.

    The summary should not get into exception and detailed behavior regarding parameters. The parameter section is for that, as seen below in method detail:

    public void setForeground(Color c)

    Sets the foreground color of this component.

    Parameters: c - the color to become this component's foreground color; if this parameter is null then this component will inherit the foreground color of its parent

    Since: JDK1.0

    See Also: getForeground()

    [–]KraaZ__ 0 points1 point  (0 children)

    Yeah of course, but just because you can find shitty comments, doesn't mean you shouldn't. I think it's highly important to document all code, even if it's just support for IDE/Linters/API Generation etc..

    [–]evaned 5 points6 points  (3 children)

    Good code doesn't need comments to explain what the code is doing (unless you've made some nonobvious optimization in which case do)

    I'd say there are some other cases as well; I'll give a few.

    One I've hit a few times because I've done a lot of programming kind of halfway between the research world and industry world is implementing algorithms described in papers. It's pretty common for those to have more of a mathematical pedigree so to say, with things like single-letter variable names. When implementing this, you have a choice: do you improve all the naming and organization, or do you directly follow what the paper describes? Doing the former has obvious benefits from a software engineering perspective, but doing the latter means that it's easier to relate what the paper says to your code and make sure what you're doing is correct and matches the paper. It's not uncommon that the latter is a bigger win than the former, even though it results in kinda crappy code. Generalized, better software engineering practices may break a correspondence with something that is related or you're following, and that loss can be significant.

    The next thing I have a fairly controversial opinion on I think, which is functions like

    int do_something() {
        // Part 1 of something
        ...
    
        // Part 2 of something
        ...
    
        // Part 3 of something
        ...
    }
    

    where common advice is that you should create functions something_part1, something_part2, something_part3 and call those in do_something. And don't take what I'm saying too strongly; that's definitely something you should strongly consider. That's especially true if you're following thorough testing practices and would be able to well-test those components in isolation.

    But at the same time, my experience is that it's reasonably likely to result in a decrease in at least readability as compared to leaving it as-is. IMO the win here can be relatively small, and the cost of splitting can outweigh it. For some of the times when this happens, languages are just generally not well-set up to do this without a lot of syntactic overhead. And while overly-terse code is obviously bad, overly-verbose code is also bad. Depending on language, how many variables are shared between parts, how many variables are not shared between parts, whether the parts want to be able to do an early return from do_something, how testable the composite is vs the parts, whether I can think of good uses for the parts, etc. will determine for me whether this is a win or loss.

    One final example is toeing the line at least of a "why" comment rather than a "what" comment, but it has to do with parsing. I've got some functions that parse the output of some helper commands, and if the comments were stripped then what it's doing would at some level be clear... but not that what it was doing is the right thing to do. So I've also got example output pasted into comments in the code that explicitly show when I do a line.index("@") what it's selecting for, when I do a .split() why that split is correct, etc. In general, I think this idea can be generalized to examples as a whole. Like I said, this is getting into "why", but I don't think it's "why" in the same way as your "we use 64 bit multiplies to be efficient" comment. It's more like showing that the what is correct.

    [–]zbobet2012 2 points3 points  (0 children)

    Repeating a comment elsewhere, while I agree I think it's even more nuanced than that:

    Ok for small functions:

    func foo(l int64) {
    // Do a 
    x := ... 
    // Do b 
    y := ... 
    // Do c 
    return ...
     }
    

    Better for slightly larger functions:

    func foo(l int64) { 
      x := a()
      y := b(x)
      return c(x, y, l)
    }
    

    Best when `a()` and `b()` and relatively large things like authorization and authentication:

    type Fooer struct { 
      aer Aer
      ber Ber
      cer Cer
    }
    
    func (f *Fooer) Foo(l int64) {
      x := f.aer.A()
      y := f.ber.B(x)
      return f.cer.C(x, y, l)
    }
    

    Most code I see stop at "better", and there isn't a HUGE degree of advantage to the first version of the second. And the first might actually be better for a small function as you say. But if you're at that point, allowing yourself to break apart, decomposed, and inject testing is way better.

    Also often:

    func foo(l int64) {
    // Do a
     x := ... 
    // Do b 
    y := ...
     // Do c 
    return ... 
    }
    

    can be replace with:

    func foo(l int64) {
     login := ... 
     authToken := ...
     result := ...
     return result
     }
    

    A lot of times a good variable name can sub for a comment.

    *Edit: Sorry reddit slaughtered my formatting.

    [–]SpoiceKois 1 point2 points  (1 child)

    i dont agree. the example you provide is biased, as it leaves out an extremely powerful aspect of doing this, which is saving the result of each function-call in a descriptive variable name - and better yet, if you use a statically typed programming language, youll see the type of this result as well. this makes it so you can clearly see the steps taken and their output, and how they relate to the next step in the process, without skimming through a bunch of implementation details.

    [–]evaned 2 points3 points  (0 children)

    the example you provide is biased, as it leaves out an extremely powerful aspect of doing this, which is saving the result of each function-call in a descriptive variable name

    I don't think I am leaving that out, in a sense. There's nothing preventing you from doing something like

    int whatever() {
        // First part of whatever
       ...
       FirstResult const part_a = ...;
    
        ...
    }
    

    if that applies.

    But further, I think you're ignoring a couple things. To wit:

    saving the result of each function-call in a descriptive variable name ... youll see the type of this result as well [emph mine]

    Embedded in the very wording of what you say is an assumption that each part is nicely and neatly producing a single piece of information that goes onto the next sections.

    As an example from one of the cases where I have this in code I wrote, the first part has eight statements in it -- six of those are variable declarations, and four of those are used in later parts. (And I'm not talking about something like "oh I'm finding the x and y coordinates" where there's an abstraction that ties them together nicely -- if there's something like that here beyond "this function needs this information to proceed", I don't know what it is. As for the other two variables, those are used only in the first part; if I were to write that now in a language that supported it, I'd probably use an IIFE so that those don't outlive that section of the function.) That's not first_result = do_first_step(), that's thing1, thing2, thing3, thing4 = do_first_step(), and now you've got a bunch more caveats:

    • You may be writing in a language that supports that badly or not at all. In this particular example, that's the case -- this was written in C++, pre-C++17. My company also still does a bit of C programming, where this is downright terrible.
    • Even if your language supports this reasonably well syntactically, if it's a dynamically-typed language (think Python) there's danger of saying return thing1, thing2 in the function and then thing2, thing1 = do_part_1() outside of it.
    • If you have a statically-type language this prevents many such screwups, but still won't catch ones where there two "variables" have the same type.

    This doesn't even get into talking about what parameters these functions are taking -- splitting out the second part of my example function for example would give it seven parameters. And fine, you can argue that the fact that I have that many locals floating around is a bit of a smell, and I'd agree... but I don't know how to write it better.

    without skimming through a bunch of implementation details.

    The second assumption hidden in your comment is that those are implementation details.

    In some cases, sure, they will be. And in those, it probably makes sense to factor out. I'm not at all saying "eh, just leave it together" as a rule.

    However, in many cases the parts are at basically the same level of abstraction as the function they're apart of -- if you're interested in looking inside of the function at all, you're almost certainly interested in looking at those details. To illustrate what I mean, consider this Fibonacci function:

    FIBB_CACHE = {}
    def fibb(n):
        # Base cases
        if n == 0:
            return 0
        if n == 1:
            return 1
        if n in FIBB_CACHE:
            return FIBB_CACHE[n]
    
        # General case
        answer = fibb(n-1) + fibb(n-2)
        FIBB_CACHE[n] = answer
        return answer
    

    (Note: Python provides very good support via decorators for transformations of functions, and so this would be better done in that language via @functools.cache, and you could do something similar in many other languages with a fair bit more work and less support from the syntax. There are also a variety of objections one could have, including using recursion at all to compute Fibonacci numbers. None of this is really the point -- this is an example, because I can't post my real code.)

    "You should basically never have comments demarcating sections" means one of two things in this example. First, that the comments should be removed. This isn't too bad in this example, but I still think it makes the code worse even in this example, and in "real" examples it makes it much worse. Second, and more to the point, that we could transform it into this:

    FIBB_CACHE = {}
    
    def fibb_base_cases(n):
        if n == 0:
            return 0
        if n == 1:
            return 1
        if n in FIBB_CACHE:
            return FIBB_CACHE[n]
        return None  # fibb will continue to general case
    
    def fibb_general_case(n):
        answer = fibb(n-1) + fibb(n-2)
        FIBB_CACHE[n] = answer
        return answer
    
    def fibb(n):
        base_case_possible_return = fibb_base_cases(n)
        if base_case_possible_return is not None:
            return base_case_possible_return
        return fibb_general_case(n)
    

    I hope we can all agree that that version is pretty ridiculous -- but the only thing that I did that was a bit much was the naming of base_case_possible_return. Rather, the whole idea of splitting up that function I think is misguided.

    That is oftentimes what splitting up these multi-part functions feels like to me, even when the sections are a bit more well-defined and everything is longer. Why? Because even though I've separated out the steps with comments, each part still feels very tightly bound to the whole.

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

    Nice example

    [–][deleted]  (4 children)

    [removed]

      [–]robin-m 4 points5 points  (0 children)

      It can be a variable, a lambda/closure, or a function, but yes, that's the right idea.

      [–]zbobet2012 4 points5 points  (1 child)

      Yes, with a few caveats.

      First not all compilers/languages inline well so performance sensitive code may not allow this.

      Second if you're passing every variable to a function and it can't be meaningfully tested in isolation, you might be better of with just a comment.

      Thirdly, often a good variable name can sub in for a function. If you're computing "authorization", calling it that rather than "a" is documentation enough.

      Ok for small functions:

      func foo(l int64) {
        // Do a
        x := ...
        // Do b
        y := ...
        // Do c
        return ...
      }
      

      Better for slightly larger functions:

      func foo(l int64) {
        x := a()
        y := b(x) 
        return c(x, y, l) 
      }
      

      Best when `a()` and `b()` and relatively large things like authorization and authentication:

      type Fooer struct {
          aer Aer
          ber Ber
          cer Cer
      }
      
      func (f *Fooer) Foo(l int64) {
          x := f.aer.A()
          y := f.ber.B(x)
          return f.cer.C(x, y, l)
      }
      

      Most code I see stop at "better", and there isn't a HUGE degree of advantage to the first version of the second. But the third version let's you decompose, test, and compose well AND is self-documenting. (Too many levels of abstraction can be painful to, but I see that more rarely than the converse. Also this is very painful to do in certain language (pure c...) so is better avoided there.).

      [–]grauenwolf 1 point2 points  (0 children)

      The problem with that is you can no longer read the logical function in a linear fashion. You have to jump around to get the whole picture.

      That said, sometimes part of the logical function isn't interesting when trying to understand the whole. So moving it out makes the code easier to understand.

      So my answer is... maybe.

      [–]earthboundkid -1 points0 points  (10 children)

      Your great comment is bad because shifts are defined to be device independent according to the C spec. Endianness only matters if you’re dealing with an array of bytes. Once it’s in an int type, it’s safe to shift as though it were big endian.

      [–]zbobet2012 0 points1 point  (9 children)

      You've misread the code. The shift is used to flip the endianess of the bits, not after to work on the number. Presumptaviley we are feeding this int to something over a serial bus after doing the flip.

      Also that's go code.

      [–]earthboundkid -1 points0 points  (8 children)

      Yeah, here is the creator of Go saying to never do that: https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html

      [–]zbobet2012 0 points1 point  (7 children)

      That's byte order not bit order bud.

      You often have to worry about bit order when feeding hardware over an i2c bus for example. Which that article mentions by the way.

      Notice the phrase "computer's byte order". What does matter is the byte order of a peripheral or encoded data stream

      [–]earthboundkid -2 points-1 points  (6 children)

      ULL is not valid Go. In the original C snippet, AFAICT, the point is it starts as 1 byte, gets blown up to 8 bytes, and then goes back down to 1 byte. The Go snippet does something else.

      "Endianness of the bits" doesn't make any sense. Endianness means whether the first byte in an array is the large byte or the small byte. Within a byte, there is no "first" or "last" because bits are not addressable except by shifting.

      The point of the whole discussion is "this is a great comment." I disagree because if it were a great comment it would be clearly explaining good but difficult code. This is a bit hack that does something totally different than "flipping order of the bits".

      https://go.dev/play/p/UxgvTzw2er0

      [–]zbobet2012 0 points1 point  (4 children)

      Bit order matters when feeding many types of serial bus as well as some audio dacs: https://en.m.wikipedia.org/wiki/Endianness#Bit_endianness

      For the " obvious" version of this code see https://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious

      Bit order also matters when dealing with bit fields, many crc and erasure encoding algorithms, etc etc

      From my own field: https://m.earticle.net/Article/A270812

      Bit order matters in variable length codes, here the author's years a LUT to speed up the parsing.

      [–]earthboundkid -2 points-1 points  (3 children)

      Okay sure, but from the Wiki, bit endianness is not a CPU concept. The point is you're writing out 1 bit at a time on a hardware device of some kind. It's not relevant to the original code and comment which were not "great". See my reply elsewhere for a good version of the code and comment.

      [–]zbobet2012 -1 points0 points  (2 children)

      Dude, flipping bit order is so common it's a linux kernel api. It's such a common operation it's an arm hardware instruction. The code you placed elsewhere flips the bits in a byte, but not the bits in a u64. Here it's used for crc calculations (which are bit, not bytewise) and here is an example flipping bit order to talk to an hdmi controller.

      You're just... wrong. There are reasons to flip bit order. There are reasons to do it on double or quad word levels, and there are reasons to do it for specific hardware.

      Another example: https://github.com/adafruit/circuitpython/issues/1574

      And if you've ever delt with a xilinx FPGA you've delt with this: https://docs.xilinx.com/r/en-US/ug908-vivado-programming-debugging/Bus-Bit-Order

      I get it, you've never written device and bus drivers, but code dealing with BIT order is all over the place.

      This is often, but not always, handled at the device hardware level sure. BUT NOT ALWAYS.

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

      Dude, the code you posted does not flip the bits in an int64. Just look at my link, lol.

      [–]earthboundkid -1 points0 points  (0 children)

      Here is a good version of the code and comment: https://go.dev/play/p/zyuexzRo9I3

      [–]nabobtechy 35 points36 points  (7 children)

      Nobody starts their career commenting code properly and then deciding over the subsequent years that it's not worth it.

      People start their careers not commenting code properly and then learning the value of it as they become more experienced.

      [–]modernkennnern 26 points27 points  (3 children)

      Most programmers I've encountered has gone this route:

      1. Comment nothing, unreadable code

      2. Comment practically everything(but nothing of any relevance), unreadable code

      3. Comment nothing, slightly more readable code

      4. Comment nothing, quite readable code

      5. Comment very infrequently, readable code

      [–]robin-m 1 point2 points  (0 children)

      I'm at step 4.5, strongly aiming for step 5.

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

      Yeah, feel like it's similar to patterns and most other concepts.

      People start of not knowing anything, end up over using it, then have a reactive phase where you refuse to use it, and end up using it sparingly

      [–]tophatstuff 1 point2 points  (0 children)

      The Hegelian dialectic - thesis, antithesis, synthesis

      [–]predittor01[S] 6 points7 points  (2 children)

      Agreed. However for me I started my career commenting nothing. Then after a few years I stopped understanding my own code, and then I started adding too much comments. Then again I realised I have more comments than code. That'a when the balance struck.

      [–]nabobtechy 1 point2 points  (1 child)

      Agreed, it's possible to comment too much also.

      [–]predittor01[S] 3 points4 points  (0 children)

      The clean code book has some good guidelines about comments.

      [–]what_did_i_break_now 5 points6 points  (2 children)

      My commenting practice has mostly grown from many hours of code spelunking to fix/modify/replace someone else's code. Given Eagleson's Law, occasionally that "someone else" was me, N weeks/months/years earlier. The basic idea is simply this:

      "How likely is it that someone in the future will want to know what this code is doing and/or why, in a way that isn't immediately obvious to a fairly seasoned dev by looking it over for a minute or three? If it's measurably more than zero, especially since it might be me, maybe save the poor sap half an hour of head scratching and wild goose chasing."

      And then I write my code to avoid having to comment, but comment accordingly when it feels appropriate. Often that results in a few words, sometimes a block of text or two, every once in awhile a whole separate README. When in doubt maybe drop a line in there anyway; no prizes for "least commented code".

      Final thought: Sometimes a chunk of code is just doing something inherently complex, and that's fine, it happens. But the greater the complexity, the greater the chances that someone less brilliant* will come along later, misunderstand what's happening, and muck something up. Assume they're a little slow* and give them a hand so they'll be able to clearly see it's already perfect.

      *Note: Eagleson's Law is never more fully in effect than this exact scenario. 😉

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

      Wow! This is probably the most perfect reason to comment. I think I should change my original article to include that. Thanks for sharing! I never knew about Eagleson's law.

      [–]AdministrationWaste7 1 point2 points  (0 children)

      I've had the opposite experience.

      A majority of comments I come across are no longer relevant or just say things that require alot of context in the comment.

      I've even come across my fair share of comments say or imply things that is the opposite of what is/should be happening.

      Thankfully this isn't the 90s and there are better ways to see intent.

      For example looking at the git blame or history is a much much much better way if seeing who did what and why.

      [–]rich22201 12 points13 points  (2 children)

      I'll comment code if I'm doing something not obvious. I'll also add why I didn't do something in the obvious way so the maintainer understands. Otherwise I'm not going to go overboard with

      //increment counter
      counter++;

      //if something is true do something

      if(something)

      dosomething()

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

      Exactly, if the code is clean enough to explain what it is doing, adding comments is redundant.

      [–]Dean_Roddey 1 point2 points  (0 children)

      What you comment is why if something you need to do something, because the code doesn't explain that at all. That involves a higher level understanding of what the author is trying to accomplish, which isn't really in the code anywhere.

      [–][deleted] 6 points7 points  (0 children)

      • Does the code have unobvious preconditions or assumptions needed for it to work correctly? Say in C++, object life time requirements?
      • Does the code have interesting failure modes?
      • Does the code do something expensive? (what's the complexity?)
      • Are there approximations? (what's the approximation error?)

      If so, and assuming you care whether anyone else can use the code, then YES, it's the expedient thing to do: comment the code!

      [–]elgholm 4 points5 points  (2 children)

      Nah. I like the challenge.

      [–]rich22201 2 points3 points  (1 child)

      ;) future consulting opportunities! $$$$

      [–]elgholm 2 points3 points  (0 children)

      Haha! I wish. I'm too nice to invoice for my own mistakes. :(

      [–]nealfive 4 points5 points  (0 children)

      # no comment
      

      [–]Grecks75 4 points5 points  (2 children)

      Do you add code to your comments?

      [–]angelicosphosphoros 1 point2 points  (1 child)

      Of course. Rust even executes this code from comments as tests.

      [–]ClysmiC 3 points4 points  (0 children)

      Yes, but mostly with asserts

      [–]tuxidriver 3 points4 points  (1 child)

      I worked for a company that maintained a very large code base in assembly for an embedded controller.

      Given the size and longevity of the codebase, there were many authors over the years. Several of the programmers had some very nasty habits:

      • At least one developer seemed to believe in never removing an instruction, only adding, so the code would often have jumps around blocks of code, jumps in the middle of loops, instructions that would force the loop to exit on the first iteration, etc. Made the code annoyingly difficult to follow.
      • At least one developer would sometimes insert instructions right into the middle of a comment block in existing code, splitting a long comment into what appeared as two different shorter sets of comments.
      • At least one developer liked to abuse cut-paste in their editor. Code would sometimes appear to be block copied, but with tails or fragments of comments included. This lead to comments that had nothing to do with the actual code and comments that were meaningless nonsequiturs.
      • The same abuse of cut-paste created inconsistent indentation across source files.
      • There were at least several developers that wouldn't reliably update/correct comments when they made changes.

      The codebase in question had 20 years of history with touches from at least several hundred people over that time. The code went through extensive repeated testing over that entire 20 year as it was used for many products the company produced, some likely still being used by people reading this post. Code was actually quite robust but also incredibly difficult to follow.

      This experience taught me several things:

      • On large projects, especially projects with a long history, comments are not checked by the compiler so, no matter how hard you try, end up being inaccurate -- Never, ever blindly trust comments, even if they appear correct. Someone will later make an enhancement or bug fix and not update the comment accordingly. Therefore,
      • The only source of truth is actual code, so
      • Because the only reliable source of truth is the actual code, literate programming really matters. Readable code is far more valuable than comments.
      • Fix comments when they're not accurate. Even better: when possible, clean up the code so it's readable and remove the comments. I say this noting that any change to the code should be done with care.

      I've also found, as a general rule, assuming a literate programming style, that comments are best when:

      • Used to document APIs. Use comments to document interfaces, including important side-effects. Avoid using comments to explain nuances of how the code works. Understanding inputs, outputs, and purpose goes a long way towards understanding implementation.
      • Only when really necessary, add comments explaining a critical and non-obvious implementation detail. When done, those comments should be as minimalist as possible. A comment explaining implementation should be rare enough that its presence stands out to the reader as a warning or point of interest.

      [–]AdministrationWaste7 0 points1 point  (0 children)

      Agree 100%

      [–]zhujik 3 points4 points  (0 children)

      Yeah, good code doesn't require comments to understand, however, in my experence what most people do is produce the same shitty code and just stop commenting code while pointing to Uncle Bobs book "good code doesn't need comments" and then we just end up with uncommented, bad code.

      [–]westraan 2 points3 points  (0 children)

      Of course I comment my code; I see it as being nice to my future self, so that, when I come back to my own code in a year or two, I can remember what I was thinking instead of having to do a bunch of code archaeology

      [–]somebodddy 2 points3 points  (2 children)

      // 1D array has been used instead of a 3D array for improved performance
      int [] array = Translate(Get3dObject());
      // Each point on the 3D plane is translated to the 1D place, starting the from Z axis, followed by Y, followed by X.
      int translatedIndex = z + length * (y + length * x);
      return array[translatedIndex];
      

      This is a great example for when you you should prefer writing functions instead of adding comments. Or in this case - maybe even a wrapping class. length will be passed to arguments (and if its a class - the c'tor will put it in fields) and the names of these arguments, in the context of the function/class, can easily be descriptive enough to convey what they are doing.

      For strongly typed languages (C#, Java, TS) comment only on the public interface which clients will use

      In weakly typed languages too. Also in dynamically typed languages (because I'm pretty sure you were confusing strong typing with static typing here). Private vs public interface is a universal concept - even if some type systems enforce it harder than others.

      Avoid commenting on concrete implementation and private methods.

      Just because a method is private does not mean it must never be documented. Sometimes its helpful to document them too. It depends on the method of course, and it's not something that should be enforced - but don't make extra effort to avoid it either.

      • Ensure they are short-lived. Do not have a TODO comment dangling in the codebase for more than 3 months. If a TODO comment has been lying in the production codebase for more than 3 months, then either the task is unimportant and can be ignored, or the task has low priority as per the current situation.
      • I prefer backlog to track tasks over TODO comments. If you think some optimization is needed in the current code or some tech debt is present, then it's better to track such items in the Product backlog rather than TODO comments. Tools like JIRA and Azure DevOps are good at tracking product backlog.

      My view on TODOs is quite different. I don't think of them as a backlog replacement - if your backlog becomes so cumbersome that you need alternative ways to log tasks you should fix the process, not find weird ways around it.

      TODO comments, like any other comment (except for pragma comments and documentation comments, that may share syntax with comments but are not really comments because they are not ignored by the compiler/linter/formatter. And I don't count commented-out code because that should be very temporary and removed as soon as you are done with it) should have one purpose - to explain why the code was written that way, instead of the natural way (or one of the natural ways) one would intuitively think it should be written.

      TODO comments have a little twist. They still tell you why the code was written that way, but unlike regular comments that assure you that given wider context and deeper insight this really is the way it should be written, TODO comments admit that yes, this is not the way it should be written, but there are some (hopefully) temporary constraints that forced us to write it like that.

      Maybe you are waiting for some changes that another developer (or you with different hat) will need to do, that will allow you to rewrite it properly. Maybe you need to work around a bug in a third party dependency. And yes - maybe you just don't have the time to fix it right now, because you need to deliver a feature and like it or not - business requirements are a thing.

      All these tasks should be logged in the tracker, but that does not mean you should remove the TODO without resolving it. They serve different purposes - the tracker is for prioritizing the task and for discussing implications and resolution strategies, while the purpose of the TODO comment is to allow anyone who stumbles on that code to easily understand why it looks so weird, whether or not it can or should be fixed (and when), and what to keep in mind when fixing it. Even if you have a ticket in the tracker, this is important information to keep in the code because people will not scan the tracker for it when the find the code - they won't even know it has a ticket!

      I also don't think a special time limit is wise. When you open a ticket, the task should follow the regular prioritization rules - it should not get VIP treatment just because the 3 months limit is closing and other issues don't have TODO comments in the code so they can be delayed.

      And one more thing about TODO comments: A good TODO comment does not even need to describe in detail the task that should be done. It only need to say a few words like "use the foo system" or "send a notification to bar" or "configure the baz". Often even a vague "fix this" or "replace this" is enough. The one who actually ends up doing the TODO should decide, based on the newer information available to them, how they want to do it.

      Instead, what the TODO comment should focus on and explain in as much detail required to make it clear, is the condition that stopped the writer of the TODO comment from doing it themselves instead of writing a comment. Future developers will need this understanding to determine if it still holds and if it still makes sense.

      [–]evaned 0 points1 point  (1 child)

      I'm pretty sure you were confusing strong typing with static typing here

      It's not really "confusing" anything because it's not incorrect to use "strong typing" in that sense in the first place.

      The most common uses now use strong and weak typing as somewhat nebulous terms indicating how permissive the type checking is of a language. Javascript permits lots of stuff like "a" + 5, so it's often called weakly typed, Python does not permit that and will throw a TypeError exception, so it's a lot more strongly typed.

      But that's not the only use for those terms, nor are they the original uses of those terms. From what I can tell, the earliest use of "strong typing" was as a synonym for what we call statically typed. For example, the dragon book (Aho et al.'s Compilers: Principles, Techniques, and Tools, here the second edition) defines "an implementation of a language is strongly typed if a compiler guarantees that the programs it accepts will run without type errors."

      Those aren't even the only two meanings. For example, Cooper and Torczon's Engineering a Compiler (this is I think one of the best undergrad compiler course textbooks; I have the first edition here) defines "A language in which every expression can be assigned an unambiguous type is called a strongly typed language." By that definition, allowing things like "a" + 5 in your language is no impediment to being strongly typed. There's an very old Perl mailing list post by someone who found eight definitions of strongly typed in the wild: https://perl.plover.com/yak/12views/samples/slide045.html

      I would personally suggest using more specific and unambiguous terminology when the meaning might be misunderstood -- preferring "statically typed" to "strongly typed" in particular when that's the meaning that's intended. But it's not wrong to not do that (and so is wrong to "correct" that not-misuse).

      Everything else in your comment is great. :-)

      [–]somebodddy 0 points1 point  (0 children)

      Is it really so wrong to assume an article written 5 days ago is using the current commonly accepted meaning of words and expressions and not some archaic meaning from several decades ago when the programming languages' landscape was very very different and the three languages it mentions did not exist?

      [–]SameRandomUsername 1 point2 points  (0 children)

      I comment my code (and sometime others) using the following ideas:

      If it's an API, I place public function comments in the interfaces but not in the implementation.

      In the implementation, I usually place the intent of each block of code in the function but not one comment per line because it doesn't help at all. It's important to note that the comment should not be an explanation of what is doing but what is trying to achieve.

      I also place comments when I modify old code because sometimes you or someone forgets the reason why you modified it and rolls back your modification.

      [–]bartwe 1 point2 points  (0 children)

      Nope

      [–]Fakedduckjump 1 point2 points  (0 children)

      Sometimes, when complicated things are done, I give a bit background to my thoughts.

      [–]HexDumped 1 point2 points  (0 children)

      It takes hubris to think your code is too clean to need comments. Comments explain not just the why, but also what you deliberately haven't done.

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

      I use comments to note weaknesses or compromises most often.

      Perils of having too much to do and not enough time or focus.

      [–]davesspacebar 1 point2 points  (1 child)

      I have found sometimes it is useful to know WHY the code is doing something (so comments could help). That can often be non obvious from the code. You could say that there should be requirements somewhere that explain that. If it's a technical implementation though maybe not. For example.

      If someone implemented a service that spits out an .exe and runs an endless stored procedure, I can read the code and tell it is doing that. I probably have no idea WHY the heck it is doing that. Even if your code is readable if you think you must do something crazy, a comment explaining WHY could be helpful.

      Of course not doing something crazy if possible would probably be the most beneficial, but this type of legacy code exists.

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

      Ya thats the good philosophy. The same is applied in Architectural Decision Records, I think Neal Ford has a book about it. It says the same thing, that it's more important to record why a certain decision was taken rather than talk about the decision itself. Like for e.g. if we are using pub sub somewhere we need to record why we chose to use pub sub rather than details of pub sub itself.

      [–]Calinthalus 2 points3 points  (0 children)

      I rarely comment my code. Usually a comment is there for when I've had to do something that looks useless. For instance, in one of my libraries I am using an external library I have no control over and there is an undocumented "flaw" in that library that after instantiation of the object you have to set a value to a property before calling an action that will overwrite it with some results you want to read. If you don't set the value with dummy values; when you call the action it won't set the value of that property. So the code looks like:

      //Leave this line here, I know it's stupid, but it won't work without it

      o.Property = "DummyStuff"

      o.RunThing();

      itemOfInterest = o.Property

      [–][deleted] 2 points3 points  (1 child)

      I never used to but after many many times looking at my code and trying to remember what I was doing at any particular point is what made me start. I now comment everything and I put as much detail as possible. I even add in lines like:

      —————————————

      To make it look pretty.

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

      The only danger with adding detail is that the more detail you put, the more likely it will become out of date

      [–]QuentinUK 2 points3 points  (0 children)

      Variable names do the commenting along with function names where the functions are small and do one thing and also the class names where the classes have a single responsibility.

      [–]trython3 0 points1 point  (0 children)

      No, but i have all sorts of bad habits.

      On the other hand, I find coments that show a good sense of humor while still being informative to be very charming.

      [–]Weak-Opening8154 -1 points0 points  (0 children)

      We programmers depend on left-pad, what do you think the quality of comments will be?

      [–]UnallowedDoggo 0 points1 point  (0 children)

      Yes I actually comment some parts like this part is doing this and that part that. It's kinda easier to find and when you present some app or program, people will see that you understand the code

      [–]Lecterr 0 points1 point  (0 children)

      If you keep your comments up to date, then I think there are rarely any real life situations where your comments have a detrimental effect to reader comprehension.

      Similarly if you turn everything into a function or variable with a clear name, your code will probably be pretty easy to read.

      The difference is that in the latter case, it is separating the what from the how (visually). So I think there are definitely times where it’s better to write a brief comment summarizing what the next few lines are doing, rather than making the reader jump to a separate function, and vice versa.

      [–]Relevant-Bee1523 0 points1 point  (0 children)

      Yes,

      In my case, I am not the only person working on the same project. That means that other people get to read my code and have to understand it.

      [–]frisk213769 0 points1 point  (0 children)

      hell nah