About Comments and TDD

From《Test-Driven Development with Python, 3rd Edition》by Harry Percival

When I first started at Python Anywhere, I used to virtuously pepper my code with nice descriptive comments. My colleagues said to me: “Harry, we have a word for comments. We call them lies.” I was shocked! I learned in school that comments are good practice?

当我第一次开始使用 Python Anywhere 时,我常常用漂亮的描述性注释来填充我的代码。我的同事对我说:“哈利,关于注释我们有话要说。我们称之为谎言。”我很震惊!我在学校学到注释是很好的做法?

They were exaggerating for effect. There is definitely a place for comments that add context and intention. But my colleagues were pointing out that comments aren’t always as useful as you hope. For starters, it’s pointless to write a comment that just repeats what you’re doing with the code:

他们为了效果而夸大其词。肯定有一个地方可以发表评论来添加上下文和意图。但我的同事指出,评论并不总是像你希望的那样有用。对于初学者来说,编写只是重复您对代码所做的事情的注释是没有意义的:

1
2
# increment wibble by 1
wibble += 1

Not only is it pointless, but there’s a danger that you’ll forget to update the comments when you update the code, and they end up being misleading—lies! The ideal is to strive to make your code so readable, to use such good variable names and function names, and to structure it so well that you no longer need any comments to explain what the code is doing. Just a few here and there to explain why.

这不仅毫无意义,而且还存在更新代码时忘记更新注释的危险,而它们最终会产生误导——谎言!理想的情况是努力使代码具有良好的可读性,使用良好的变量名称和函数名称,并将其结构得很好,以便您不再需要任何注释来解释代码的用途。只是在一些地方解释原因。

There are other places where comments are very useful. We’ll see that Django uses them a lot in the files it generates for us to use as a way of suggesting helpful bits of its API.

还有其他地方注释也非常有用。我们将看到 Django 在它生成的文件中大量使用它们,以便我们使用它们来建议其 API 中有用的部分。

And, of course, we use comments to explain the User Story in our functional tests—by forcing us to make a coherent story out of the test, it makes sure we’re always testing from the point of view of the user.

当然,我们在功能测试中使用注释来解释用户故事——通过迫使我们从测试中得出一个连贯的故事,它确保我们始终从用户的角度进行测试。

一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from selenium import webdriver

browser = webdriver.Firefox()

# Edith has heard about a cool new online to-do app.
# She goes to check out its homepage
browser.get("http://localhost:8000")

# She notices the page title and header mention to-do lists
assert "To-Do" in browser.title

# She is invited to enter a to-do item straight away

# She types "Buy peacock feathers" into a text box
# (Edith's hobby is tying fly-fishing lures)

# When she hits enter, the page updates, and now the page lists
# "1: Buy peacock feathers" as an item in a to-do list

# There is still a text box inviting her to add another item.
# She enters "Use peacock feathers to make a fly" (Edith is very methodical)

# The page updates again, and now shows both items on her list

# Satisfied, she goes back to sleep

browser.quit()

扩展阅读:John Ousterhoudt’s 《A Philosophy of Software Design》摘要:

Writing Comments

  • Why are comments needed?

    • Code alone can’t represent cleanly all the information in the mind of the designer
    • Even if information could be deduced from code, it might be time-consuming:
    • Comments provide clarity that reduces complexity:
      • E.g., make abstractions more clear
  • Comments are still controversial (!)

    • A significant fraction of all commercial code (50%?) is uncommented
    • Excuses:
      • “This code is self-documenting”
      • “Comments get out of date and become misleading”
      • “I don’t have time to write comments”
      • “The comments I have seen are worthless; why bother?”
  • Comments should describe things that are not obvious from the code.

  • Mistake #1: comments duplicate code (see slides)

  • Mistake #2: non-obvious info is not described

    • Lower level details (especially for variables, arguments, return values):
      • Exactly what is this thing?
      • What are the units?
      • Boundary conditions
        • Does “end” refer to the last value, or the value after the last one?
        • Is a null value allowed? If so, what does it mean?
      • If memory is dynamically allocated, who is responsible for freeing it?
      • Invariants?
    • Higher-level information (capture the intuition):
      • Abstractions: a higher-level description of what the code is doing.
      • Rationale for the current design: why the code is this way.
      • How to choose the value of a configuration parameter.
  • Two kinds of documentation for classes and methods:

    • Interface: what someone needs to know in order to use this class or method
    • Implementation: how the method or class works internally to implement the advertised interface.
    • Important to separate these: do not describe the implementation in the interface documentation!
  • Interface documentation:

    • Put immediately before the class or method declaration
    • Goal: create simple, intuitive model for users
    • Simpler is better
    • Complete: must include everything that any user might need to know
    • Interface description may use totally different terms than the implementation (if they are simpler)
  • Example: index range lookup

    • Large table of objects in a storage system, split across dozens of servers

    • Table has indexes for looking up objects by certain attributes (name, salary, etc.)

    • IndexLookup class retrieves a range of values in index order

      1
      2
      3
      4
      5
      6
      7
      8
      query = new IndexLookup(table, index, key1, key2);
      while (true) {
      object = query.getNext();
      if (object == null) {
      break;
      }
      ...
      }
  • Implementation documentation (comments inside methods):

    • For many methods, not needed
    • For longer methods, document major blocks of code
      • Describe what’s happening at a higher level
      • E.g., what does each loop iteration do?
    • Document tricky aspects, non-obvious reasons for code
    • Document dependencies (“if you change this, you better also…”)
    • Documenting variables is less important for method local variables (can see all of the uses), but sometimes needed for longer methods or tricky variables.
  • Documenting cross-module design decisions:

    • Example: network protocol
    • Example: how does the system deal with zombie servers?
    • Challenging:
      • No single rational place to put the documentation (people won’t know where to look for it)
      • Don’t want to repeat everywhere
    • One possible approach:
      • Create designNotes file, with various tagged sections

        • “Zombies”
        • “Timing-dependent tests”
      • In the code, just refer to the design notes file:

        // See "Zombies" in designNotes.

  • To maximize value of comments:

    • It must be easy for people to find the right documentation at the right time
    • The documentation must get updated as the code changes
  • Techniques:

    • Document each thing exactly once: don’t duplicate documentation (it won’t get maintained)
      • Use references rather than repeating documentation: “See documentation for xyz method”.
    • Put documentation as close as possible to the relevant code
      • Next to variable and method declarations
      • Push in-method documentation down to the tightest enclosing context
    • Don’t say anything more in documentation than you need to
      • e.g., don’t use comments in one place to describe design decisions elsewhere
      • Higher-level comments are less likely to become obsolete
    • Look for “obvious” locations where people can easily find documentation (see Status example in slides)
  • Most people put off writing comments:

    • “Why waste time writing comments when the code is still changing?”
    • “Once I get the code done, I’ll write all the comments”
  • Problems with this approach:

    • You probably won’t go back and write the comments later
    • If you do, the comments will be bad:
      • You’re in a hurry and emotionally checked out
      • You have forgotten many of the design decisions, subtleties
      • Comments will be a superficial duplication of what’s obvious from the code
  • Try writing comments at the beginning:

    • I write class comments, method headers (signature and comments) before writing the bodies of methods
    • Helps me to define the overall APIs, juggle functionality between methods
    • As I write and test code, can revise the comments to make them better and better
  • Eliminate unnecessary comments so you can focus on the ones that matter.

    • “Feature” code needs far less documentation than infrastructure code
  • Name choice is an important form of documentation

    • Take time to think of names that are clear and unambiguous
    • Be specific (but not too long)!
    • Always use the same variable name for the same kind of object
    • Avoid using the same name to refer to different kinds of things
  • Suggestions for projects:

    • Header comment blocks for every method, every class.
    • Document every class instance variable, every method parameter, every result.
    • Add comments inside methods if/when needed
    • Skip comments only if you’re sure it will be obvious to readers
    • Follow Javadoc conventions
  • Red flags for comments:

    • Hard to come up with a clear and simple name for variable?
    • Method documentation has to document every internal feature of the algorithm in order to be complete?
    • Interface documentation for a class or method has to be very long in order to be complete?

请我喝杯咖啡吧~

支付宝
微信