Need help?
<- Back

Comments (166)

  • jackfranklyn
    The "what" vs "why" distinction breaks down when your code encodes domain knowledge that readers can't infer from context.I build accounting software and half my "what" comments are actually explaining business rules that would be impenetrable otherwise. Something like: // Bank transfers appear as two transactions - a debit here and credit elsewhere // We match them by looking for equal-opposite amounts within a 3-day window That's explaining "what" but also implicitly "why" - because that's how double-entry works and that's the tolerance banks allow for settlement delays. You can't really extract that into a method name without it becoming absurd.The Uncle Bob approach of extractNameMatchingTransfersWithinSettlementWindow() doesn't actually help - now I need to know what settlement windows are anyway, and I've lost the context of why 3 days.
  • awesan
    I feel like no one serious uses the uncle Bob style of programming anymore (where each line is extracted into its own method). This was a thing for a while but anyone who's tried to fix bugs in a codebase like that knows exactly what this article is talking about. It's a constant frustration of pressing the "go to definition" key over and over, and going back and forth between separate pieces that run in sequence.I don't know how that book ever got as big as it did, all you have to do is try it to know that it's very annoying and does not help readability at all.
  • scoofy
    I feel like a complete weirdo when it comes to comments and variable names. I've never worked professionally as a coder, but I've been working with python and a bit of js for like 15 years now. I strongly believe that variable names should be long, and explain what they are, and that comments should be long, and explain what's happening.I have no idea why people want to "save time" to write short comments and short variable names. I just CTRL+C, CTRL+V my variable name anyway. They compound on each other, and that ends up adding an unnecessary level of complexity, and the possibility for errors.When I come back to a piece of complex code after a year or two, I'm very, very happy that I've used a variable name like "three_tuple_of_weight_radius_price" instead of "tuple" or "t".
  • programmertote
    One thing I learned from programming since the early 2000s, there is no such thing as one size fits all advice. You do what is best for future folks--as I like to call the unfortunate folks who would have to maintain the code I wrote--by providing them helpful hints (be it business rules, assumptions related to code/tech) along with as simply and clearly written code as possible (how do I know if my code is simple and easy to understand? Have a junior teammate review my code and have her/him leave comments wherever she has to spend more than 10-15 mins reading an area in the code).I hope not of a lot of the future folks hate me for leaving them with ample context and clear/dead simple code.
  • eterm
    I wouldn't take the examples from Bob Martin as gospel, see also: "Don't refactor like Uncle Bob": https://theaxolot.wordpress.com/2024/05/08/dont-refactor-lik...
  • filoeleven
    The bigger point I take from this is that the purpose of comments and good names are all attempts to help the developer grasp "the context of this code". The article uses "context switch" repeatedly, and in fact never uses the word "context" any other way. Since the author acknowledged they're starting a friendly flame war, I'll go ahead and add that the biggest problem with the example code is that it's object-oriented and mutable, which forces a sprawling context on the developers working with it.When I read the replace() method, I was immediately confused because it has no arguments. stringToReplace and alreadyReplaced are properties of the class, so you must look elsewhere (meaning, outside of the function) for their definitions. You also don't know what other bits of the class are doing with those properties when you're not looking. Both of these facts inflate the context you have to carry around in your head.Why is this a class? In the replace() method, there is a call to translate(symbolName). Why isn't there also a SymbolNameTranslator class with a translate() method? Who decided one was simple enough to use a function while the other warrants a class?SymbolReplacer surely could also be done with a function. I understand that this is illustration code, so the usage and purpose is not clear (and the original Bob Martin article does not help). Is there a reason we want these half-replaced strings made available every time we call SymbolReplacer.replace()? If there is, we can get the same data by using a reduce function and returning all of the iterations in a list.A plain, immutable function necessarily contains within it the entire scope of its behavior. It accepts and returns plain data that has no baggage attached to it about the data's purpose. It does one thing only.
  • panstromek
    I also find that phrase super misleading. I've been using a different heuristic that seems to work better for me - "comments should add relevant information that is missing." This works against redundant comments but also isn't ambigous about what "why" means.There might be a better one that also takes into account whether the code does something weird or unexpected for the reader (like the duplicate clear call from the article).
  • mattacular
    Explain "why not what" is good general advice. My further advice for comments is: even bad comments can be useful (unless they're from LLM output maybe...) therefore when in doubt, write a comment. Write it in your own words.Had to add the last sentence for the circa 2020s developer experience. LLM comments are almost never useful since they're supposed to convey meaningful information to another human coder, anything your human brain can think of will probably be more helpful context.
  • frumiousirc
    The article is about comments. But, more generally, I think the issue here is about naming things.Names capture ideas. Only if we name something can we (or at least I) reason about it. The more clear and descriptive a name for something is, the less cognitive load is required to include the thing in that reasoning.TFA's example that "weight" is a better variable name than "w" is because "weight" immediately has a meaning while use of "w" requires me to carry around the cumbersome "w is weight" whenever I see or think about "w".Function names serve the same purpose as variable names but for operations instead of data.Of course, with naming, context matters and defining functions adds lines of code which adds complexity. As does defining overly verbose variable names: "the_weight_of_the_red_ball" instead of "weight". So, some balance that takes into account the context is needed and perhaps there is some art in finding that balance.Comments, then, provide a useful intermediate on a spectrum between function-heavy "Uncle Bob" style and function-less "stream of consciousness" style.
  • jph
    These examples could both be much better IMHO with a top comment block that describes the purpose of the functionality and shows good usage examples. Something like this below, and ideally using runnable doc comments to help keep the comment correctly explaining the code.Replace symbol placeholders in the input string with translated values. Scan the string for symbol placeholders that use the format "$foo". The format uses a dollar sign, then optional ASCII letter, then optional word characters. Each recognized symbol is replaced with its corresponding value.Symbols are only replaced if the symbol exists i.e. getSymbol(String) returns non-null, and the symbol has not already been replaced in this invocation.Example: - input = "Hello $name, welcome to $city!" - output -> "Hello Alice, welcome to Boston!" Return the string with symbol placeholders replaced.
  • antirez
    I made a point here https://antirez.com/news/124 that comments are needed at the same time for different reasons, and different comments have differente semantical properties that can be classified in classes you very easily find again and again, even in very different code bases.
  • jbreckmckye
    Sometimes I want to use comments because I'm doing something vaguely algorithmic, and I know some readers won't follow the code.I'm trying to think of a good example, maybe something like a pointer window based function (off the top of my head)(This isn't real code. Don't get hung up on it) func DedupeStrings(ss []string) []string { if len(ss) < 2 { return ss } ss = strings.Sort(ss) u := 1 // index of end of "uniques" set for i := 1; i < len(ss); i++ { // Consume until new value if ss[i] == ss[i-1] { continue } // Put new value in 'uniques' set ss[u] = sorted[i] u++ } // Final state: all unique items are positioned // left of 'u' index return ss[:u] } People will quibble, but- I'm not convinced you could change the variable names without harming clarity. Would a name like uniquesEndIndex really be any clearer? It adds noise to the code and still doesn't satisfy a confused reader- I don't want to use function calls for documentation, eg putInUniques(). I'm doing it this way because I want it to run really quick.
  • xg15
    The "don't explain variable definitions with comments because people won't see the comments at usage sites" argument also seems obsolete in the face of modern(?) IDEs.If I look through code and see a variable I don't know, I want to see its definition anyway, so I know the type, scope, initial value, etc. And it's trivially possible to do that with everything that has a "jump to definition" command.
  • rokkamokka
    I agree wholeheartedly - I've fought against refactors like this for the same reason. In the end readability is king, and jumping through 6 methods instead of one decreases readability quite a bit.
  • ivanjermakov
    I noticed that when I write code that is not trivial to understand I tend to extract intermediate values into variables with meaningful names. applyDrag(): void { const { quad: quadConfig } = settings const quad = this.getRigidBody() const quadVel = vec3ToTwgl(quad.linvel()) const dragMag = aerodynamicDrag(quadConfig.dragCoefficient, v3.length(quadVel), quadConfig.frontalArea) const dragDir = v3.negate(v3.normalize(quadVel)) const dragForce = v3.mulScalar(dragDir, dragMag) const dragImpulse = v3.mulScalar(dragForce, dt) quad.applyImpulse(vec3TwglToRapier(dragImpulse), true) } This way code gets more natural language anchors which helps understanding what it does.
  • nooee
    Also note, that after Uncle Bob's refactoring, you now have six additional functions floating around, which may or may not make sense outside of the context of their original caller. This can make the API surface of your class (even if it's just the internal one) harder to grasp and it invites other devs to reuse code that was never intended for reuse, creating unintended couplings. That's mainly a problem for languages that don't allow nested functions though. But in Java, I am sceptical of excessive function creation for the sake of self-documenting code, unless maybe it's in the context of a command pattern, where the whole class only has one obvious function anyway.
  • onion2k
    If you need a comment to explain 'w' means 'weight' then you should remove the comment and rename the variable 'weight'.
  • kayo_20211030
    Love it. Particularly the "correct" refactoring at the end. The future maintainer will thank you. (BTW: that future maintainer might be you). To a first approximation one should write "local" code i.e. the bits that are important should be right in front of you in the editor. It shouldn't need too much navigation, with all the overhead of context switches, to understand what you're looking at.Of course, be sensible and use good judgement :-)But, that's what we're paid for, right?
  • willtemperley
    I like to see comments for external information that can't be derived from the code or API documentation.For example, for some reason padding bytes are allowed in variable length integers (LEB128) for reasons I still do not understand: // Allow padding bytes that do not affect the value let expectedBits: UInt8 = (result < 0) ? 0x7F : 0x00
  • scottlamb
    This is tangential to the article's point, but that `replace` function is a complete WTF in a way both authors completely ignore. Because it replaces things in the entire string in a loop, it will translate symbols recursively or not depending on ordering. Imagine you have the following dictionary: a=$b b=oops if your input string just has one of these, it will just be translated once as the programmer was probably expecting: input: foo $a bar output: foo $b bar but if your input string first references $b later, then it will recursively translate $a. input: foo $a bar $b output: foo oops bar oops Sometimes translating recursively is a bizarre behavior and possibly a security hole.The sane thing would be to loop through building the output string, adding the replacement for each symbol as you go. Using String.replace and the alreadyReplaced map is just a bad idea. Also inefficient, as it and throws away strings and does a redundant search on each loop iteration.Feels typical of this whole '90s-era culture of arguing over refactoring with Java design patterns and ornate styles without ever thinking about if the algorithm is any good.Edit: also, consider $foo $foobar. It doesn't properly tokenize on replacement, so this will also be wrong.
  • shevy-java
    I use a lot of comments, for many reasons.While it leads to more things to read, which thus may take time, I feel that the benefits of using comments far outweighs the negative sides. This is even valid when comments are outdated usually. To me, adjusting and updating comments often was much easier and faster than describing something de-novo.In the ruby land this is quite problematic because many do not use any comments. The result is a horrible code base. Getting people who wrote that code to use comments is often too late, as they already abandoned ruby in favour of another language, so I never buy the cop-out explanation of "the code is self-explanatory" - not even in ruby it is. Everyone can write horrible code.
  • StellarScience
    Ignoring the git commit message strawman (those can include "what/why the change", not "what/why the code") and the Uncle Bob strawman, the final code block looks fine. But notice: // translate will replace all instances; only need to run it once This is a "why". // Replace all symbols This is a "what". It's better conveyed by improving the function name rather than by a comment: String replaceAllSymbols() { Ultimately this article buttresses conventional wisdom about comments.
  • meindnoch
    "Uncle Bob" is a bad programmer, simple as.Change my mind!
  • d-lisp
    As I am involved in more low level stuff, I prefer to read the source than the man pages, and I am very happy with people overcommenting their code as a user of e.g. a lib. On the other hand, it is unbearable to me to see comments on a codebase I am working on. Fortunately, emacs show/hide comments exists, so I find myself overcommenting things.
  • epolanski
    I like antirez' style of comments.He starts implementing any module or function by first writing the documentation for it and let's it guide both the functionality and structure.Makes Redis also extremely easy and enjoyable to read.Random example: https://github.com/redis/redis/blob/unstable/src/aof.c
  • Scarblac
    Nowadays my rule is "the comment should contain enough about the why and the how so that the LLM generates the next few lines mostly correctly."
  • aquafox
    I'm a data scientist and a lot of my R code are dplyr-chains a la data |> select(features) |> filter(low_quality) |> mutate(feature=...). It just saves time to comment on what those chains do instead of having go through them every time I want to change something.
  • drob518
    I agree with the OP, but, unfortunately, posts like this generate more heat than light. Like a lot of things, what and how to comment code comes down to “do the right thing,” not a list of rules, where everyone will always find counter examples. Do whatever you need to communicate effectively with “the next guy,” who could very well be you.
  • CuriouslyC
    Comments should explain everything, but via links. Large comments cause context rot, so keep your comments tight and focused, and provide links to details as needed.
  • ImPleadThe5th
    I really do not like working with uncle bob hardlinersThere have been so many times where I have commented _why_ I think some uncle bob-ism made the code unclear and the response is always:> *Sends link to Clean Code, maybe you don't know about this?No, I do, and I am allowed to disagree with it. To which they always clutch their pearls "How do you think you know better than uncle bob!?", this is a Well Established Pattern TM.I don't think I know better than Uncle Bob, but I don't think Uncle Bob works on this codebase nearly as much as you or I.
  • DiabloD3
    My favorite genre of comments: it warns you not to touch it, and if you do, you must add to the list under the comment how many hours you wasted and got nowhere.
  • rossant
    I like to use comments extensively, even if it’s just to visually separate blocks of code in my IDE. A bit more spacing simply feels cleaner to me.
  • reactordev
    Should stick a (2017) on there as this is almost a decade old.Comments should explain. If it’s why, it’s why. If it’s what, it’s what. The point of comments is to explain, to the reader/editor, what the intent of the code is and how you chose to approach it. That’s it. No need to fight over what, why, how, who, etc.If you have domain knowledge that helps support the code, maybe a link to further reading, by all means add it to comments please!
  • okonomiyaki3000
    We need to differentiate between a "What this is" comment and a "What this does" comment. "What this does" is a lot closer to "Why" than it is to "What this is". I would hope "What this is" is rarely needed but "What this does" can certainly be helpful pretty often.
  • oweiler
    His first "what" example actually explains "why" clear is called two times. The code perfectly captures the "what".
  • croes
    Isn’t the purpose of comments to make code understandable?If that needs a why it’s a why-comment.If it needs a what it’s a what-comment. Especially if clever programming tricks are used. 6 month later you already forgot what the trick is and how it works.
  • jarek83
    I think the benefits of either clean code (sepearate methods) and commenting "what" are of different kind:- clean code one, for me it just reads easier, specific bits of the larger operation are not surrounded with noise coming from other bits like it is in the commenting "what" one. I can focus more on each step, and that can make it easier to spot a bug or to refactor/adjust/extend, as now I'm more like on a given page of Lego set instructions- the commenting of "what" one - yeah, this obviously makes it quicker (but probably not easier) to see the larger picture. It has benefits, but I believe this helps more hacker/scripter kind of people than programmers
  • xlii
    I'm all against commenting gatekeeping. IMO this is an anti-pattern.Even in stupidest cases, like: // add two and two let four = two + two The rationale for this is that if you start to mute developer on case of "this is not comment-worthy" you start to actually losing context for the codebase. Sure, maybe "add two and two" is not contextful enough, but "add same account twice" might be signal for appropriate code.Maybe that's me, but I rarely saw teams which over-document, under-documenting is usually the case. So if we ever meet in professional environment - comment away. Worst case scenario - I'll skim over it.
  • shiandow
    If we're doing hot takes then I propose the following guideline taken from mathematics.Only comments should explain what, variable names should only hintThe first example is perfectly fine, nobody has the time to derive or read a verbose formula involving the words 'weight', 'radius' and 'price'.
  • KaiserPro
    I have changed my commenting style now, since the rise of LLMs.I used to comment in a similar way to claude/chatgpt, as in both the what and why. (although the why in LLMs is often lost. ) I used to comment as if it was for someone who didn't know the libraries very well. (ie me in two years time. Documentation at the previous place was utterly shite, so it was effectively a journal of discovery)However, my commenting style is both what and why. My variable names can be longer than my FANG peers, mainly because I know that English not being a first language means that half arsed abbreviations are difficult to parse.But effort is placed on not using fancy features, python mostly, so avoiding lambdas and other "syntactic sugar", unless they absolutely make sense. If I do use them, i have a comment saying _why_ I'm doing it.Some of my colleagues were dead against any kind of commenting. "my code should be readable enough without it". They had the luxury of working on one bit for a few months, then throwing it away.
  • tangotaylor
    “What” comments can be quite nice to quickly parse through code.But I don’t think they’re worth it. My issue with “what” comments is they’re brittle and can easily go out of sync with the code they’re describing. There’s no automation like type checking or unit tests that can enforce that comments stay accurate to the code they describe. Maybe LLMs can check this but in my experience they miss a lot of things.When “what” comments go out of sync with the code, they spread misinformation and confusion. This is worse than no comments at all so I don’t think they’re worth it.“Why” comments tend to be more stable and orthogonal to the implementation.
  • locallost
    People (and I'm one of them) usually say why instead of what because in general the what can be understood if you can read code. But obviously no hard rules, if I write something twisted because I had no idea how to do it better or had the time, then I'll write what it does. It's not perfect, but nothing is.Same goes for comments vs commit messages. It's a fact comments get outdated and then you have an even bigger problem whereas a commit message is always correct at the time it was made. But obviously again, no hard rules. Sometimes I feel it's better to write a comment, e.g. if it's something that is really important and won't change a lot.
  • drivingmenuts
    Here's a thought, and I'm just spitballin' here: maybe they could do both? Sure, writing an encyclopedic tome about three lines of code is probably a bit out of bounds, but overexplaining is only bad when it's your wife or girlfriend. It might be a good idea to be absolutely clear about things.
  • TZubiri
    The maybe is the strongest part of this article.In my early days I read a lot of "you should" "this is wrong". But code is an expressive medium, you can do things one way, or do it the other, you can write "amountOfEmployees" or you can write "ne" with an "#amount of employees" comment, either way is absolutely fine and you can use whichever depending on your priorities, tradeoffs or even your mood.Also I used to obsess over code, (and there's a lot of material that obsesses about code), but after you become profficient at it, there's a cap on the returns of investing time into your codebase, and you start to focus on the product itself. There's not much difference between a good codebase and a marvelously polished codebase, so you might as well use the extra focuse on going from a bad UX to a neutral UX or whatever improvement you can make to the product.
  • mgaunard
    bug workarounds don't need to be justified if instead of working around the bug you just fix it instead.
  • zephen
    There are five canonical questions: What, When, Where, Why, and How."When" is occasionally a good question to answer in a comment, e.g. for an interrupt handler, and "Where" is also occasionally a good thing to answer, e.g. "this code is only executed on ARM systems."The other three questions typically form a hierarchy: Why -> What -> How.A simplistic google shows that "code comments" are next to "what" and "how" at about the same frequency as they are next to "why" and "what."This makes some amount of sense, when you consider the usual context. "Why" is often an (assumed) obvious unstated business reason, "What" is a thing done in support of that reason, and "How" is the mechanics of doing the thing.But with multiple levels of abstraction, _maybe_ the "What" inside the hierarchy remains a "What" to the level above it, but becomes a "Why" to the next level of "What" in the hierarchy. Or maybe the "How" at the end of the hierarchy remains a "How" to the level above it but becomes a "What" to a new "How" level below it.Is it:Why -> What/Why -> What/Why -> What/Why -> What -> HoworWhy -> What -> How/What -> How/What -> How/What -> HowIn many cases the intermediate nodes in this graph could be legitimately viewed as either What/Why or as How/What, depending on your viewpoint, which could partly depend on which code you read first.In any case, there are a few hierarchies with final "Hows" that absolutely beg for comments (Carmack's famous inverse square root comes to mind) but in most problem domains that don't involve knowledge across system boundaries (e.g. cache optimization, atomic operations, etc.), the final "How" is almost always adequately explained by the code, _if_ the reader understands the immediately preceding "What."If I see a function "BackupDatabase()" then I'm pretty sure I already know both "Why" and "What" at the highest levels. "How" I backup the database might be obvious once I am reading inside the function, or the code might be opaque enough that a particular set of lines requires explanation. You could view that explanation as part of "How" the database is backed up, or you could view that explanation as "What" the next few lines of code are doing.Again, this viewpoint might even partly depend on where you started. If you are dumped inside the function by a debugger, your question might be "What the heck is this code doing here?" but if you are reading the code semi-linearly in an editor, you might wonder "How is BackupDatabase() implemented?"
  • maximgeorge
    [dead]
  • huflungdung
    [dead]
  • akabalanza
    I'm glad LLMs will make these conversations obsolete, just like linters did to tab-vs-spaces
  • broken-kebab
    IMO the example shows exactly that splitting code in smaller pieces is way better than just commenting it.It makes it easier for dev's brain to parse the code e.g. to understand what code really does, while fattier but commented version makes it harder but tries to replace it with information about original coder's intentions. Which is maybe important too but not as important as code itself.Not to forget that it's too easy to change code and leave comments obsolete.