#begin

We will continue our discussion about comments with another chapter about comments. This time Prof. Ousterhout will give some advise on how to properly write them. Let’s see. And, well prepare for a rant.

 

Comments should describe things that aren’t obvious from the code

This immediately triggers me; I still feel like good code is self describing and writing lots of comments is, as Uncle Bob says, a failure to express ourselves in code. But I think we have to assume, from now on, that with comments, John means summaries in a C# context. If so, I agree. So let’s from now on assume he means summaries instead of comments. Because then I think the upcoming chapters make much more sense and I don’t have to feel like I disagree that much.

So as the title of the chapter implies, comments should describe things that aren’t obvious from the code. I think the example he gave with the substring implementation is a very nice one. It was about the indexes being inclusive to the algorithm. I think, I you have used some random number generator like system.random, or unityengine.random you have ran into the issue where you thought the limit was inclusive or exclusive. Summaries would serve as proper documentation here to provide the caller of the code with this information since intellisense would pick this up and show you in your IDE.

John says that often, low level details are not so obvious. And I agree. High level concepts are easy to describe when you follow domain driven design concepts, but low level implementation details might require some extra documentation that may be written as summaries straight in the source code. So, yeah; he’s totally right. He also says that these comments can lift the level of abstraction because if the comment is good, you don’t need to read the code. And, yes I agree again. When you write proper, informative summaries above functions you don’t need to read the implementation. I do however still think that, the function name should also reflect properly what the implementation is doing. But, as with the substring example, sometimes it’s really nice to add just that little meta-data about what the function does. This makes it easier for a developer to discover what the function does and thus also reduce the cognitive load.

He then says something really interesting and I quote: “Developers should be able to understand the abstraction provided by a module without reading any code other than its externally visible declarations”.

This implies that developers aught to provide summaries for public API’s. Which makes perfect sense by the way. I think the perfect example for this would be OpenAPI documentation for describing web endpoints. I think if you have ever needed to implement some WebAPI, you probably know what Swagger is. It’s this really nicely visual and even functional Web documentation format. Prof. Ousterhout says that he will teach you to write good comments in this upcoming chapter so let’s dive into it and find out what his advise is.

His first piece of advise is to pick conventions so that all your comments are the same format and everyone knows how to write them. And he starts of by saying something I expected, I could not remember this anymore but now it all makes sense he actually says this, and I quote: “If you
are programming in a language for which there exists a document compilation tool, such as Javadoc for Java, Doxygen for C++, or godoc for Go!”

This is exactly what we have been assuming. We assumed John meant, summaries, when he was talking about comments and now he says it himself. So just to make things clear. Summaries are these comment blocks in C# with three front-slashes which say <summary> in sort of xml tags. You can provide meaningful information, or comments in between the opening and closing tags. These summaries are generated when you type three slashes above properties, fields, methods and class definitions. And when you define a summary you also have the opportunity to document each parameter to function, it’s return value and even the type of exception it throws.

These summaries will then be shown by intellisense when you are programming and are about to use or call something on that class. So your IDE will show a little popup with the information that’s in the summary. This is really helpful, especially with things like inclusive indexes for substring and random functions.

So I think I can fully agree with John about this. This will indeed also debunk the 4 main excuses not to write comments, or should I say summaries, which we talked about in the previous chapter. So let’s continue with the advise to pick conventions.

John says that conventions serve two purposes; First they ensure consistency and second they help ensure you actually write them. If you don’t have a clean idea of what you are going to comment you won’t actually write them, and then you end up with no comments at all. He says that comments fall into on of the following categories, and he lists four; First is Interface, these comments would be the summaries for methods. They describe the interface of the class, how the functions work and what the parameters mean. Like if indexes are inclusive or exclusive.

Second is Data structure member, these would be the summaries above properties or fields. They would describe what these member variables or public facing properties mean. Just remember not to add a summary saying Player above a property names player. That does not provide any new meaningful information so you might as well just remove the comment. Don’t be dogmatic about writing summaries everywhere because you will end up with these redundant comments simply repeating the implementation.

Third is Implementation comment which is a comment inside the code of a method or function, which describes how the code works internally. Hmm now I disagree again. These seemingly random in-lined comments, aren’t summaries and thus will not show up through intellisense and thus you’ll probably not read them any way. These are the comments that will get outdated, spread lies and misinformation. I’ll probably delete them as soon as I can, and if they provide some meaningful implementation details I’ll add that information to the summary. But that does not happen often, most of the time I’ll just delete these randomly inlined comments.

