21

I want to document my code such that there is minimum need for reading and browsing the code again months later.

I know that there are different types of documentation (in source code and outside, sequence diagrams, and so on).

I just want to know what is an efficient way to document my code so that, when a few months later I want to see my code, I spend less time on reading code and understanding code flow.

6
  • 44
    The best method for spending less time reading code later on is to write clearer and more understandable code.
    – Mael
    Mar 8, 2018 at 6:43
  • 2
  • The documentation depends on the target. If you are addressing to developers, then comments in the code be quite useful. If you are addressing to analysts, diagrams of the overview are useful too. If you are addressing to tech-savy audience, make a user guide.
    – Laiv
    Mar 8, 2018 at 7:20
  • @Laiv Well in view of a developer of my own code and maybe other developers code. Mar 8, 2018 at 7:57
  • Biggest thing is to keep the code small. If the code needed to implement an item in your issue tracking system is large, then your team may need to learn how to break it up some more so the amount of code reviewed isn't overwhelming. Mar 8, 2018 at 21:17

6 Answers 6

16

I must admit I do not agree with some of the things that the other answers recommended, so I'm going to throw my two cents;

Comments

Documentation is extremely helpful for strangers reading your code. Usually many things will not be verbose enough to be read and understood immediately, and you should then explain what you are doing.

Edit: the discussion in the comment section has pointed out something right – over-commenting is usually done when writing bad code.

Commenting your work should be precise and minimal, but, in my opinion, should definitely be present. At least a comment for every 15 lines of code. For example, on top of blocks on code, add a line about what you're doing:

def login(username: str, password: str, create_session: bool = True):

    # Filter the user we need from the database
    hash = md5(password)
    users = db.table("users", db_entities.USER)
    results = [x for x in users.query(lambda c: c.get("username") == username and c.get("password_hash") == hash)]


    if len(results) == 0:
        return None, None
    else:
        # Create a login session record in the database.
        if create_session:
            sessions = db.table("sessions", db_entities.SESSION)
            ses = sessions.new()
            ses.set("username", username) \
                .set("expiery", 31536000 + time.time())
            sessions.update(ses)
            return results[0], ses
        else:
            return results[0], None

Minimal comments that explain why and what you're doing are very helpful throughout the code. I do not agree with the answer that states

If I come across code containing comments, I prepare for the worst: the code is likely to be bad, and to be honest the comments are likely to be bad too.

Many times, gracefully, good code is documented. It is true that bad programmers see their documentation like "Alright, my code is bad, let's add a few sentences to make it clearer".

Yes, and while this occurs quite a lot, it is also true that good programmers that write clean code also want to make sure that they return to their code and understand why they want their function to behave like that, or why did they need that line that seems a bit redundant, etc...

Yes, comments that explain obvious things, comments that are unclear, comments that were just put together to make sure that "this code is documented, yeah, whatever", are code smell. They make reading the code harder and irritating. (Adding an example below)

# Logging into Gmail when the module is imported
_client = login()
def get_client():
    global _client
    return _client

Example clarification: "No shit, Sherlock. does _client = login() log into the mail service? OMG!"

More clarification: the login() method has no relation to the login() method from the above example.

But comments that do match the standards, explain the why's and not the how's, and answer the right questions, are very very (very) helpful.

Inline comments

One thing you should NOT (and if I could write that bigger, I would) do, is write your comments in the same line of the code. It makes comments very line-specific, which completely misses the purpose of commenting your code.

For example, bad inline comments:

outer = MIMEText(details["message"]) # Constructing a new MIMEText object
outer["To"] = details["to"] # Setting message recipient
outer["From"] = "xAI No-Reply" # Setting message sender
outer["Subject"] = details["subject"] # Setting message subject
outer.preamble = "You will not see this in a MIME-aware mail reader.\n" # I don't know what I'm doing here, I copied this from SO.
msg = outer.as_string() # Getting the string of the message
_client = details["client"] # Assigning the client
_client.sendmail(SENDER, details["to"], msg) # Sending the mail

Would be much easier to read and understand this code without the comments, that make it messy and unreadable.

Instead, comments inside your code should be placed above blocks on code, and they should answer the important questions that may arise while reading the code block.

# Constructing the email object with the values 
# we received from the parameter of send_mail(details)
outer = MIMEText(details["message"])
outer["To"] = details["to"]
outer["From"] = "xAI No-Reply"
outer["Subject"] = details["subject"]
outer.preamble = "You will not see this in a MIME-aware mail reader.\n"
msg = outer.as_string()

# Sending the mail using the global client (obtained using login())
_client = details["client"]
_client.sendmail(SENDER, details["to"], msg)

Much clearer, right? Now you also know that you have to use the login() function and provide the parameters to send_mail() with everything you used. Helps a bit, but one thing is still missing.

Function documentation

