General Guidelines

[cg01] Since more time is spent reading code than writing it, having clear, thorough documentation is crucial both during initial development and for successful long-term maintenance. The goal is not redundancy; commenting every line of code like:

// increment i by 1

just raises the noise floor and provides no useful information. However, a comment like:

public boolean addMidiEvent(MidiEvent event)
   // add the timestamped MidiEvent 'event' at the correct place in our
   // event list. We are using an array representation of a heap structure
   // (see Sedgewick, "Algorithms in Java", chapter 9). This algorithm is
   // guaranteed to be at worst O(2 log(n))

   // code left as an exercise for the reader...


both explains the design and intent of the code that follows, along with a pointer to an external reference explaining the design and theory behind the underlying data structure.

Header Files

[cg02] The primary function of comments in header files is to provide documentation for programmers who will be using the classes you write. Ideally, other programmers should never need to refer to your implementation files to make proper use of your code.

In languages that do not use separate header files, the same principles obtain; the interface must be documented at a sufficiently high level for users of the code.

Make sure that the following things are well and correctly commented:

  • overall class design / usage information
  • member function usage
  • function parameters
  • return values
  • any special constraints

[cg03] Comments for member functions precede the function declaration, and are not followed by a blank line. Comments should be formatted with the appropriate markup for automatic document extraction tools:


asdoc (from Flex SDK)


<doc> tags into ndoc or Doxygen












Epydoc or Doxygen

Examples in the StyleGuide will be formatted for Doxygen, using the JavaDoc-style markup.

 * @class ASafeBuffer
 * @brief The `ASafeBuffer' class wraps the standard library std::deque class. 
 * Most of the functions simply delegate to the appropriate 
 * std::deque function. Note that we do NOT derive publicly from std::deque 
 * -- the lack of a virtual destructor makes that unsafe.
class ASafeBuffer
   enum BufferState
      kSuccess = 0,     /**< The operation was successful */
      kNotInitialized,  /**< The buffer has not been initialized */
      kFatalError       /**< Fatal error using the buffer */

   // construction, assignment and destruction...
   ASafeBuffer(const ASafeBuffer& x);
   ASafeBuffer& operator=(const ASafeBuffer& x);

    * appends the value 'val' to the end of the buffer.
    * @param val value to append to the buffer
    * @return ASafeBuffer::kSuccess unless there was an initialization problem.
   BufferState Append(UInt8 val);

   std::deque fBuffer;    /**< comment member variables here... */

    * ...or place larger comments for member variables 
    *  before their declaration.
   SInt32 fBufferSize;

Any configuration files required by the document extraction software (e.g., doxyfile) should be maintained as part of the project in source control, but the generated documentation files should not be kept in source control. Project Managers should be sure to include information on the tool used on their projects in each project's ReadMe.txt file, including version information.

Whenever we release source to a client, the release should include extracted documentation that was generated from the source.

The goal of using markup is to enable us to capture just enough information (that we should already be documenting anyway) in a format that can be used both by our developers as API documentation, and also given to clients at the end of the project with the source code. All this information is covering things that we should already be documenting, just with additional structure.

Be sure to markup the documentation for at least:

  • High-level comments about classes (assumptions, uses, constraints)
  • Member function usage instructions, including
    • Function parameters
    • Return types and values
  • Enumerated types/constants
  • Member variables

Implementation Files

[cg04] The primary audience for comments in your implementation files is the maintenance programmers who will be working on your code in the future. Most likely, you will be that maintenance programmer, which should give you extra cause to be charitable with your comments.

Comments precede the code being documented, and are at the same indentation level. The comments should not just echo the code in prose, but should discuss the intent of the code. In Code Complete, McConnell recommends implementing functions by writing the comments first, then going back and actually writing the code. The approach is sound -- if you don't understand the requirements of the function well enough to write it as a narrative first, you don't yet have a firm enough design to write code from.

Standard Markup Rules

We have a few conventions that we use to encode additional information into comments. Using a standard format simplifies searching for these markups.

[cg05] //!!! indicates that attention needs to be paid to the next line or section of code. Typical reasons include calling attention to placeholder code, or code that is known to be temporarily incomplete. Use ellipsis dots to indicate larger areas:

// this code temporarily removed pending resolution of bug 9xxx.
// (several lines of code)

[cg06] //$$$ indicates a request that the following code be reviewed before a change is considered complete. This practice began before A&L used source code management software, and we used this technique when making changes in a source file 'owned' by another programmer, who would review the requested changes before merging them into the master sources. Since most multi-programmer projects are still divided such that each source file has a primary author, this technique is still a useful way to mark changed code for review by someone with more experience with the code. The original code should be commented out, but retained as a reference:

// for (SInt32 i = 0; i < kLoopLimit; ++i)
// {
//    this->Process(i);
// }
//$$$ after
// doesn't the process function expect 1-based indexing?
for (SInt32 i = 0; i < kLoopLimit; ++i)
   this->Process(i + 1);