Fourth is the Cross-module comment which are comments that describe dependencies that cross module boundaries. This sound like something that is very useful, but I don’t know where you would add these kinds of comments where they make sense while programming and invoking intellisense. This kind of high level documentation aught to be visualized in diagrams on your documentation wiki or atlassian confluence for example. Should this really be in the code, not if you ask me I think.

He says however, that the most important categories of comments fall into the first two which where interface and data structure comments. So summaries above classes, methods and their properties and variables. He also says that implementation comments are often unnecessary, which I totally agree with and these cross-module comments are very, very rare because they are problematic to write. John says he’s explain why in a later section of this chapter. I think that’s going to be pretty interesting because I cannot remember what these comments aught to be.

So let’s start with his first advise on how to write good comments, and this is right up my alley because I agree completely, and as a matter of fact I just talked about this and I think Uncle bob will even agree on this and it’s “Don’t repeat the code”. Yeah, as Uncle bob said in Clean Code, comments that repeat code are redundant and worthless. Even when you use them for generation purposes with tools like doxygen they are worthless. If you have a comment that just repeats the code then doxygen will be able to do the exact same thing and you will end up with the same result anyway. So if you do not add any comments, doxygen will generate documentation, which just repeats the code, which makes sense because that’s easy to generate.

He says something interesting about this as well and that is that, if you simply repeat the code in the comments, you are roughly on the same level of abstraction and comments that reside roughly on the same level of abstraction are rarely useful. I think that is a really nice observation and he’s right. It makes total sense to provide comments that are on a slightly lower level of abstraction so you can provide the reader with just that little bit of extra information without him having to read the code.

He then raises the first red flag in a while and it says, and I quote: “If the information in a comment is already obvious from the code next to the comment, then the comment isn’t helpful. One example of this is when the comment uses the same words that make up the name of the thing it is describing.”.

And a fist step to provide better comments is to use different words in the comment fro those in the name of the entity being described. You should carefully choose words that provide additional information about the meaning of the entity you are commenting rather than repeating it’s name of implementation.

Next up he continues with something I hinted at just a minute ago and that is that: ”lower-level comments add precision”. This makes it easier for a user or reader of the code to understand it. If these comments, describing the implementation of something at just a slightly lower level of abstraction, pop up during development though intellisense, they can provide a low more ease to programming some game itself. Prof. Ousterhout basically says that comments augment the code by providing information at a different level of detail. And, this is indeed something good comments do. I think Uncle Bob agrees with this as well, although he’s really fanatic about removing comments from code that do not belong there. But in Clean Code he points out that comments used for generating documentation are accepted.

So good comments provide precision by clarifying the exact meaning of the code. Plus they also offer intuition, such as the reasoning behind the code. Comments that are on the same level of abstraction are likely to repeat the code, which is not what we want since they are useless anyway.

He says that precision of most useful when commenting variable declarations such as member variables, method arguments and return values. Comments can fill in missing details about things like: What are the units for this variable? Are the boundary conditions inclusive or exclusive? If a null value is permitted, what does it imply? If a variable refers to a resource that must eventually be freed or closed, who is responsible for freeing or closing it? Are there certain properties that are always true for the variable (invariants), such as “this list always contains at least one entry”?
You could of course figure all of this out by reading the code but that would take more time. And I somewhat agree since most of the time I would avoid for example allowing null to be used as a parameter to a function. Plus I would try to abstract away side effects like freeing up or closing resources. But, hmm, yeah, John programs a lot in C++ which I think is the reason that he put this in. In C++ of course you have to do a lot more resource management than in C#, because C# is as managed language. We have a garbage collector. But then again, if you open a file stream and don’t close it you can still run into these problems, but I would still try to abstract that side effect away. But things like documenting units or boundary conditions like inclusive or exclusive are generally nice to add as comments, although, units of a variable might also be part of your domain model so simply using some object will imply what unit’s it has. But alright, I agree that for example, if you have a some kind of stopwatch function, you could add a summary saying what the unit of the timing is, is it milliseconds? Or simply seconds? So would you supply the number 1000 or simply 1 to the function. That would be nice to see in your IDE through intellisense, right?

Prof. Ousterhout does say that you should keep an eye out for comments that provide vague details about the entity that they are describing. Remember that one of the reasons to add comments is to add precision, so be precise in your comments. He says you should be thinking about nouns and verbs while commenting a variable. In other words, focus on what the variable represents, not how it is manipulated.

Next up is another section about what good comments do and that’s; “Higher-level comments enhance intuition”. This is the second way good comments can be helpful, so comments describing lower level concepts promote precision, but comments that describe higher level concepts improve intuition. These comments omit details and help the reader understand the overall intent and structure of the code. John says that these comments are commonly used for comments inside methods and for interfaces.

