Another danger comments present is that they can be used to justify bad coding prac-
tices. Many times programmers will be tempted to produce code that is too complicated
or too hard to maintain, and add comments to it, rather than rewrite it to good standards.
In fact, many experts recommend avoiding comments completely, and produce what is
called “self-documented code”—that is, code that is so well written that it does not need
any documentation. We believe that is an ideal that programmers should strive for, but that
comments have their place, especially in the form of describing the programmer’s intent.
We strongly encourage programmers to use good names and good programming
practices and reserve comments mainly for external references and statements of intent.
If the code cannot be abstracted and it is still complex, summary comments may be
appropriate. Code explanations and markers should only be used as temporary mea-
sures, and repetitions of the code should be always avoided.
A problem with comments is that most programming books and tutorials, because
they are geared for beginners (or at least for people who do not know a particular tech-
nique or library), tend to provide too many comments, usually repeating or explaining
the code. Many programmers will either imitate this style, or go to the other extreme and
avoid comments at all costs. McConnell (2004) and Kernighan and Pike (1999) provide
examples of good commenting practices.
9.5 Debugging
Debugging is the act of locating and fixing errors in code. The errors are usually discov-
ered through testing, but they can be found by other means, including code inspections
and through normal use of the program. We can identify four phases in the debugging
process (besides discovering the error, which we do not consider part of debugging).
These phases will need to occur in almost every case. Keep in mind that debugging is a
highly iterative process, in which you will be creating a hypothesis about what causes
the errors, writing test cases to prove or disprove the hypothesis, and changing the
code to try to fix the problem. If the hypothesis happens to be false, you will need to go
back to generating and corroborating a new hypothesis. The four phases in the debug-
ging process can be summarized as follows:
1. Stabilization, sometimes called reproduction: The purpose of this phase is to be able
to reproduce the error on a particular configuration (in many cases the developer’s
machine), and to find out the conditions that led to the error by constructing a
minimal test case. We do not need to look at the code at all in this phase; we just
need to identify which input conditions, combined with which program states,
produce the error.
The output of the stabilization phase is a series of test cases that produce the
error, and possibly some cases that perform correctly. Stabilization also involves
minimization of the conditions that led to the error. After you write a test case that
reproduces the error, try to write a simpler one that also fails. Although stabiliza-
tion is a trivial task in many cases, it can be very difficult sometimes. Many errors
will appear to occur at random, and testing the program twice with the same input
will sometimes produce different results, depending on the state the program is in.
Variables that are not initialized, dangling pointers, and the interaction of several
threads tend to produce errors that appear to be random.
91998_CH09_Tsui.indd 193 1/10/13 10:57:49 AM