Has been widely discussed. You should always let your readers know what your function is about, why and what it does. How it does that, this does not belong to the documentation, but maybe to footnotes of the function.

You should clearly describe what you expect your parameters to be, and if you want them to be obtained/created in a specific method. You should declare what your function should return, what its use is, etc.

Again, that's my opinion and methodology while writing my code. Not only those, but those are just some of the things I could not agree with the other answers about. Oh, and of course, not just the comments read your code out, but your code itself. Write clean code, understandable and maintainable. Think about your future self while coding ;-)

18
  • 6
    Disagree with a given example - instead of writing many comments in one huge function, you should compose it from many smaller functions with descriptive names, that will act as comments. Without a risk of being out of sync with what the code is really doing.
    – user11153
    Mar 8, 2018 at 14:06
  • 6
    Finally some sanity. Extracting every piece of code that could use a comment into its own function is how you end up with thousands of functions spread across hundreds of files. Mar 8, 2018 at 15:45
  • 2
    That second example is lovely. Mar 8, 2018 at 19:20
  • 7
    The comments in the second example are much too verbose. Some of them (e.g. "Did we find anything?") just repeat what the code says and would be better removed. For other, you could gain more readability by refactoring, like making (stream.is_empty()) the loop condition, or moving the check for accept_literals outside.
    – Frax
    Mar 8, 2018 at 20:10
  • 3
    @cpburnz, "I've had to dig through too many legacy projects and third-party libraries without any code comments to appreciate comments that explain what's happening and why". exactly my point all along: comments are there to explain crap code. Since the question is "how do I write code that easy to read" then clearly this answer is wrong as it focuses on writing comments to explain bad code, rather than writing good code in the first place.
    – David Arno
    Mar 9, 2018 at 9:16
57

IMO the best documentation is the documentation you don't actually need. I also hate writing documentation and comments.

With that being said:

  • Pick readable and talking names. Don't use n, but instead numberOfItemsFound for example.
  • Don't shy back from storing parts of a calculation in a constant variable rather than pushing everything into one line.
  • Move partial tasks from branches into their own (inline) functions, if you're reusing them or the parent function becomes long and tedious to follow.
  • Be more elaborate and only optimize code over readability where it's really required.
8
27

Treat your code as documentation

Your code is your primary documentation. It precisely describes what the resultant app, library or whatever, actually does. As such, any attempts at speed up the understanding of that code has to start with the code itself.

There's lots written on how to write readable code, but some of the key points are:

  • do not rely on comments to explain bad code, make the code better and get rid of the comments,
  • write short focused functions, methods, classes etc,
  • use names appropriate to the context (eg n is good for a loop, longer descriptive names are needed for items with greater scope),
  • treat function names as if they were comments, eg don't use UpdtTbl with a comment explaining it updates the table with the supplied rules when UpdateTableContentsWithSuppliedRules can be used as the name,
  • avoid mutability. Every time you change the contents of a variable, you increase the complexity of the code. Assign that new value to a new variable (with a good name) where feasible.
  • lastly, and most importantly, avoid "clever" code. The only real clever code is code that is easy to read. If you write some complex bit of code and find yourself think "wow, aren't I clever here?", the answer is almost guaranteed to be "no, you aren't".

Become better at reading code

Reading code, regardless of how simple it is, is a learned skill. No one is naturally good at reading code. It takes practice; lots of practice. So, for example, go to Github or whatever and read the code of the libraries that you use, rather than just using those libraries. Find code to read and read it.

Comments are a code smell

Only then do we get to other types of documentation. Firstly, as previously stated, avoid comments. If I come across code containing comments, I prepare for the worst: the code is likely to be bad, and to be honest the comments are likely to be bad too. Someone who can't communicate well through code is unlikely to be able to communicate any better through natural language.

Beware autogenerated API documentation

Also, beware autogenerated API documentation. If I have to resort to reading such docs, it'll be because your code is so hard to read. Again, make the code simple and I can read that directly.

Tests are docs, too

Tests are documentation too. So don't treat your unit tests as a chore. Treat them as a way of communicating with others (your six month's later self being included here) as to how the code works and is intended to be used.

Draw pictures if it helps

If you like UML, then by all means find yourself a good tool and generate UML diagrams from your code. Just never ever ever ever try to use it to generate code. It's not good as a design tool and you will end up with horrible code as a result.

Have a "1000ft" view document

Finally, write yourself an overview document. What does the app do? How does it do it? What other systems does it connect to? Things like that. Do not attempt to describe the code structure here though. Let the code do that. Let this document remind you why you wrote the code in the first place.