That’s pretty interesting, he says that comments describing high level concepts are often used inside methods, however inside methods you are often dealing with lower level stuff. But commenting interfaces with some high level summary comments that describe the abstraction I can understand. Also again because they show up in your IDE. But let’s continue and see what else he has to say. 🙂

And he says that high level comments are more difficult to write than low level comments because you must think about the code a certain way. Yeah, I think I agree, describing things in abstract matters can be difficult to put on the screen. However, having made a proper domain model will help you out with such comments since you can talk in your defined ubiquitous language.

And Prof. Ousterhout also says that high level comments are difficult to write because, we engineers, tend to be very detail-oriented. We like details, and managing lots of them. It’s essential for being a good engineer. Haha, great I love this. He’s right! He then also says that great engineers also have the ability to take a step back and think about the details of the system at a higher level. This is totally true. We as engineers are certainly obsessed with details, but we also need to create proper architecture and design, thus we need be able to switch perspectives rapidly.

So a good comment that describes high level concepts does two things: 1) it provides an abstract description of what the code does, and 2) it describes, in high level terms, why the code is executed. I think these are two very valid arguments for high level comments you might add to summaries of classes, interfaces or even their methods. I just, still.., can’t see why you would inline such comments inside functions. In the book John shows some examples and they are all multi-line comment blocks inside a function. These are the comments that do not show up in documentation or your IDE. So, you must read the code anyway to find out what it does. These are the kinds of comments I usually just delete, or rewrite and put them as summary to the function if they make sense. But alright.

Next up is another type of good comment and that is: “Implementation comments: what and why, not how”. These are the kinds of comments that only appear inside methods to help readers understand how they work internally. Hmmm, this is what I talked about just a second ago, and these are the comments I usually delete as soon as I see them. Since, how cliche, I think that code should still be self documenting, and these inlined comments will get out of date in a large team. When we use automated refactoring tools code might get refactored in many places but the act of refactoring will not change the comments, so they will become out of date because of it.

John says that the main goal of implementation comments is to help readers understand what the code is doing, not how it does it. I think this is generally how comments should be written. If you understand the what and the why of the code, you can easily find out how it does it by reading the code since, well, we are programmers and we understand code. But sometimes the what and the why might be vague and we might not know why we implemented some feature in the first place.

He gives an example of a comment that describes what happens in each iteration of a for loop. I personally heavily disagree here. This seems like nonsense to me. These are the exact comments, Uncle Bob will tell you to delete quickly because of reasons we talked about earlier. Prof. Ousterhout follows this up with some rather interesting concept and that is you might provide an some extra comments when you fixed a bug in the system, but the code to fix the bug is not entirely obvious. You might want to add a comment there saying this particular obscure or unclear code is need to circumvent this or that scenario. I think this might generally be helpful, yeah. But then again, I would always try my absolute best to refactor the code in such a way that the bugfix makes sense from just a code point of view and I don’t need to comment. But there are these cases that you can’t and thus you commit the crime of writing an inlined comment.

He then say that for larger methods, you might want to add comments for the local variables in that function, and you focus your comment on what the variables represent, not how it is manipulated in the code. This again, highly contradicts the advise in clean code, right? First of all, clean code tells you not to write large functions, separate them out and extract smaller private methods that level the abstraction level. And second, Uncle Bob does not want you to use inlined comments to describe local variables of a function. Use self-describing names and make your code readable that way. Remember the chapter about naming things as well, choose names that best describe what the code represents, names must be intention revealing. This is exactly what John says you should do through comments, why not do it in code directly? Also note that, for local variables in functions, you can choose a long name if that feels right. These variables are not exposed outside the function so you can make them as long and descriptive as you can. Remember Uncle Bob’s advise that the length of a name corresponds to the length of it’s scope. So for variables a short name is only used in a small scope, yet a long name is used in larger scopes. For functions it is the other way around, use short names when functions are used often, and use longer names when they are used less, like private methods.

And then there is the last type of good comment in this chapter and those are comments that communicate cross-module design decisions. Prof. Ousterhout says that in a perfect world every design decision would be encapsulated within a single class. But that does rarely happen. In OO we often separate things and sometimes the nature of the problem is something distributed. Think about network protocols. They are implemented in both the sender and receiver, which are probably in different places. These kinds of design decisions are often important, subtle and complex and account for a good portion of bugs. So good documentation for this is crucial.

