Or: Comments, what they are and how to use them
Inline documentation, or “comments”, is a habit of good programming that beginners don’t always use effectively. They should be used the way a storyteller would pause the plot to tell some backstory or give exposition to help the reader understand what the characters are saying and doing. This tutorial is partly written to address a myth that comments should be unnecessary and that the code should explain itself.
High-level languages are user interfaces. They’re not meant to be executed directly by the computer: they’re always translated to machine code or expression trees by a compiler or an interpreter. Compilation is, in fact, secondary to the point. The point of code is to distill an understanding of solution for humans to read and comprehend, and it can (and should), be a multimedia experience. In addition to the syntax of the language itself, your experience with code will probably include color, diagrams, metrics, inline graphics and animation.
It is not unusual for the act of programming to change the developer’s understanding of the problem, sometimes to the point where a computer program is no longer needed. By contrast, the binary produced by compiling code is a disposable artifact, an inconvenience necessitated by hardware limitations. It’s discarded the moment a change in the source code makes it obsolete.
But where the code should explain the solution, comments help explain the problem. They are the syntactical answer to the meta-syntactical questions inherent to any program.
When to use inline documentation
To explain why you do something
const decimal Acme_Comarketing_Margin = 1.03;
// Acme's Accounts Payable takes a deduction for themselves
// to cover marketing expenses, so we compensate by charging
// them more per product.
decimal itemPrice = itemPrice * Acme_Comarketing_Margin;
Not every external factor that’s part of the problem can be lucidly explained by the form of the solution. Without the comment, the name of the constant and the math operation would tell you that the product price is being increased to cover some kind of co-marketing expense, but it wouldn’t tell the programmer that it was put there because Acme takes the deduction when they submit a payment. In fact, there may be no practical way to express Acme’s remittance behavior in code. So the comment is there to protect against the future when the terms of the partnership may change.
To reason about the code
bool NewFunctionIsFaster(Func<int> newFunction, Func<int> classicFunction)
{
...benchmark setup elided...
Func<int> testedFunc = newFunction;
for (int i = 0; i < BenchIterations; i++)
{
for (int j = 0; j < 2; j++)
{
watch.Reset();
watch.Start();
testedFunc(i);
watch.Stop();
Results[testedFunc][i] = watch.ElapsedTicks;
// Alternate between the new and classic function to eliminate the
// influence of background activity
testedFunc = testedFunc == newFunction ? classicFunction : newFunction;
}
}
return Results[newFunction].Average() < Results[classicFunction].Average();
}
One of the essences of writing bug-free code is redundancy of meaning. The more the code contains the same concept expressed in different forms, the easier it is to catch bugs early. Unit tests, method contracts and types can encode redundant meaning in executable form so they can proof each other. But inline documentation can encode redundant meaning, too. Perhaps one day the developer will read the comment and realize that the code it describes is wrong. But without that comment, the realization may never come and the code will be taken for granted.
Use your judgement when writing this kind of documentation. You don’t need to comment every variable initialization or loop setup, just the sections of code that are complex or embody some kind of assumption that isn’t already evident.
To mark-up code for refactoring
// QA: Unit test this
public int ProjectedVolume(StockItem item)
{
... hazardous code ...
}
// DOC: Document this
public string ComplexFormatting(string input)
{
... complex code ...
}
// TODO: Refactor to reduce size
public bool VortexSort(IEnumerable<IComparable> data)
{
... 150-line function...
}
// TODO: Inefficient, needs optimization
public IEnumerable<decimal> ExtractOutliers<IEnumerable<decimal> data)
{
.. deeply-nested loops ...
}
For the solo developer, comments should be used to mark-up code to be revisited later. In a team environment, it helps to use keywords that other team-members can search on. The QA team would search for “// QA
” to discover new modules that need testing, technical writers search for “// DOC
“, and so-on.
Combine this with a case-tracking system and delete the comments after the work has been done.
When not to use inline documentation
To state what should be obvious
Wrong:
// Increment i
i = i + 1;
Slightly less wrong:
// Increase line position
i = i + 1;
Better:
LinePosition++;
Better yet:
foreach (string line in Lines)
...
Best:
var Alerts = from line in File.ReadAllLines(LogFile)
where line.SubStr(0,1) == "!"
select line;
Don’t use comments to state the obvious. Attempt to make that kind of comment unnecessary by choosing the names of your variables, fields and methods carefully. Prefer to refactor code than explain it to death with inline documentation. And explore the features of your language to find new ways of expressing the same function with fewer lines and clearer code.
To disable obsolete code
; ****** APPLIES A VERY ARTIFICIAL CORRECTION FOR DECLINE*********
; yrloc=[1400,findgen(19)*5.+1904]
; ... elided code ..
; APPLY ARTIFICIAL CORRECTION
; yearlyadj=interpol(valadj,yrloc,x)
The correct way to disable obsolete code is to delete it. In order to keep old code handy in case it needs to be restored later–or to satisfy auditing and peer-review processes–it’s better to use a source control system and a discipline of regular check-ins.