How many of you write self-documenting code?

Started by
63 comments, last by SimonForsman 8 years, 9 months ago
I try to write self-documenting code, so far as it depends on me.

Unfortunately I have to work with other people, systems written years ago that have severe cruft issues and bolted on bandaids from years of 'must ship now' bugfixes, and external APIs whose authors have questionable coding practices - or who simply cannot write clean APIs due to language limitations (e.e. most C APIs)

So yes, I will sprinkle several comments in my code where they increase readability. In some cases this ends up being a "what this code is doing" comment, but in most cases it's a "we have to do it this way because X" or "this has to be in this order because of dependency X, Y, and Z". Heck, sometimes I've even run into a compiler or library bug (yes, they do exist, in fact I ran into one yesterday) and so have to put in a comment saying "this looks ugly/isn't what you expect, because of bug documented at X".

I write shipping code, not academic code. And shipping code needs comments smile.png

Oh, and a lot of template code needs comments. I challenge you to write maintainable template meta-programming code in C++ that is readable by a non-expert (or even yourself in six months) without comments.
Advertisement

I write self-documenting code and I'm always surprised that many times professionals fail to do so (based only on your comments though, as I rarely work with programmers).

I don't have autocomplete but with descriptive names I don't really need to as I rarely make typos and don't have to remember funky names because the names are descriptive.

Making descriptive names also forces me a bit to think over what I'm doing instead of just jumping in.

My biggest problem with comments are that they easily get disconnected/outdated from the thing the are describing and that many times it's harder for me to word these comments that coming up with the actual code.

In the OP's example, the choice of "better" variable names are actually equally as useless as x, y, i, j2 or whatever. Take "phrase" as an example. That's a useless variable name because it's saying what it is rather than what it does. Phrase might as well have a value of "hello", "hello, hello, hello", "you never listen to a word that I say", or "down with supporters for abolition of opposition to antidisestablishmentarianism" and the name is equally discriptive in all cases: i.e not one bit descriptive at all.

This kind of approach is dangerous (and has similarities to the dreaded Systems Hungarian). It's sufficiently verbose to give a fuzzy feeling that you're doing things right, it may even convince others that you're doing things right, but it's still useless. Call it "greetingPhrase", "oneTimeOnlyGreetingPhrase", or something that actually describes what it does instead.

Even so, it's still not self-documenting. Under what conditions do you want to use a greetingPhrase? What conditions negate it? Which gnarly bug was fixed back in 2007 that this horrible line of code relates to? Meaningful variable names can't replace that kind of information.

So: choose good variable names and comment properly.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

Lots of great discussion here. If I were to summarize your points for everyone,

1. Use descriptive, but not ridiculously long names for your variables, functions, and classes.

2. Use the Single Responsibility Principle/Separate your code by functionality

3. Get a copy of Clean Code. Enjoy it many times.

4. Never ever [ever] use single-character names to represent data unless it's for the purpose of iteration.

5. It's OK to comment the things that require documentation (ex. headers), but commenting every other line of code can make the source a little convoluted.

In the OP's example, the choice of "better" variable names are actually equally as useless as x, y, i, j2 or whatever. Take "phrase" as an example. That's a useless variable name because it's saying what it is rather than what it does. Phrase might as well have a value of "hello", "hello, hello, hello", "you never listen to a word that I say", or "down with supporters for abolition of opposition to antidisestablishmentarianism" and the name is equally discriptive in all cases: i.e not one bit descriptive at all.

This kind of approach is dangerous (and has similarities to the dreaded Systems Hungarian). It's sufficiently verbose to give a fuzzy feeling that you're doing things right, it may even convince others that you're doing things right, but it's still useless. Call it "greetingPhrase", "oneTimeOnlyGreetingPhrase", or something that actually describes what it does instead.

Even so, it's still not self-documenting. Under what conditions do you want to use a greetingPhrase? What conditions negate it? Which gnarly bug was fixed back in 2007 that this horrible line of code relates to? Meaningful variable names can't replace that kind of information.

So: choose good variable names and comment properly.

I just used " phrase " versus " p " in a generic scenario to illustrate the difference between the two names.

If you would like an actual example here is a code snippet from one of my textmode games. The first is my actual line of code. The second is a more common variation.


CHAR_INFO screen_buffer[SCREEN_HEIGHT][SCREEN_WIDTH];

CHAR_INFO scrBuff[80][80];

.

Choose Descriptive / Unambiguous Names

