skip to content

For a Ruby gem's CI, how would you use yard stats --list-undoc and yard doc --fail-on-warning to stop the public API documentation from rotting?

level: seniorimportance: nice to knowfreq 18%

answer

  1. percent documented per object type
  2. undocumented means blank docstring
  3. warnings: unknown @param, unknown tag
  4. --fail-on-warning aborts the run
  5. .yardopts shared by dev and CI

basics

~20 s

yard stats --list-undoc prints a % documented figure and lists each undocumented public object with its file and line; it does not fail on gaps by itself. yard doc --fail-on-warning exits non-zero on warnings such as an unknown @param name.

solid answer

~50 s

`yard stats` counts files, modules, classes, constants, attributes and methods, prints `NN.NN% documented`, and with `--list-undoc` lists every object whose docstring is blank, with `file:line`. It counts **public** objects unless you add `--protected` or `--private`, and `--no-private` drops `@private` ones. Undocumented objects are not warnings, so the stats run passes regardless; you gate coverage by parsing the percentage in a CI step, often as a ratchet that may not drop. Correctness is gated with `yard doc --fail-on-warning`, which aborts when YARD warns: an `@param` naming a parameter the method no longer has, an unknown tag such as `@returns`, a link it cannot resolve. Put the shared options in `.yardopts` so developers and CI run the same thing. With plain RDoc, `rdoc -C lib` gives the coverage report, and `RDoc::Task`'s `rdoc:coverage` raises when it is incomplete.

code

bash · 4 lines
bash
yard doc --fail-on-warning
yard stats --list-undoc | tee yard-stats.txt
pct=$(grep -o '[0-9.]*% documented' yard-stats.txt | cut -d% -f1)
awk -v p="$pct" 'BEGIN { exit (p < 90) }'

go deeper

for a junior

Recall that yard stats reports a percent-documented figure and that --list-undoc shows which objects lack comments.

for a middle

Explain what counts as undocumented, a blank docstring on a public object, and which YARD warnings reveal stale tags.

for a senior

Design the job: --fail-on-warning for drift, a scripted or ratcheted threshold for coverage, a shared .yardopts, and @api private excluded from the metric.

for a principal

Decide how much documentation to enforce: coverage metrics invite empty comments, so pair a modest ratchet with review of public API changes and tested examples.

## What rots, and how you can detect it Documentation for a gem drifts in two different ways, and each needs its own check: - **Gaps**: someone adds `GeoLookup::Client#batch_geocode` and never writes a comment. A tool can count these. - **Lies**: someone renames `country:` to `region:` and the comment still says `@param country`. A tool can catch these only if the documentation is structured, which is what YARD tags give you. ## `yard stats`: measuring gaps `yard stats` parses the code (or, with `--use-cache`, loads the `.yardoc` database) and prints one line per object type, in the order files, modules, classes, constants, attributes and methods, followed by a total such as `87.50% documented`. - An object counts as **undocumented** when its docstring is **blank**: no prose and no visible tags. - `--list-undoc` appends an "Undocumented Objects" list sorted by file, each entry with its path and `file:line`; `--compact` shortens it. - By default only **public** objects are counted; `--protected` and `--private` add those, and `--no-private` drops objects tagged `@private`. - `--query` narrows the count, for example to objects that are not `@api private`. The important detail: an undocumented object is **not a warning**. `yard stats` exits successfully at 40% just as it does at 100%, so a coverage gate is a script around its output. ## `--fail-on-warning`: catching lies YARD logs warnings while parsing and rendering. The ones that matter for API docs: | Warning | Typical cause | |---|---| | `@param tag has unknown parameter name: country` | the argument was renamed or removed | | `@param tag has duplicate parameter name` | a copy-paste left two tags for one argument | | `Unknown tag @returns` | a misspelt tag that renders as nothing useful | | `Cannot resolve link to GeoLookup::Result` | a `{...}` reference to a class that no longer exists | `yard doc --fail-on-warning` makes the command **exit with an error status** whenever any warning was logged, which turns every row of that table into a red build. Link-resolution warnings appear while HTML is rendered, so the gate should run the real generation rather than `--no-output`. ## A CI job for the geocoding gem 1. Commit a `.yardopts` with the options everyone uses, such as `--markup markdown`, `--hide-api private` and `lib/**/*.rb - README.md CHANGELOG.md`. 2. Run `yard doc --fail-on-warning` to catch stale tags and broken links. 3. Run `yard stats --list-undoc`, extract the percentage, and fail below a threshold, or below the value on the main branch so coverage can only rise. 4. Keep the undocumented list in the job log so the author sees exactly which method to document. ## The RDoc equivalents If the gem uses plain RDoc: - `rdoc -C lib` (`--coverage-report`) prints the items that are not documented instead of generating HTML; `-C1` also reports undocumented parameter names. - In the RDoc 8.1 source, coverage mode exits unsuccessfully unless everything is documented, and the `rdoc:coverage` task that `RDoc::Task` defines raises `RDoc coverage is incomplete`. Ruby 4.0.7 bundles RDoc 7.0.4, so confirm the behaviour on the version your Gemfile resolves. ## Judgement calls - **Gate the API, not everything.** Excluding `@api private` objects keeps the metric about what users rely on. - **Ratchet rather than demand 100% on day one** for an existing gem; demanding it at once usually produces empty one-line comments that pass the counter and help nobody. - **A blank docstring is not the only failure.** A tagged but wrong comment passes `yard stats`; only the warning gate and review catch it.

  • A method has a docstring made only of `@api private`. Does `yard stats` count it as documented?
    `yard stats` treats a docstring as blank when it has no text and no **visible** tags. `@api` is not shown in output, so a docstring holding only `@api private` still counts as undocumented unless you exclude those objects with a query. Tags like `@param` or `@return` do count.
  • Why can a 100% documented gem still ship wrong documentation?
    Coverage only asks whether a docstring exists. A comment that describes an old return type or a behaviour the code no longer has passes `yard stats`. `--fail-on-warning` catches the structural drift, parameter names and links, but wrong prose needs review and `@example` code that is actually exercised by tests.

saying these in an interview costs you the question

  • yard stats exits non-zero whenever an object is undocumented.
  • --fail-on-warning fails the build on low documentation coverage.
  • yard stats counts private methods by default.
  • 100% documented means the documentation is correct.
  • A comment with any text at all hides a wrong @param from YARD.