90

I'm having some discussions with my new colleagues regarding commenting. We both like Clean Code, and I'm perfectly fine with the fact that inline code comments should be avoided and that class and methods names should be used to express what they do.

However, I'm a big fan of adding small class summaries that tries to explain the purpose of the class and what is actually represents, primarily so that its easy to maintain the single responsibility principle pattern. I'm also used to adding one-line summaries to methods that explains what the method is supposed to do. A typical example is the simple method

public Product GetById(int productId) {...}

I'm adding the following method summary

/// <summary>
/// Retrieves a product by its id, returns null if no product was found.
/// </summary

I believe that the fact that the method returns null should be documented. A developer that wants to call a method should not have to open up my code in order to see if the method returns null or throws an exception. Sometimes it's part of an interface, so the developer doesn't even know which underlying code is running?

However, my colleagues think that these kinds of comments are "code smell" and that "comments are always failures" (Robert C. Martin).

Is there a way to express and communicate these types of knowledge without adding comments? Since I'm a big fan of Robert C. Martin, I'm getting a bit confused. Are summaries the same as comments and therefore always failures?

This is not a question about in-line comments.

Martin Schröder
  • 354
  • 1
  • 4
  • 19
Bjorn
  • 1,073
  • 1
  • 8
  • 8
  • 41
    Robert Martin said "Comments are **always**failures"? Well, then he's a fringe extremist, and should be taken with a pinch of salt. (Yes, I'm aware that he writes like this for rhetorical purposes, to get his message across. My point is, *so should you*.) – Kilian Foth Jun 04 '15 at 08:35
  • 1
    Yes that's what he writes, and I'm taking both he, my colleagues and myself with a big pinch of salt :) But I still think that there is a "dilemma" here that is interesting. – Bjorn Jun 04 '15 at 08:50
  • 23
    Uncle Bob's books should come with a 1kg bag of salt... – AK_ Jun 04 '15 at 09:44
  • 3
    gnat - that question concerns in-line comments – Bjorn Jun 04 '15 at 09:50
  • 7
    If you are following Robert Martin, the documentation for the null case should be a test. That is, you should have a test showing in which case the method can return null. Alternatively, since this is Java, a @Nullable annotation would also be better than the comment. – Martin Epsz Jun 04 '15 at 11:45
  • 2
    @MartinEpsz Looks more like C# – Ben Aaronson Jun 04 '15 at 12:23
  • What about the `@Nullable` and `@NotNull` annotations in java? – gurghet Jun 04 '15 at 14:32
  • 18
    @Bjorn I own a copy of Clean Code and have read it cover to cover more than once. Yes, Uncle Bob prefers code to be self-documenting, but there are multiple examples of comments _in his own code in the book_. The point is if you feel compelled to write a comment, try really hard to change the code rather than adding the comment, but do not disallow comments entirely (even in-line comments). –  Jun 04 '15 at 14:59
  • 7
    The method should be called TryGetById and the comment should be removed. – usr Jun 04 '15 at 15:14
  • 2
    @gnat I disagree that this is an exact duplicate of [that question](http://programmers.stackexchange.com/q/1/1130). This question is specifically addressing documentation comments and when/how to use them, and there are already some good answers here that address this specific question. The other question is about in-line comments, and even explicitly says it is not about *"javadoc style method or class comments"*. The answers there are tailored to in-line comments, and do not address this specific question. Plus, its closed and locked, so no new information can be added. – Rachel Jun 04 '15 at 15:50
  • @Rachel (and everyone else) There's an [active Meta discussion](http://meta.programmers.stackexchange.com/questions/7413/why-was-this-question-closed-as-duplicate) about this. – yannis Jun 05 '15 at 09:21
  • Another solution for the problem of explicitly maybe returning null is the Option Type(https://en.wikipedia.org/wiki/Option_type). This can be implemented in any language, but it may not always be idiomatic. – Martin Epsz Jun 05 '15 at 11:39
  • Isn't the single responsibility thingie more of principle than a pattern? – Peter Mortensen Jun 05 '15 at 14:09
  • @PeterMortensen The wikipedia page is called "Single Responsibility Principle", so I believe you are right. – Zack Jun 05 '15 at 16:54
  • 1
    One of the best teams I ever worked on referred only-half-jokingly to comments as "lies". We did use comments, but 80% of the time preferred to express the ideas using code changes. Extract and name a method was a favourite. You're only doing this weird thing to work around a bug in someone else's code? Then extract the weird thing in a function called "work_around_bug_in_libxyz()". – Jonathan Hartley Jun 06 '15 at 22:45
  • 5
    Some things never change. Many programmers just don't like writing comments. My first job was almost 40 years ago, and I had to modify COBOL programs written by people who didn't put any comments in their code, and I got real frustrated trying to figure out what the code was doing. Now they can use a misreading of an excellent book to justify their distaste for comments, and I still get frustrated trying to figure the code out. The code has nice descriptive method names, but the names don't tell me everything I need to know, so I have to open up tons of code to figure it out... – ajb Jun 06 '15 at 23:44
  • 6
    The idea of making your code more expressive, instead of using comments as a crutch, is an excellent one. The idea that comments are always bad is taking an excellent idea to a really bad extreme. The idea of not providing API documentation is one of the more destructive ideas that's become popular, and I am certain it is _not_ an idea supported by Martin or other of our better authors, but is rather based on a sloppy and out-of-context reading of what they've said. – ajb Jun 06 '15 at 23:47
  • The XML comment documentation is good, but the comma splicing is terrible. Try this instead: "Retrieves a product by its id; returns null if no product was found." – Kyralessa Jun 09 '15 at 15:30
  • It's so sad to keep seeing devs think comments are the way to code. It's just really amazing to me. Just tells me how far we still have to go to mature this profession. – PositiveGuy Jun 17 '17 at 19:09
  • >However, I'm a big fan of adding small class summaries that tries to explain the purpose of the class and what is actually represents. No, start writing tests, keep classes small, and name things well. That's ALL you need to be doing. If you are not writing tests and your design smells, comments just make even more clutter if your code smells and you don't have tests. – PositiveGuy Jun 17 '17 at 19:11
  • There is also a very monolithic feel in the .NET community as a whole. Devs are doing things the same way they've been doing them 10 years ago and need to wake up and realize that we're doing things WRONG. We should be practicing TDD, writing good tests and writing self described code. That means you don't go and grab Entity Framework every time you need persistence. You need to start writing tests and writing clean code, and you'll realize all these canned solutions by MS are bloat. – PositiveGuy Jun 17 '17 at 19:13

10 Answers10

122

As others have said, there's a difference between API-documenting comments and in-line comments. From my perspective, the main difference is that an in-line comment is read alongside the code, whereas a documentation comment is read alongside the signature of whatever you're commenting.

Given this, we can apply the same DRY principle. Is the comment saying the same thing as the signature? Let's look at your example:

Retrieves a product by its id

This part just repeats what we already see from the name GetById plus the return type Product. It also raises the question what the difference between "getting" and "retrieving" is, and what bearing code vs. comment has on that distinction. So it's needless and slightly confusing. If anything, it's getting in the way of the actually useful, second part of the comment:

returns null if no product was found.

Ah! That's something we definitely can't know for sure just from the signature, and provides useful information.


Now take this a step further. When people talk about comments as code smells, the question isn't whether the code as it is needs a comment, but whether the comment indicates that the code could be written better, to express the information in the comment. That's what "code smell" means- it doesn't mean "don't do this!", it means "if you're doing this, it could be a sign there's a problem".

So if your colleagues tell you this comment about null is a code smell, you should simply ask them: "Okay, how should I express this then?" If they have a feasible answer, you've learned something. If not, it'll probably kill their complaints dead.


Regarding this specific case, generally the null issue is well known to be a difficult one. There's a reason code bases are littered with guard clauses, why null checks are a popular precondition for code contracts, why the existence of null has been called a "billion-dollar mistake". There aren't that many viable options. One popular one, though, found in C# is the Try... convention:

public bool TryGetById(int productId, out Product product);

In other languages, it may be idiomatic to use a type (often called something like Optional or Maybe) to indicate a result that may or may not be there:

public Optional<Product> GetById(int productId);

So in a way, this anti-comment stance has gotten us somewhere: we've at least thought about whether this comment represents a smell, and what alternatives might exist for us.

Whether we should actually prefer these over the original signature is a whole other debate, but we at least have options for expressing through code rather than comments what happens when no product is found. You should discuss with your colleagues which of these options they think is better and why, and hopefully help move on beyond blanket dogmatic statements about comments.

Ben Aaronson
  • 6,883
  • 1
  • 30
  • 30
  • 9
    Or the `Linq`-equivalent of `Try...`, `...OrDefault`, which return `default(T)` if the clause would lead to an empty result. – Bart van Nierop Jun 04 '15 at 10:29
  • 4
    I really appreciate your distinction between inline-code-comments and documentation comments, and the examples given :) – Rachel Jun 04 '15 at 15:41
  • 2
    The possible return values of a function should be evident by its signature. The `TryGetValue` pattern is a reasonable way of doing this in C#, but most functional languages have a better way of representing a missing value. [Read more here](http://twistedoakstudios.com/blog/Post1130_when-null-is-not-enough-an-option-type-for-c) – Alex Jun 04 '15 at 16:40
  • 1
    @AlexFoxGill Yep, good point! I actually recently recommended a couple of simplified versions of this idea here: http://programmers.stackexchange.com/questions/285577/if-possible-would-it-be-a-bad-practice-to-use-nullable-for-non-value-types/285586#285586 . It seemed a bit beyond the scope of the current question. – Ben Aaronson Jun 04 '15 at 16:45
  • 1
    @cmaster Within .NET, I think there's a clear convention that `Try...` with a signature similar to the one I outlined will return false to indicate failure, and not use an exception. In fact whether or not you want an exception is the main reason to choose between `Int32.Parse` and `Int32.TryParse`, for example. – Ben Aaronson Jun 04 '15 at 16:48
  • 2
    @BenAaronson: If one wants to have a generic interface that can support covariance, one could use either `T TryGetValue(params, ref bool success)` for any type T, or `T TryGetValue(params)`, with null indicating failure, for class-constrained type T, but the `TryGetXX` pattern that returns `bool` is incompatible with covariance. – supercat Jun 04 '15 at 17:07
  • 1
    Even vacuous comments like `Returns the id` are helpful, because they still convey that _"The author of this method didn't feel there were any pitfalls in using it that were worth mentioning"_, which is good to know. A lack of documentation comment might convey that, but it could also have been an oversight. – BlueRaja - Danny Pflughoeft Jun 04 '15 at 19:12
  • I can't see any reason to document that it returns a product by ID--the name & return already tell you that. The only thing I see that needs documenting is whether it returns a null or throws if there is no such product. The Try version mentioned in the comments needs no documentation as it clearly returns false if there's no such product. – Loren Pechtel Jun 04 '15 at 23:06
  • 6
    In Java 8, you can return an `Optional` to indicate that there might not be a Product returned from the method. – Wim Deblauwe Jun 05 '15 at 06:18
  • 1
    +1 for "hopefully help move on beyond blanket dogmatic statements". – jpmc26 Jun 05 '15 at 23:23
  • `So in a way, this anti-comment stance has gotten us somewhere: we've at least thought about whether this comment represents a smell, and what alternatives might exist for us` - I happen to like the Try- naming convention in .NET, but it is not an "anti-comment stance", rather it's just a nifty convention. Look at the actual public documentation of the `Try`-named methods in .NET. They are always accompanied by an explicit comment stating that such methods are to signal failure using the return value. – Brandin Jun 06 '15 at 11:19
  • @Brandin Yep, I agree. The "anti-comment stance" I was referring to was the coworkers in the OP. In this case, the Try-convention could be a way to eliminate the need to flag up the null, so taking the coworkers' stance of "can we remove the need for this comment" led us to think of the idea of using the convention. I didn't mean that there's anything *inherently* anti-comment about that convention. – Ben Aaronson Jun 06 '15 at 11:56
  • this is terrible both the name (why tack on 'Try', pointless) and the stupid out param. Out params are just complete garbage. Do not do this! public bool TryGetById(int productId, out Product product); This is exactly why I left .net for Node and React, this mentality. – PositiveGuy Jun 17 '17 at 18:49
  • @PositiveGuy I agree. The "Try" pattern is common in .NET but it's not used so much any more because out parameters are annoying to handle. Potentially with C# 7 it may become a common idiom to use a `Tuple` for this kind of situation, which would be a little nicer. – Ben Aaronson Jun 19 '17 at 09:33
  • @PositiveGuy Anyway, like I said in the last paragraph, this answer isn't a recommendation to change the method signature, I think the original version with the comment is the most straightforward (unless you use `Option` everywhere in your project). It's just a demonstration of how treating comments as a possible code smell can highlight potential points of change. – Ben Aaronson Jun 19 '17 at 09:33
  • I thought the solutions for returning null were really spot on, but how about throwing an exception? Has anyone come up with a way in code to deal with thrown exceptions? Specifically in a language like C# that doesn't have a `throws` keyword. – Devin Gleason Lambert Jul 12 '17 at 21:37
  • @DevinGleasonLambert I don't know of any way to make that self-documenting in C#. – Ben Aaronson Jul 13 '17 at 10:39
  • Actually @BenAaronson I went on to reading Chapter 7 Error Handling, and after reading section Use Unchecked Exceptions, he more or less condemns the use of the Java Throws keyword. Not that it specifically answers my question, but of the self-documenting code constructs for exception handling out there, I would think this would probably be the best one. – Devin Gleason Lambert Jul 13 '17 at 11:28
  • instead of returning a null, can we throw NotFoundException? – mercury0114 Aug 24 '23 at 09:56
113

The Robert C. Martin quote is taken out of context. Here is the quote with a bit more context:

Nothing can be quite so helpful as a well-placed comment. Nothing can clutter up a module more than frivolous dogmatic comments. Nothing can be quite so damaging as an old crufty comment that propagates lies and misinformation.

Comments are not like Schindler's List. They are not "pure good." Indeed, comments are, at best, a necessary evil. If our programming languages were expressive enough, or if we had the talent to subtly wield those languages to express our intent, we would not need comments very much -- perhaps not at all.

The proper use of comments is to compensate for our failure to express ourself in code. Note that I used the word failure. I meant it. Comments are always failures. We must have them because we cannot always figure out how to express ourselves without them, but their use is not a cause for celebration.

So when you find yourself in a position where you need to write a comment, think it through and see whether there isn't some way to turn the tables and express yourself in code. Every time you express yourself in code, you should pat yourself on the back. Every time you write a comment, you should grimace and feel the failure of your ability of expression.

(Copied from here, but the original quote is from Clean Code: A Handbook of Agile Software Craftsmanship)

How this quote is reduced into "Comments are always failures" is a good example of how some people will take a sensible quote out of context and turning it into stupid dogma.


API documentation (like javadoc) is supposed to document the API so the user can use it without having to read the source code. So in this case the documentation should explain what the method does. Now you can argue that "Retrieves a product by its id" is redundant because it is already indicated by the method name, but the information that null may be returned is definitely important to document, since this in not in any way obvious.

If you want to avoid the necessity of the comment, you have to remove the underlying problem (which is the use of null as a valid return value), by making the API more explicit. For example you could return some kind of Option<Product> type, so the type signature itself communicates clearly what will be returned in case the product is not found.

But in any case it is not realistic to document an API fully just through method names and type signatures. Use doc-comments for any additional non-obvious information which the user should know. Take say the API documentation from DateTime.AddMonths() in the BCL:

The AddMonths method calculates the resulting month and year, taking into account leap years and the number of days in a month, then adjusts the day part of the resulting DateTime object. If the resulting day is not a valid day in the resulting month, the last valid day of the resulting month is used. For example, March 31st + 1 month = April 30th. The time-of-day part of the resulting DateTime object remains the same as this instance.

There is no way you could express this using just the method name and signature! Of course your class documentation might not require this level of detail, it is just an example.


Inline comments are not bad either.

Bad comments are bad. For example comments which only explains what can be trivially seen from the code, the classical example being:

// increment x by one
x++;

Comments which explains something which could be made clear by renaming a variable or method or otherwise restructuring the code, is a code smell:

// data1 is the collection of tasks which failed during execution
var data1 = getData1();

These are the kind of comments Martin rails against. The comment is a symptom of a failure to write clear code - in this case to use self-explanatory names for variables and methods. The comment itself is of course not the problem, the problem is we need the comment to understand the code.

But comments should be used to explain everything which is not obvious from the code, e.g. why the code is written a certain non-obvious way:

// need to reset foo before calling bar due to a bug in the foo component.
foo.reset()
foo.bar();

Comments which explains what an overly convoluted piece of code does is also a smell, but the fix is not to outlaw comments, the fix is the fix the code! In the real word, convoluted code does happen (hopefully only temporarily until a refactor) but no ordinary developer writes perfect clean code the first time. When convoluted code happens it is much better to write a comment explaining what it does than not write a comment. This comment will also make it easier to refactor later.

Sometimes code is unavoidably complex. It may be an complicated algorithm, or it may be code sacrificing clarity for performance reasons. Again comments are necessary.

JacquesB
  • 57,310
  • 21
  • 127
  • 176
  • 14
    There's also the case where your code handles a situation that just _is_ complicated, and no simple code can handle it. – gnasher729 Jun 04 '15 at 09:02
  • 6
    Good point, gnasher. This seem to often happen when you have to optimize some piece of code for performance. – JacquesB Jun 04 '15 at 09:07
  • 2
    This is a great answer. I think that as developers we often want to have rules to work by, and "always comment" and "never comment" are convenient ones. But to be useful, we have to remember what the point of using comments is, and your answer captures that very well - the point is to communicate things that we can't otherwise communicate in the code. Remembering that and forgetting the dogma is key. – thomij Jun 04 '15 at 16:30
  • 9
    Even a comment in `x++` may be good if it's something like "increment x by one, wrapping around if it's UINT32_MAX"; anyone who knows the language spec would know that incrementing a `uint32_t` will wrap, but without the comment one may not know whether such wrapping was an *expected* part of the algorithm being implemented. – supercat Jun 04 '15 at 17:10
  • 4
    Instead of commenting that you're working around a bug, you could extract the workaround as a method such as `fooBug123NotResettingWorkaround`. And bam, the comment is unnecessary. – l0b0 Jun 04 '15 at 17:45
  • 5
    @l0b0: I hope you are joking! – JacquesB Jun 04 '15 at 23:48
  • 2
    @JacquesB Why would I be? That name is the best kind of reminder that this code is temporary, and it's also an actual container for the fix rather than having to guess which lines below (or even above, after refactoring) the comment are relevant for the fix. – l0b0 Jun 05 '15 at 07:16
  • 2
    @l0b0: Fair enough, but you have just removed valuable information from the program - the fact that we "need to reset foo before calling bar due to a bug in the foo component." The method name does not communicate all that, and removing this information only makes the code harder to maintain. – JacquesB Jun 05 '15 at 08:34
  • 11
    @l0b0 There is no such thing as temporary code. The code never gets refactored because the business was happy with the outcome and won't approve funding to fix it. Five years from now, some junior developer will see this code, you're not even using WizBug 4.0 anymore since you've replaced it with Bugtrocity v9, so "Bug123" means nothing to him. He now thinks this is how permanent code is supposed to be, and proceeds to be a terrible developer his entire career. Think of the children. Don't write temporary code. – corsiKa Jun 05 '15 at 17:44
  • @supercat: I agree, although I would probably like the comment to explain *why* wrapping is the expected and desired behavior, if it is not obvious from the context. – JacquesB Jun 06 '15 at 09:04
  • @JacquesB: Agreed, though in-line comments should I think focus more on assumptions (i.e. that `x` *will* wrap) than reasons for doing things. My point was that comments which say exactly what a statement doesn't aren't necessarily useless, even though I would probably write an increment which had to wrap at 65535 as `x=(x+1) & 65535;`. If I had a decrement which needed to wrap at zero but I didn't care exactly what it wrapped to, though, I might write `x--; // Wrap at zero`. Even though wrapping may be implied by the type of `x`, the comment would make clear that... – supercat Jun 06 '15 at 17:13
  • ...such behavior was required. If code didn't care about what value `x` wrapped to, beyond its being larger than any valid value, writing the code as `x--;` would make it less dependent upon the type of `x` than would variations which cast to a particular size. – supercat Jun 06 '15 at 17:15
  • 1
    you don't need comments when you write good tests (especially if you practice disciplined TDD) and self documenting code. Your tests document the requirements, your code tells you the "how" by modularizing your code into very small pieces and giving things good names. It's that simple, we don't need comments and I haven't commented code in probably 10 years. – PositiveGuy Jun 17 '17 at 18:51
  • @PositiveGuy would unit test popup quickly when hovering on the method signature? Or is it more that I would need to review the test suite to find out more details about expected behaviour of the code? Why in your opinion Javadoc or `` type of comments are bad? – Piotr Golacki Sep 15 '22 at 15:00
40

There is a difference between commenting your code and documenting your code.

  • Comments are needed to maintain the code later, that is change the code itself.

    Comments may indeed be perceived as problematic. The extreme point would be to say that they always indicate a problem, either within your code (code too difficult to understand) or within the language (language unable to be expressive enough; for instance, the fact that the method never returns null could be expressed through Code contracts in C#, but there is no way to express it through code in, say, PHP).

  • Documentation is needed to be able to use the objects (classes, interfaces) you developed. The target audience is different: it's not the persons who will maintain your code and change it that we are talking about here, but persons who barely need to use it.

    Removing documentation because the code is clear enough is insane, since documentation is here specifically to make it possible to use classes and interfaces without having to read thousands of lines of code.

Arseni Mourzenko
  • 134,780
  • 31
  • 343
  • 513
  • Yes, but at least part of Martin's point is that with modern development practices, the tests are the documentation, not the code itself. Assuming the code is being tested with a BDD-style test system, e.g. specflow, the tests themselves are a directly readable description of the behaviour of the method ("given a database of products when GetById is called with the id of a valid product then the appropriate Product object is returned [...] when GetById is called with an invalid product id then null is returned" or something like this). – Jules Jun 07 '15 at 02:45
13

Well, it seems your colleague reads books, takes in what they say, and applies what he learned without thinking and without any consideration of context.

Your comment about what the function does should be such that you can throw away the implementation code, I read the comment of the function, and I can write the replacement for the implementation, without anything going wrong.

If the comment doesn't tell me whether an exception is thrown or whether nil is returned, I can't do that. Furthermore, if the comment doesn't tell you whether an exception is thrown or whether nil is returned, then wherever you call the function, you must make sure your code works correctly whether an exception is thrown or nil is returned.

So your colleague is totally wrong. And go ahead and read all the books, but think for yourself.

PS. I saw your line "sometimes it's part of an interface, so you don't even know what code is running. " Even just with virtual functions, you don't know what code is running. Worse, if you wrote an abstract class, there isn't even any code! So if you have an abstract class with an abstract function, the comments that you add to the abstract function are the only thing that an implementor of a concrete class has to guide them. Those comments may also be the only thing that can guide a user of the class, for example if all you have is an abstract class and a factory returning a concrete implementation, but you never see any source code of the implementation. (And of course I shouldn't be looking at the source code of one implementation).

gnasher729
  • 42,090
  • 4
  • 59
  • 119
  • I have not commented code in 10 years. Comments are bloat, garbage. Nobody comments code these days. We focus on well formed and named code, small modules, decoupling, etc. THAT makes your code readable not comments. Tests ensure that if you throw away code, that nothing goes wrong, not comments. Tests tell you how you use the code you write, how you call them, and why they are there in the first place. You're way too old school you need to be learning about testing and clean code my friend. – PositiveGuy Jun 17 '17 at 19:03
12

There are two types of comments to consider - those visible to people with the code and those use to generate documentation.

The type of comment that Uncle Bob is referring to is the kind that is only visible to people with the code. What he is advocating for is a form of DRY. For a person that is looking at the source code, the source code should be the documentation that they need. Even in the case where people have access to the source code, comments aren't always bad. Sometimes, algorithms are complicated or you need to capture why you're taking a non-obvious approach so that others don't end up breaking your code if they try to fix a bug or add a new feature.

The comments that you are describing are API documentation. These are things that are visible to people using your implementation, but that may not have access to your source code. Even if they do have access to your source code, they may be working on other modules and not be looking at your source code. These people would find it useful to have this documentation available in their IDE as they are writing their code.

Thomas Owens
  • 79,623
  • 18
  • 192
  • 283
  • I honestly never thought of DRY applying to code + comments, but it makes perfect sense. Sort of like the "increment X" example in @JacquesB's answer. –  Jun 04 '15 at 15:04
7

The value of a comment is measured in the value of the information it gives minus the effort needed to read it and/or ignore it. So if we analyze the comment

/// <summary>
/// Retrieves a product by its id, returns null if no product was found.
/// </summary>

for value and cost, we see three things:

  1. Retrieves a product by its id repeats what the name of the function says, so it's cost without value. It should be deleted.

  2. returns null if no product was found is very valuable information. It likely reduces the times other coders will have to look at the implementation of the function. I'm quite certain that it saves more reading than the reading cost it introduces itself. It should stay.

  3. The lines

    /// <summary>
    /// </summary>
    

    carry no information whatsoever. They are pure cost to the reader of the comment. They may be justified if your documentation generator needs them but in that case you should probably think about a different documentation generator.

    This is the reason why the use of documentation generators is a disputable idea: They generally require a lot of additional comments that carry no information or repeat obvious stuff, just for the sake of a polished document.


An observation that I have not found in any of the other answers:

Even comments that are not necessary to understand/use the code can be very valuable. Here is one such example:

//XXX: The obvious way to do this would have been ...
//     However, since we need this functionality primarily for ...
//     doing this the non-obvious way of ...
//     gives us the advantage of ...

Likely a lot of text, completely unnecessary to understand/use the code. However, it explains a reason why the code looks the way it does. It will stop people short that look at the code, wonder why it's not done the obvious way, and would start refactoring the code until they realize why the code was written like this in the first place. And even if the reader is smart enough to not jump to refactoring directly, they still need to figure out why the code looks the way it does before they realize that it should best stay the way it is. This comment could literally save hours of work. Thus the value is higher than the cost.

Likewise, comments can communicate the intentions of a code, instead of just how it works. And they can paint the big picture that's usually lost in the minute detail of the code itself. As such, you are right in being in favor class comments. I value class comments the most if they explain the intention of the class, how it interacts with other classes, how it is meant to be used, etc. Alas, I'm not a big author of such comments...

  • 3
    Wow - change your document generator because it requires a couple extra lines of html for parsing? Let's not. – corsiKa Jun 05 '15 at 17:45
  • 2
    @corsiKa YMMV, but I for one would prefer a documentation generator that reduces the costs in comments to a minimum. Of course, I'd also rather read a well written header file than a doxygen documentation that's out of sync with the actual code. But, as I said, YMMV. – cmaster - reinstate monica Jun 05 '15 at 18:06
  • 5
    Even if a method's name describes its purpose brilliantly, repeating that purpose using natural language may make it easier for someone reading it to associate that purpose with any caveats that follow. A restatement of what's described in the name will be brief enough that even if the value is low, the cost will also be low. I thus disagree with the first part of your post. +1, however, for the second part. Documentation of alternative approaches that were evaluated and rejected can be extremely valuable, but such information is seldom given the attention it deserves. – supercat Jun 05 '15 at 20:54
  • `GetById` raises the question, what is id, and also, get what from where. Documentation comment should allow development environment to display answer to these questions. Unless it is explained elsewhere in the module doc comments, it would also be one place to tell why one would get the id anyway. – hyde Jun 08 '15 at 05:48
  • Comments blow, clean code (self describing), TDD (getting feedback often and getting feedback on your design often), and tests (give you confidence and document behavior) rules! TESTS people TESTS. An nobody here is talking about that. wake up – PositiveGuy Jun 17 '17 at 19:04
  • 1
    @PositiveGuy Well, the question is about comments, not tests, so answering/commenting on TDD is going off on a tangent. – cmaster - reinstate monica Jun 18 '17 at 14:24
5

Uncommented code is bad code. It is a widespread (if not universal) myth that code can be read in much the same way as, say, English. It must be interpreted, and for any but the most trivial code that takes time and effort. Plus, everybody has a different level of capability in both reading and writing any given language. Differences between the author and the reader's coding styles and capabilities are strong barriers to accurate interpretation. It is also a myth that you can derive the intention of the author from the implementation of the code. In my experience, it is rarely wrong to add extra comments.

Robert Martin et al. consider it a "bad smell" because it may be that the code is changed and the comments not. I say it is a good thing (in exactly the same way that a "bad smell" is added to domestic gas to alert the user to leaks). Reading comments give you a useful starting point to interpreting the actual code. If they match then you will have increased confidence in the code. If they differ then you have detected a warning smell and need to investigate further. The cure to a "bad smell" is not to remove the smell but to seal the leak.

  • Where do you draw the line? `i++; //Increment i by 1`? – Ben Aaronson Jun 04 '15 at 12:29
  • 2
    Given that you are an intelligent author and the text is for the benefit of an intelligent audience; You draw the line using common sense. Obviously, as it stands, the example is dumb but that's not to say that there may be not a be a very good reason to clarify with a comment why the code includes an i++ at that point. – Vince O'Sullivan Jun 04 '15 at 13:39
  • Yeah, that's usually the right way to do it. I just feel like from your answer, probably you draw the line at a quite different place to me, so I was wondering where that happens to be. – Ben Aaronson Jun 04 '15 at 13:42
  • 9
    `i++; // Adjust for leap years.` – Vince O'Sullivan Jun 04 '15 at 13:54
  • 6
    "Robert Martin et al. consider it a "bad smell" because it may be that the code is changed and the comments not." That's only part of the smell. The worst of the smell comes from the idea that the programmer decided not to try to write the code in a more descriptive way, and instead chose to "seal the leak" with a comment. His assertion is that rather than slap a "// adjust for leap years" comment, one probably should have a "adjustForLeapYears()" method in the code itself (or something similar). The documentation comes in the form of tests that exercise the leap year logic. – Eric King Jun 04 '15 at 15:39
  • 3
    I would consider adding a method call to replace a single line of code with a comment overkill, particularly since the name of the method is actually itself just a comment that labels a piece of code and no guarantee of better documentation accuracy. (If that line occurred in two or more places then introducing a method call would, of course, be exactly the right solution.) – Vince O'Sullivan Jun 04 '15 at 18:10
  • @VinceO'Sullivan Methods are not just a device to remove duplication, they represent an abstraction. They are not there to help you write code faster, but also to understand it quicker. Code+comment equals implementation+intent, whereas a method call only represents intent. Which is the actual overkill? – Eric Jun 05 '15 at 13:39
  • @Ericking Hmm, saying that instead of writing an "adjust for leap years" comment you should move the two lines of code to a function and name it "AdjustForLeapYears" sounds to me like you are just using a function name as a comment. How is this better than writing a comment, other than that it conforms to an arbitrary rule that you shouldn't use comments? That's like saying that "goto"'s are bad ... so I wrote "#define jump goto" so now I can write "jump" instead of "goto" and the problem is solved! :-) – Jay Jun 05 '15 at 14:34
  • 1
    @Jay The reason is that making your abstractions explicit (by, e.g., introducing methods) _is_ the rule. Not doing so because you might end up with a method that has one line is the exception. Let me paraphrase the _real_ arbitrary rule: "Use your programming language's structures (typically methods and classes) to introduce abstractions, _unless_ the implementation of that abstraction can be expressed as one line of code, in that case specify the abstraction in _natural language_ by adding a comment." – Eric Jun 05 '15 at 15:18
  • @VinceO'Sullivan: You don't always have to abstract with a function. You can also use named variables, e.g.: `bool connection_successfull = mysql_connect(); if(!connection_successfull) {...}`. – phresnel Jun 05 '15 at 16:16
  • 1
    @Jay: No, they are not the same. A function name and signature is, depending on the language, enforced by the compiler. A comment is not. – phresnel Jun 05 '15 at 16:17
  • @Jay I disagree. If the relevant context can be provided by the code itself, then it _should be in the code_. Otherwise, you're just disguising what should be code as a comment. I usually sum up Uncle Bob's take on comments this way: A comment is an admission that you failed to make the code explicit enough to stand on its own. Sometimes you _can't_ wrap enough context in the code, and you're forced to fall back on comments, but that should be the exception. The "leap year" example above is an obvious case where the code could easily be clearer, and the comment is an avoidable shortcut. – Eric King Jun 05 '15 at 16:59
  • @phresnel If a function is only called from one place, and you only created that function so that the name could serve as "self-documentation", then the fact that the compiler can enforce parameter types is irrelevant. The only reason the function exists is to have a name, and while the compiler can enforce that the name in caller and callee match, it can't enforce that the name accurately describes what the function does, which is the only reason why this function exists. So the one thing that matters, the compiler CAN'T enforce. – Jay Jun 08 '15 at 02:24
  • @EricKing So if you can express "meaning" and "descriptiveness" in code rather than comments, that's a good thing. I'd agree with that in principle. But the idea of moving code to a function just so you can give the function a name rather than writing a comment is applying this rule in a pedantic, irrational manner. The only gain in using the function name is that you comply with this rule. How about if instead of writing the comment, I added a line of code that said, "bool adjust_for_leap_year = true;". Then I never use this variable again. Now I don't need the comment, because the "code" ... – Jay Jun 08 '15 at 02:30
  • ... describes the purpose of the following lines. I have complied with the rule to not write comments. Is this better? I would say absolutely not, it is far worse, because the reader may be confused into thinking that this variable actually does something, rather than just being a name added to the code to act as a fake comment. That is very similar to the suggestion of writing a dummy subroutine whose only purpose is to provide a name that serves as a comment. – Jay Jun 08 '15 at 02:32
  • 1
    @Jay I don't know what you mean by "dummy subroutine". If the subroutine does just what it says is does, then it's hardly "dummy". Whatever. Also, there's no "rule against writing comments", not even in _Clean Code_. That's a straw-man. Anyhow, the basic premise is that if the meaning of the code _can_ be expressed by the code itself, then it _should_ be. Comments, should only be used when that isn't possible (which, contrary to popular belief, is rarely the case). If you don't agree with that style of coding, then so be it. But it's hardly pedantic and irrational. – Eric King Jun 08 '15 at 03:27
  • As a matter of fact, there's an entire chapter about comments in _Clean Code_, where a list of "good comments" are enumerated, including "Informative Comments", "Explanation of Intent", Clarification", Warning of Consequences", "TODO Comments", "Amplification", and API documentation. This list is preceded by a section called "Explain Yourself in Code" followed by the sentence "Keep in mind, however, that the only truly good comment is the comment you found a way not to write". – Eric King Jun 08 '15 at 03:32
  • @Jay: Exactly, the compiler can enforce that the "typed and named" comment stays in place where it's needed. It can also enforce formal correctness of the code (not semantic correctness, unfortunately). Comments are 100% into the wild. Anyways, it would be redundant to repeat all the arguments; _Clean Code_ et al have all the reasons. – phresnel Jun 08 '15 at 08:20
  • This web page, posted yesterday, contains a short block of code that fits easily on the screen: http://ericasadun.com/2015/06/09/swift-i-am-ridiculously-pleased-with-this/ The question below the code illustrates exactly why comments are a good thing and not some kind of "necessary evil". – Vince O'Sullivan Jun 10 '15 at 07:23
  • unreal that we still have people who are not aware of testing, and design code well, keeping things modular, small, and naming things well. It's sad. Comments blow. I haven't done them in 10 years and nobody I know comments code, they focus on clean code nowdays and writing tests. I TDD which brings even more benefits to me as a developer beyond this discussion. Commenting code isn't something we even think about. – PositiveGuy Jun 17 '17 at 19:07
  • >Reading comments give you a useful starting point to interpreting the actual code. You're totally lost. Reading TESTS give you a starting point. If you're not writing tests, you're a lost sole. – PositiveGuy Jun 17 '17 at 19:08
3

In some languages (F# for instance) this entire comment/documentation can actually be expressed in the method signature. This is because in F#, null is generally not an allowed value unless specifically allowed.

What is common in F# (and also several other functional languages) is that instead of null you use an option type Option<T> which could either be None or Some(T). The language would then understand this and force you to (or warn you if you don't) match on both cases when you try to use it.

So for example in F# you could have a signature that looks like this

val GetProductById: int -> Option<Product>

And then this would be a function that takes one parameter (an int) and then either returns a product, or the value None.

And then you could use it like this

let product = GetProduct 42
match product with
| None -> printfn "No product found!"
| Some p -> DoThingWithProduct p

And you would get compiler warnings if you do not match both possible cases. So there's no possible way to get null reference exceptions there (unless you ignore the compiler warnings of course), and you know everything you need just by looking at the function signature.

Of course, this requires that your language is designed in this way - which many common languages such as C#, Java, C++ etc are not. So this may not be helpful to you in your current situation. But it's (hopefully) nice to know that there are languages out there that lets you express this kind of information in a statically typed way without resorting to comments etc. :)

wasatz
  • 3,495
  • 3
  • 17
  • 19
1

There are some excellent answers here and I don't want to repeat what they say. But let me add a few comments. (No pun intended.)

There are lots of statements that smart people make -- about software development and many other subjects, I suppose -- that are very good advice when understood in context, but which silly people than take out of context or apply in inappropriate situations or take to ridiculous extremes.

The idea that code should be self-documenting is one such excellent idea. But in real life, there are limitations on the practicality of this idea.

One catch is that the language may not provide features to document what needs to be documented clearly and concisely. As computer languages improve, this becomes less and less of an issue. But I don't think it has completely gone away. Back in the days when I wrote in assembler, it made a lot of sense to include a comment like "total price=price of non-taxable items plus price of taxable items plus price of taxable items * tax rate". Exactly what was in a register at any given moment was not necessarily obvious. It took many steps to do a simple operation. Etc. But if you're writing in a modern language, such a comment would just be a restatement of one line of code.

I always get annoyed when I see comments like, "x=x+7; // add 7 to x". Like, wow, thanks, if I had forgotten what a plus sign means that might have been very helpful. Where I might really be confused is in knowing what "x" is or why it was required to add 7 to it at this particular time. This code might be made self-documenting by giving a more meaningful name to "x" and using a symbolic constant instead of 7. Like if you had written "total_price=total_price + MEMBERSHIP_FEE;", then a comment is probably not needed at all.

It sounds great to say that a function name should tell you exactly what a function does so that any additional comments would be redundant. I have fond memories of the time I wrote a function that checked if an item number was in our database Item table, returning true or false, and which I called, "ValidateItemNumber". Seemed like a decent name. Then someone else came along and modified that function to also create an order for the item and update the database, but never changed the name. Now the name was very misleading. It sounded like it did one small thing, when really it did much more. Later someone even took out the part about validating the item number and did that somewhere else, but still didn't change the name. So even though the function now had nothing to do with validating item numbers, it was still called ValidateItemNumber.

But in practice, it is often impossible for a function name to completely describe what the function does without the name of the function being as long as the code that makes up that function. Is the name going to tell us exactly what validations are performed on all parameters? What happens on exception conditions? Spell out every possible ambiguity? At some point the name would get so long that it just becomes confusing. I'd accept String BuildFullName(String firstname, String lastname) as a decent function signature. Even though that does not spell out whether the name is expressed "first last" or "last, first" or some other variation, what it does if one or both parts of the name are blank, if it imposes limits on the combined length and what it does if that's exceeded, and dozens of other detail questions one might ask.

Jay
  • 2,657
  • 1
  • 14
  • 11
0

There are only two things that are useful in understanding what some code does, and they are useful because they break the build if they are wrong, and are therefore reliable.

Comments in code are not reliable, cannot be trusted, and are therefore not useful. In many cases they are misleading and therefore harmful.

In the question, the OP was suggesting adding a comment indicating that the method can return null, but what if this method is calling something that calls something that gets changed by somebody to throw an exception instead? Are they going to trace all paths through the code to find and update this comment? Absolutely not, so now the comment becomes incorrect and misleading which actually slows people down and makes them write buggy code.

The two things that are actually useful in understanding what a method does are the method signature and the unit tests. These are useful because they are reliable.

The method signature can't not represent the parameters that are passed or the type that is returned unless the programmer did some horrible type casting (please don't do this). Since the compiler performs type checking, you can rely on the method signature being accurate and a reliable source of information. This is where we come to the hardest problem in programming, naming things. The name you choose for a type will become part of the "documentation" for every method that references that type, both current use cases and future use cased that you haven't thought of yet, and this is why choosing names is considered hard to do, but very important to get right.

Looking at the method signature alone does not convey all of the information about what exactly I can pass in and exactly what I will get back in various circumstances, and there are no programming languages that are rich enough to encapsulate that (Ada came pretty close).

All of the rest of the information is in the unit tests. Unit tests contain statements like:

describe('GetById') {
  it('returns a product for valid product ids') {
  }

  it(`returns null for invalid product ids') {
  }
}

These statements tell you everything else you need to know about this method, what you can pass to it, what gets returned, and what behaviors it implements. Additionally these tests verify that these statements are true, and the build breaks if anything changes.

Take my earlier example of someone changing a deeply nested method somewhere so that it now throws an exception when the product id is invalid. This change will make the GetById unit test fail, this test failure will prevent the changes from being merged until some remedial action is taken.

The fact that the unit tests are run automatically by the CI process means that the statements they make can be relied upon, and are therefore useful. The fact that comments are not verified at all makes them unreliable and therefore useless.

My recommendation: pay close attention to naming things and write good unit tests. Never write comments to describe how the code works. The only place where comments make sense are to document a shared library or an API, but these comments are for consumers of the library or API, not for the maintainers.

bikeman868
  • 879
  • 6
  • 9
  • I don't know, I had to read a code some developer created and there are no High-level comments such as C# `` and having similarly named classes and absence of high-level documentation I could do with some high-level description. There are no unit tests. But why I should go to a completely different part of the project (tests) to find about the behaviour of the class. Also enigmatic acronyms in the code as function arguments etc. don't help. So I postulate that high-level summaries make sense, along with special cases and caveats – Piotr Golacki Sep 15 '22 at 14:47
  • 1
    I totally agree with the high level summaries. The interesting aspect of documenting software is that the highest level of documentation is the smallest/quickest to write, the most useful, and requires the least maintenance. The deeper you go into the detail the more likely it is to need updating with each release, there is much more of it to write, and it's closer to the code and therefore easier to discover for yourself (less useful). Its not often that the highest value has the lowest cost! – bikeman868 Sep 21 '22 at 22:52