Names have to reflect what a variable, field, property stands for. Names have to be precise.

.

Check out this quote from a Clean Code Cheat Sheet . There's a wealth of other good information there.

Ah, the dreaded topic.

I always write complete names, although there is a limit to my sanity of how long I can tolerate them. The above example is a little too pedantic, I agree. I wouldn't write more than three-word-long variable names. So sum_of_integers_raised_to_a_power would either become just `sum` or `power_sum` or something else that would make sense in the context of the algorithm.

Contrary to the popular opinion, I ALWAYS document my code. Why? Because I never want to reread my code. This is the common answer from people who are against documentation: "code should be self-documenting", "just read the code you'll understand what it does". Here's the thing. I don't want to read your code, like ever. To me, if I want to use someone else's or even my own library, I just want to use it. Tell me how to use it. Tell me what params to pass. Don't make me go through your rabbit hole just to see if I should pass 0 or 9 to some function.

So many times have I run into situations where the author just says "oh it will accept any value". Sure enough, as soon as I started passing some params that his library didn't support, he then says "oh, except that, still working on that issue." Really?

I don't want to read your code, or even reread my own code. I blackboxed my code all the time. Write it once, test it thoroughly, document all the limitations, package it. Done. I don't want to reread it to find out what's happening, because I will forget months down the road. I would rather read a simple summary written in plain English, rather than follow the code's logic as deep as hell itself. It's faster and more efficient.

Documentation of code is a separate thing from commenting code in my mind.

Documentation is of the API, comments are on the implementation.

Of course APIs should be documented.

Not necessarily with comments on each and every method though.

And the worst is when you can't get an overview because it is filled with things like

/*! \brief getter for X.
*
* This method gets X

*

* @return an integer representing the X
*/

int getX();

Most of the API should be self explanatory, and then you can complement it with _brief_ comments about special cases and gotchas, and maybe a usage description of the entire class/api, with some examples of usage. If in the file, collected at one place at the top.

Of course you shouldn't need to read the implementation to understand _what_ it does, that should be obvious from its name, its return value, and the name of its parameters. You read the code if you need to know _how_ it does it, or need to fix bugs or modify it.

If there is limits on what you can send in, it should either be designed with language constructs that enforce the limits, or clearly documented and asserted.

I generally do. Otherwise I come back to it in six months and have no idea what's happening. Even worse are typedefs, macros and sometimes templates. It can really be difficult to fathom what's happening if they are overused or misused.

This is my thread. There are many threads like it, but this one is mine.

I generally do. Otherwise I come back to it in six months and have no idea what's happening. Even worse are typedefs, macros and sometimes templates. It can really be difficult to fathom what's happening if they are overused or misused.


Agreed on macros and templates, but can you give an example of a case where a type alias would make your code less readable? In my experience type aliases can enhance readability as well as adding some semantic value that helps understand what the code is supposed to be doing. Especially when it comes to using (never mind implementing) complicated templates:

void IntegerFunction(int x);
void FloatFunction(float x);

// ComplicatedTemplatedThing is actually simpler than some real-world production code I've seen used with this pattern...
typedef ComplicatedTemplatedThing<int, int, IntegerFunction, std::string, EnumType> IntThing;
typedef ComplicatedTemplatedThing<float, float, FloatFunction, std::string EnumType> FloatThing;

IntThing foo;
IntThing bar;
FloatThing::Subclass wololo;
I personally try to write self-documenting code where I can, with comments mainly used to explain design/implementation decisions. Since I'e been using a lot of type aliases recently specifically to do that I'd like to see what's good to avoid...


Since I'e been using a lot of typedefs recently specifically to do that I'd like to see what's good to avoid..

With C++11 typedefs are a bit obsolete, using has a clearer syntax and can do everything typedef can do, and more (specifically, it can be templated, which can simplify syntax even more, if you need it)

I (and Scott Meyer) would suggest avoiding typedefs all together, unless you have to support C++98.

Since I'e been using a lot of typedefs recently [background=#fafbfc]specifically to do that I'd like to see what's good to avoid..[/background]


With C++11 typedefs are a bit obsolete, using has a clearer syntax and can do everything typedef can do, and more (specifically, it can be templated, which can simplify syntax even more, if you need it)

What if you're stuck with a compiler (eg. VS2012) that doesn't support using?
I did mean type aliases in general, though. Fixed above. smile.png

This topic is closed to new replies.

Advertisement