19
  • 15
    I agree with all your point, except that comments do have their place. While I agree there's no point in comments like add 1 to i, comments should explain why the code does what it does. For example, the code if (!something.Exists()) {...} can use a comment like: // something exists only when (explanation of the broader scenario).
    – Jonathan
    Mar 8, 2018 at 12:58
  • 17
    We've all seen our fair share of // increment x x++; comments that are of no use, but it's wrong to throw the baby out with the bathwater and declare that comments are always bad. For example, comments of the form // this case should never happen because xyz throw exception "unreachable".
    – angrydust
    Mar 8, 2018 at 14:48
  • 7
    Very nice list. But like @Jonathan. I do not agree with the comments. Some times you have to account for bugs in a third party frameworks though. While this can be refactored into its own function it's still nice to leave a bit of description to why the workaround (bugnumber or bugname/description of the bug) is required.
    – magu_
    Mar 8, 2018 at 16:34
  • 17
    @DavidArno But you cannot do that for a comment explaining why something was not done. Like //XXX: Not using straight-forward method Foo here because .... Such comments can be immensely valuable, but are impossible to convey with code for obvious reasons. Mar 8, 2018 at 16:37
  • 7
    I like it even more dramatic: every comment is a failure to express yourself well in code. For example, I have a 4 line comment in one method, explaining the workaround for a 3rd party bug. I failed to express that well in code, so it's in a comment. I'd say it improved readability tough, because I doubt anybody would enjoy scrolling horizontally to read a very long and very descriptive method name. "Comments are a code smell" - yes, but we have to remember that not everything that smells is sh*t.
    – R. Schmitz
    Mar 8, 2018 at 18:18
5

Provide a cover letter

Unless you are in a very technical domain, most questions around the code will not be about the 'how' but about the 'why' or the 'what'.

As such, the way to reduce people from having to look in your code, is to write a short description of it. The advantage of this is that you can compile an overview of descriptions quite easily, and that this is much more accesible. (Even to people who won't/are not allowed to see the code).

Even if people are technical, the cover letter should offer guidance of where they should be looking for something.

Simple extremely minimalistic points:

  1. Introduction, why does this code (base) exist
  2. What function does the code subset fulfill
  3. Where is the code (script name for instance)

Example

  1. This set of scripts scrapes StackOverflow and upvotes answers by Dennis Jaheruddin
  2. a. This script is responsible for parsing the html, and analyze whether it is the right user
  3. a. The script is found at: ScrapeAndVote/RecognizeDennis.scr
1

The biggest speed gain I usually get from building separate commits that each represent an intermediate step that compiles and works.

So if I have to introduce a new parameter to a function in order to implement a specific feature, then there is one commit that does nothing but add the parameter in the declaration, in the definition and at all call sites. Then, the next commit introduces functionality, and the third updates the call sites that make use of the new feature.

This is easy to review, because the purely mechanical changes can be glanced over quickly, and then get out of the way.

Similarly, if you reformat code, that should always be a separate commit.

1

Although there are one or two apparent points of disagreement among the existing answers, if only in emphasis, I'll try to summarise the usual advice in a way that makes clear where everyone's been coming from:

  1. Firstly, write clean code; any other "documentation" will take care of itself after that. Clean code is a whole set of principles to learn in the first place: single-responsibility classes, short methods that do one thing, good variable and method names, better class/type names than these by focusing on metaphors (e.g. call a MultiButtSupporter a sofa), unit tests to indicate requirements, DRY, SOLID, a consistent paradigm and so on.
  2. Code reveals how code works; comments reveal why code works. For example, explain a +1 with "prevents an off by 1 error", or some complicated formula with "derived in this textbook or webpage".
  3. Whatever you've been doing with comments, point 1 above may well achieve that in clean code. See comments as failures/necessary evils, or even lies if over time they get out of sync with code as both are edited. Comments shouldn't compensate for badly written code, because why would comments be written with any more talent or care than the code was?

On the other hand, if anything I probably err too far the other way, almost never using comments. Your code reviewers will let you know if you've got the balance in the wrong place for them, but if you make a conscious effort to follow the above 3-point plan you'll probably be close to their optimum anyway.

2
  • 2
    How is a "prevents an off by 1 error" comment different from a comment that says "the +1 is not a typo" or "I am not aware of off by one errors in my program"? (Useful comments generally relate to something bigger than +1 in source code, or to something outside the source code.) So that still leaves "derived in this textbook or webpage" as a valid and actually great example in your point #2. Then your point #3 seems to suggest that you might be able to express "derived in this textbook or webpage" using clean enough code without any comments; wow, I'd like to see that in action. Mar 9, 2018 at 9:46
  • @JirkaHanika Maybe off-by-one was a bad example. As for 3, what I meant was "each may" rather than "maybe each"; so no, I don't think code alone can clarify such things. (Well, you could try gaussianFromThisTextbookNamesApproximation as a variable name, but that's a bad idea!)
    – J.G.
    Mar 9, 2018 at 10:53

Not the answer you're looking for? Browse other questions tagged or ask your own question.