|
| 1 | +--- |
| 2 | +name: doc-comments |
| 3 | +description: How to write inline comments, rustdoc, and module documentation in the Biome codebase. The audience is Biome developers reading the source, not end users. Use whenever writing or editing `//` comments, `///` item docs, or `//!` module docs — including comments added incidentally while fixing bugs or implementing features. |
| 4 | +compatibility: Designed for coding agents working on the Biome codebase (github.com/biomejs/biome). |
| 5 | +--- |
| 6 | + |
| 7 | +## Purpose |
| 8 | + |
| 9 | +Comments and doc comments in this repository are read by contributors, months |
| 10 | +or years after they were written, with none of the context you have right now. |
| 11 | +This skill defines who that reader is, what each kind of comment is for, and |
| 12 | +which patterns are banned. |
| 13 | + |
| 14 | +**Scope boundary:** rustdoc inside `declare_lint_rule!` / `declare_assist_rule!` |
| 15 | +blocks is end-user documentation — it is generated into the website. This skill |
| 16 | +does not apply there; see [lint-rule-development](../lint-rule-development/SKILL.md). |
| 17 | + |
| 18 | +## The Reader |
| 19 | + |
| 20 | +Write for a Biome contributor who is competent in Rust but has **no access to |
| 21 | +your current context**: not this conversation, not the pull request, not the |
| 22 | +issue, not the diff. They see only the repository at HEAD. |
| 23 | + |
| 24 | +Two consequences follow directly: |
| 25 | + |
| 26 | +1. **Never narrate change history.** Words like "now", "previously", |
| 27 | + "no longer", "the new approach" are meaningless at HEAD, where only one |
| 28 | + approach exists. State how the code works, not how it came to be. |
| 29 | +2. **Never address the reviewer.** A comment that argues your change is |
| 30 | + correct ("this properly handles X") belongs in the PR description, not in |
| 31 | + the source. The comment must justify the code as it stands, permanently. |
| 32 | + |
| 33 | +## Three Kinds of Documentation, Three Different Jobs |
| 34 | + |
| 35 | +| Kind | Job | Contains | |
| 36 | +| ---- | --- | -------- | |
| 37 | +| `//!` module docs | Explanation | Why the module exists, core concepts and terminology, how the pieces relate, design rationale | |
| 38 | +| `///` item docs | Reference | The contract: behavior, inputs and outputs, invariants, panics, errors. Neutral and factual | |
| 39 | +| `//` inline comments | Rationale | Only what the code cannot say: constraints, workarounds (with issue links), non-obvious coupling, why the obvious alternative is wrong | |
| 40 | + |
| 41 | +Do not mix the jobs. Implementation details do not belong in `///` docs — put |
| 42 | +them as `//` comments inside the body. The contract does not belong scattered |
| 43 | +across inline comments — put it on the item. |
| 44 | + |
| 45 | +## The Deletion Test |
| 46 | + |
| 47 | +Before writing any comment, ask: **does this state something the reader cannot |
| 48 | +recover from the code itself?** |
| 49 | + |
| 50 | +- If the information is already carried by names, types, or structure, do not |
| 51 | + write the comment. If the name fails to carry it, improve the name. |
| 52 | +- Information that legitimately needs a comment: an invariant, a rationale, a |
| 53 | + coupling to code elsewhere, a workaround with a link, surprising behavior of |
| 54 | + a dependency, a term of art the module defines. |
| 55 | + |
| 56 | +When editing later, the same test applies in reverse: a comment that no longer |
| 57 | +passes it should be deleted, not left to rot. |
| 58 | + |
| 59 | +## Banned Patterns |
| 60 | + |
| 61 | +**Narrating the next line.** Delete these on sight: |
| 62 | + |
| 63 | +```rust |
| 64 | +// Increment the generation counter |
| 65 | +generation += 1; |
| 66 | +``` |
| 67 | + |
| 68 | +**Change-history narration.** Rewrite as present-tense rationale: |
| 69 | + |
| 70 | +```rust |
| 71 | +// BAD: We now intern types instead of cloning them. |
| 72 | +// GOOD: Interning avoids cloning these types on every lookup. |
| 73 | +``` |
| 74 | + |
| 75 | +**Reviewer-addressed justification.** Move the argument to the PR: |
| 76 | + |
| 77 | +```rust |
| 78 | +// BAD: This correctly handles the overload case from the bug report. |
| 79 | +// GOOD: Overloads are matched by arity before parameter types, so a |
| 80 | +// partial-arity call cannot select the wrong candidate. |
| 81 | +``` |
| 82 | + |
| 83 | +**Restated rustdoc.** A `///` doc that rewords the item name says nothing: |
| 84 | + |
| 85 | +```rust |
| 86 | +// BAD: |
| 87 | +/// Handles the type inference. |
| 88 | +fn infer_types(...) |
| 89 | + |
| 90 | +// GOOD: |
| 91 | +/// Infers the type of `expr` in the scope of `module`, returning |
| 92 | +/// `TypeData::Unknown` when the expression references an unresolved import. |
| 93 | +fn infer_types(...) |
| 94 | +``` |
| 95 | + |
| 96 | +**Vague hedging.** "Some cases", "various reasons", "handles edge cases", |
| 97 | +"etc." — either name them or drop the sentence. |
| 98 | + |
| 99 | +**Emojis.** Banned everywhere in this repository, comments included. |
| 100 | + |
| 101 | +**Ad-hoc section banners** (`// ----- helpers -----`, `// ==== TYPES ====`). |
| 102 | +For grouping in long files, use the region comment pattern below instead. |
| 103 | + |
| 104 | +## Region Comments |
| 105 | + |
| 106 | +Long files group related items with paired region markers: |
| 107 | + |
| 108 | +```rust |
| 109 | +// #region FILE-LEVEL METHODS |
| 110 | +... |
| 111 | +// #endregion |
| 112 | +``` |
| 113 | + |
| 114 | +This is an established convention across the codebase (`biome_service`, |
| 115 | +`biome_module_graph`, `biome_rowan`, the parsers). The `Workspace` trait in |
| 116 | +[`crates/biome_service/src/workspace.rs`](../../../crates/biome_service/src/workspace.rs) |
| 117 | +uses it to group its methods (`PROJECT-LEVEL METHODS`, `FILE-LEVEL METHODS`, |
| 118 | +`SEARCH-RELATED METHODS`). Editors fold on these markers, which is the point: |
| 119 | +they exist for navigation, not documentation. |
| 120 | + |
| 121 | +Rules: |
| 122 | + |
| 123 | +- Every `// #region` has a matching `// #endregion`. An unpaired marker breaks |
| 124 | + editor folding silently. |
| 125 | +- The name states what the group contains. It can be a plain label |
| 126 | + (`Shared helpers`) or anchored to a function (`#region parse_thematic_break_parts`) |
| 127 | + when the region holds one entry point and its private support code. |
| 128 | +- Use regions only where they earn their keep: files or `impl`/`trait` blocks |
| 129 | + long enough that folding helps. A file that fits on two screens does not |
| 130 | + need them. |
| 131 | +- A region name is organization, not documentation. It never substitutes for |
| 132 | + rustdoc on the items inside it. |
| 133 | + |
| 134 | +## Editing Existing Code |
| 135 | + |
| 136 | +- Preserve existing doc comments. If your change alters behavior, extend or |
| 137 | + correct the specific prose — never replace it with generic text. Deleting |
| 138 | + hard-won context is worse than leaving a comment slightly stale. |
| 139 | +- Match the surrounding density. A heavily documented module deserves the same |
| 140 | + level on new items; do not blanket a sparse module with comments. |
| 141 | + |
| 142 | +## Exemplar |
| 143 | + |
| 144 | +The `//!` module docs at the top of |
| 145 | +[`crates/biome_service/src/workspace.rs`](../../../crates/biome_service/src/workspace.rs) |
| 146 | +show the target register. They define a term the rest of the module depends on |
| 147 | +("open documents") and give its meaning in both the LSP and CLI contexts; they |
| 148 | +explain a design decision the signatures alone would make confusing (the |
| 149 | +workspace is stateful, yet every method takes `&self`, because the trait must |
| 150 | +be thread-safe and caching happens internally); and they state the error |
| 151 | +philosophy once, at the top, instead of repeating it on every method. |
| 152 | +Everything is present tense; nothing mentions how the design evolved or |
| 153 | +defends a change. |
| 154 | + |
| 155 | +## Self-Check Before Finishing |
| 156 | + |
| 157 | +After completing any task that touched comments, re-read **only the comments |
| 158 | +in your diff**, in isolation from the code changes: |
| 159 | + |
| 160 | +1. Does each one pass the deletion test? |
| 161 | +2. Does any reference the conversation, the change itself, or the reviewer? |
| 162 | +3. Would a reader without access to the diff understand each one? |
| 163 | + |
| 164 | +Fix or delete what fails. Deletion is the default; a missing comment is |
| 165 | +cheaper than a misleading one. |
| 166 | + |
| 167 | +## References |
| 168 | + |
| 169 | +- [Diátaxis](https://diataxis.fr/) — the framework behind the |
| 170 | + explanation / reference / rationale split above. |
| 171 | +- [lint-rule-development](../lint-rule-development/SKILL.md) — for rule |
| 172 | + rustdoc, which is end-user documentation. |
0 commit comments