And Yeah, I totally agree. Especially with the example of network protocols, or API’s for example. Think about the massive amount of value the openAPI specification brings? And implementation of openAPI would be Swagger. This is really, really nice documentation. Yet, it does not rely on comments persee. You can extend openAPI docs with additional comments but you rarely see that happening. But then again, since web API’s are implemented at some place, and consumed at potentially many other places you really require this documentation. So I totally agree with John that this needs to be documented somewhere, may that be in somekind of openAPI spec, wiki’s or confluence for example. I’m not really sure if I would document this in comments, because, once it’s deployed, you cannot read the comments, but let’s continue with the book and find out.

He says that the biggest challenge with cross-module documentation is finding a place to put it where it will naturally be discovered by developers. Great, he confirmed my suspicion. I feel comments would not be the best place for this kind of documentation.

He gives an example of some system he was building where the system defines some status value. These values were implemented as an enum and thus, when you needed to check what they meant or when you needed to add a new status you went to the enum file and there was the comments describing everything, plus a little guide on what steps to take to implement a new status.

This seems totally reasonable to me. I do add summary comments above enum values often. So I agree with this example. I also want to point out that we’ve had a system with a similar problem and we also put comments in the code for documentation purposes. But they were for ourselves, and not for consumers of the code since they would get a compiled binary and not the source code. But we were building a Unity3D game which ran on a massive screen 4K screen, with stereoscopic rendering, in an amusement park. Players would be able to remote-control them from tablets that were mounted on the cart they were sitting in. We implemented the communication between the ride itself, and the tablets of the players and the game running on some server-rack with UDP. UDP is a fast, yet unreliable network protocol to send messages back and forth. So each message would have a specific identifier based on some bytes and those were the things we commented in the code. But we also documented them based on the specs we got from our partner who actually build the ride. These docs were in some pfd I think. Having the messages types commented in the code meant we did not have to open and search for them in that massive pdf, which improved the speed at which we could work.

Prof. Ousterhout then explains that still these cross-module comments are awkward in their use and I agree. I think they should be documented elsewhere, like your wiki, confluence, or some pfd as I described in the example just a second ago. He also says that, if you document it elsewhere, developers might not read it. And I agree again. Developers will probably only read the docs, when they get stuck. So I think you need to communicate with the developers and tell them to actually look at the file. Maybe a way to do this is to add a large disclaimer in the README.MD file of your system which points to some wiki. I think, nowadays having a proper README file in your project is really required for good communication. So in your README file you can refer to some external docs. I think, README’s are generally read by developers since they also often include information about install procedures and some simple examples.

John also hints at this. He says he has experimented with adding single file to a repo which is called Design Decisions and documenting all this stuff in there. This makes the documentation be in a single pace, which is very useful. He does say however that, when you do this the docs are no longer with the code and thus you have the risk developers wont read them. So, I think you should rename that design decisions file to README.MD and be done with it. Also remember that markdown supports adding links to other files in your repo or external sources so you can point at other sources very easily.
So to wrap this chapter up; The goal of comments is to ensure that the structure and behavior of the system is obvious to readers, so they can quickly find the information they need and make modifications to the system with confidence that they will work. John has described four types of comments that are useful and I agree with him on these concepts in a general sense. But on some of the details I don’t.

But alright let’s step back a little bit. I think, when we assume that many of the comments he’s talking about are actually summaries on fields, properties, methods and classes then I agree with most of what he said. I just think that we have far better resources for actual documentation of games or software programs. I mean, you are going to put documentation about some system in your game design document, not in the source code itself. This might include really complex algorithms for game systems and other things.

I also would like to point out that, although practices in Clean Code advise you to delete many types of comments, there are still comments that are deemed to be acceptable. These comments include; comments that explain rationale and intent, Legal comments and comments that are used for documentation generating purposes. So I think, although there are many contradictions in these two books; Clean Code and A Philosophy of Software Design, there is some middle ground here where I would agree with both parties and I think Uncle Bob and Prof. Ousterhout might agree as well.

We also need to consider the fact that John speaks about C and C++ in his book, and a bit of Java here and there. But I think the majority is targeted at C and C++. These languages are one a lower level than C#. For example, in C and C++ you need to manage your memory and you do a lot of pointer arithmetic. You can do this in C# as well, but generally we don’t. I think, C and C++ require a bit more documentation because of this fact. In these low level languages you have a lot more details to manage and thus the code might not be as obvious as a higher level language as C# for example. So if you are managing buffers, pointers and memory, you might add comments to this code to explain what these things are doing, and why you are doing it.

So, that’s it for this blog and excuse me for ranting this much. I hope I did not scare you away and you join me next time again. Cya!

#end

01010010 01110101 01100010 01100101 01101110

Hey, sorry to bother you but you can subscribe to my blog here.

Never miss a blog post!

You have Successfully Subscribed!