Code Commenting Guidance
Protocols and styles for commenting code in DocOps Lab projects.
Employing good code commenting practices is more important than ever in the age of LLM-assisted programming. Existing comments are a model for future comments, and poor commenting hygiene is contagious.
In order to maximize the usefulness of code comments for both human and AI readers, DocOps Lab projects follow specific commenting conventions, including purpose and style constraints.
LLM-backed tools and linters are used to review comments and enforce adherence to these conventions, but developer attention is critical. Comments are unlikely to be improved upon after initially merged.
Code Comments Orientation
To begin, we will standardize our understanding of what types of comments are applied to what kinds of code.
Kinds of Comments
Code comments come in several distinct types.
- documentation
-
Code comments used to build downstream-facing reference docs for methods, classes, functions, data objects, and so forth.
Docstrings are specifically comments used in the generation of rich-text reference docs. That they also happen to “document” the code to which they are adjacent is secondary.
- expository
-
Code comments that explain the purpose or function of code blocks, algorithms, or complex logic, strictly in natural language. Also called “inline comments”, these arbitrary remarks are mainly what is governed by this protocol guide.
- rationale
-
Comments that explain the reasoning behind a particular implementation choice, design pattern, or data structure.
- status
-
Stability and lifecycle markers:
DEPRECATED:,EXPERIMENTAL:,INTERNAL:,UNSTABLE:. May also include planned removal dates, version gates, feature flags. - admonition
-
Developer-facing warnings, notes, or tips embedded in code. Use
WARNING:,NOTE:, andTIP:prefixes to mark these comments distinctly. - task
-
Comments like
TODO:andFIXME:are used to mark code that needs further work. - instructional
-
Code comments left in template, stub, or sample files for interactive use. These comments tend to be intended for a downstream user who will interact directly with the file or one based on it.
- label
-
Comments that simply annotate sections of code by category or general purpose, to help with demarcation and navigation. These comments are usually brief and may use special formatting to stand out.
- directive
-
In some languages, we use special character patterns to signify that a comment has a special purpose, other than for generating reference docs. These comments may mark code for special parsing, content transclusion, or other operations.
In AsciiDoc, comments like
// tag::example[]and// end::example[]are used to mark content for inclusion elsewhere.The popular linter Vale recognizes HTML comments like
<!-- vale off -→and<!-- vale on -→to disable and re-enable content linting. - sequential/collection
-
Comments that number or order logical stages in a complex or lengthy process or members of a set. Usually something like
# STEP 1:,# PHASE 1:, and so forth, or else# GROUP A:,# SECTION 1:, etc. Always use uppercase for these markers (ex:# STEP:not# Step:).
Flavors of Code
- Ruby
-
The most robust environment for code comments, Ruby supports RDoc/YARD-style documentation comments that can be used to generate reference documentation. See Ruby Commenting Protocols for more.
- Bash
-
We make extensive use of comments in Bash scripts, but Bash has no standard for documentation comments or structured comments.
- AsciiDoc
-
Comments in
.adocfiles tend to be labels, tasks, and directives (AsciiDoc tags). AsciiDoc files tend not to have expository comments, since the content is already documentation. - YAML/SGYML
-
YAML files use copious label and instructional comments to help downstream users navigate and understand large or complex data structures. Comments can also be used to annotate nesting depth. See YAML Commenting Protocols for more.
- Liquid
-
Our use of Liquid comments is inconsistent at best. Part of the problem is their terrible format with explicit
{% comment %}and{% endcomment %}tags. While Liquid 5 has greatly improved that, DocOps Lab tooling is standardized on Liquid 4 at this time. - HTML
-
We don’t code much HTML directly. It is mostly either converted from lightweight markup or rendered by Liquid templates (or JavaScript). Comments are usually to mark nested objects for convenience, to label major structures or to highlight/clarify obscure asset references, or as directives such as
<!-- vale off -→, which disables content linting. - JavaScript
-
We are not a JavaScript shop, but we do write a good bit of vanilla JavaScript. Comments are used mainly to establish our bearings in the code and therefor are sometimes heavier than with other languages.
- CSS/SCSS
-
We mainly write CSS as SCSS, and commenting is mainly to express the intent upon compiling.
General Style Rules
-
Do not use em dashes or en dashes or (` - `).
-
Use colons (`: `) to prefix a comment with a classification.
-
Use semicolons (`; `) to break up clauses.
-
-
Use sentence-style capitalization.
-
Do not use terminal punctuation (periods, exclamation points, question marks) unless the comment is multiple sentences.
-
Hard wrap comments around 110-120 characters.
-
Use one-sentence per line.
-
Try not to wrap anything except full sentences.
-
When wrapping multi-line sentences, indent subsequent lines by an additional space.
-
Expository Comments: Use and Abuse
Arbitrary inline comments used to explain code should be used consistently and only when they add value.
Arbitrary comments can often add value, under an array of conditions that may be more art than science. We must be forgiving and understanding of occasional or even frequent misfires in various developers' subjective takes on what is useful.
This guide exists to help with comment evaluation.
Principles
Expository comments (and their authors) should adhere to these principles:
- 1) Express purpose, not implementation.
-
Comments should explain why code exists or what it is intended to do, rather than how it does it. (Rationale comments are available for explaining design/engineering choices, if necessary.)
- 2) Summarize peculiar or complex implementation (without violating #1).
-
Expository comments may include a brief reference to an explicit design choice. Still not a rationale comment (too brief, in passing) nor a task comment (no further action prescribed), just a nod to an unusual or non-obvious implementation detail.
- 3) Use natural, imperative language.
-
Comments should not contain code, and they should be formatted as English clauses or sentences. Comments should be phrased as commands or instructions, focusing on the action being performed, from the perspective of what the code is to do.
- 4) Be concise.
-
Comments should be as brief as possible. Multi-sentence comments should be the exception. In fact, comments should not typically be complete sentences.
- 5) Maintain relevance and accuracy.
-
Comments should be reviewed and updated as code changes to ensure they remain accurate and relevant.
- 6) Never cover straightforward code (except…).
-
Not all blocks need comments at all. The main criterion is whether the code’s purpose or function would not be immediately clear from the code itself to a newcomer with beginner or intermediate knowledge of the language and little familiarity with the application architecture.
Exception: Sometimes an oddity or pivotal point needs to be highlighted even in otherwise straightforward code.
- 7} Do not use comments as notes to reviewers.
-
Temporary comments intended to guide code reviewers should be avoided. Code used to help with flag logical points or communicate during pair programming or pre-commit review should be denoted as admonitions (such as `# LOGIC: ` or `# REVIEW: `) or `# TEMP: ` and removed before merging.
General Examples
Unnecessary Comments
# Create destination directory if needed
FileUtils.mkdir_p(File.dirname(target_path))
This code does exactly and only what the English comment says.
In fact, the comment is muddier than the code.
The code will create any necessary parent directories, whereas the comment only mentions the destination directory itself and does not explain if needed.
In mkdor_p, the if needed means if the ancestor directories do not exist.
# Determine if we should copy the file
file_existed_before_copy = File.exist?(target_path)
This comment is trying to explain the purpose of the line it precedes, but this is unnecessary. The code itself merely sets a variable to a Boolean value. Not only is the direct purpose of the variable clear from its name and the code making up its value, but the purpose of the variable is only relevant in the context of later code that uses it.
Comments that Add Value
end
# Public helper methods accessible to LogIssue class
def normalize_source_path source_file
normalized = source_file.gsub(/#excerpt$/, '').gsub(%r{/$}, '')
normalized.gsub(%r{^\./}, '')
end
def normalize_problem_path reported_path, source_file
A comment preceded and followed by blank lines indicates that it references or labels multiple subsequent blocks. (Or it is be part of a series of such comments that tend to and in its own case may yet still cover multiple blocks each.)
This categorizes sections for user convenience. It also helps LLM-backed tools to find relevant sections more easily.
# Try to convert absolute path back to relative path
if missing_path =~ %r{/home/[^/]+/[^/]+/work/[^/]+/(.+)$} ||
missing_path =~ %r{/([^/]+/[^/]+\.adoc)$}
@path = Regexp.last_match(1)
end
Summarizing a complex Regex pattern is vital. Conveying the intent of the pattern is far more important than explaining its mechanics.
Style
Expository comments have a subject: the code they refer to, typically in the form of a line or block. In nearly all cases, comments should immediately precede the subject code.
# Validate all inputs individually
inputs.each do |input|
# ...
end
In some languages, comments can be placed inline. This should be used sparingly.
We most commonly do this in YAML files.
inputs: # List of inputs to validate
- input1
- input2
We also do this in JavaScript files.
let count = calculated; // Start with the dynamic value
Examples
# Calculate the factorial of a number using recursion
# Handle the base case
# Never call factorial with a negative number
# Validate all inputs individually
Good comments are descriptive and purely abstract. They express an instruction and/or a principle to be adhered to or enforced within the subject block.
# Initialize the result to 1
# Loop through numbers from 1 to n
# Return the result
# Calculates the factorial of a number
Comment Protocols by Language
Ruby Commenting Protocols
All public-facing methods and classes should be documented with YARD documentation comments.
For expository comments, follow the Principles outlined above.
API Documentation Comments
Many of our Ruby gems provide public APIs that are documented using YARD.
Private methods and classes may also be documented, but this is not required.
Never describe a method just by what it returns or what parameters it takes. Describe what the method does behind the scenes or what its summarized purpose.
When documenting Ruby classes and methods with YARD, follow these patterns:
- class descriptions
-
Keep class descriptions focused on the class’s primary responsibility and role within the system. Avoid overselling capabilities or implementation details.
- method descriptions
-
Lead with what the method accomplishes, not just its signature. Example: "Processes the provided attributes to populate Change properties" rather than "Initializes a new Change object."
- capitalization consistency
-
When referring to class objects conceptually or as an instance (not variable names), use CamelCase names. Use lowercase for most instances where the term refers to a real-world object or concept.
- voice consistency
-
Use descriptive, present-tense “voice” for API documentation and YARD comments.
Exceptions
On rare occasions, comments are used to denote deep nesting in large files.
end keywords that wrap up large blocks/statements end # method my_method
end # class MyClass
end # module MyModule
end # module SuperModule
end # module OurCoolGem
Whenever possible, even when deep nesting is warranted, keep files small enough that such labels won’t be need, all else being equal.
YAML Commenting Protocols
YAML files often contain extensive comments to help users understand the structure and purpose of the data.
Comments should be used to label sections, explain complex structures, and provide hints or assistance for downstream/later users populating data fields.
# General data
inputs:
- name: input1 # required
- name: input2 # optional
config: # Settings for the application itself
setting1: value1 # Enable feature X (which is not called setting1 and thus needs translation)
body: | # Use AsciiDoc format
This is content for the body of something.
We sometimes use comments to categorize a large Array or Map for navigation, even if the data is included in all members of the Array.
# POSTS
- slug: first-post
title: My First Post
type: post
- slug: second-post
title: My Second Post
type: post
# - etc
# PAGES
- slug: about
title: About Me
type: page
# - etc
This is used when it makes no sense to nest data under parent keys like posts: and pages:, yet users will still need to navigate through large collections.