Effective Dart: Documentation

It’s easy to think your code is obvious today without realizing how much yourely on context already in your head. People new to your code, andeven your forgetful future self won’t have that context. A concise, accuratecomment only takes a few seconds to write but can save one of those peoplehours of time.

We all know code should be self-documenting and not all comments are helpful.But the reality is that most of us don’t write as many comments as we should.It’s like exercise: you technically can do too much, but it’s a lot morelikely that you’re doing too little. Try to step it up.

Comments

The following tips apply to comments that you don’t want included in thegenerated documentation.

DO format comments like sentences.

  1. // Not if there is nothing before it.
  2. if (_chunks.isEmpty) return false;

Capitalize the first word unless it’s a case-sensitive identifier. End it with aperiod (or “!” or “?”, I suppose). This is true for all comments: doc comments,inline stuff, even TODOs. Even if it’s a sentence fragment.

DON’T use block comments for documentation.

  1. greet(name) {
  2. // Assume we have a valid name.
  3. print('Hi, $name!');
  4. }
  1. greet(name) {
  2. /* Assume we have a valid name. */
  3. print('Hi, $name!');
  4. }

You can use a block comment (//) to temporarily comment out a sectionof code, but all other comments should use //.

Doc comments

Doc comments are especially handy because dartdoc parses them and generatesbeautiful doc pages from them. A doc comment is any comment that appearsbefore a declaration and uses the special /// syntax that dartdoc looks for.

DO use /// doc comments to document members and types.

Linter rule: slash_for_doc_comments

Using a doc comment instead of a regular comment enables dartdoc to find itand generate documentation for it.

  1. /// The number of characters in this chunk when unsplit.
  2. int get length => ...
  1. // The number of characters in this chunk when unsplit.
  2. int get length => ...

For historical reasons, dartdoc supports two syntaxes of doc comments: ///(“C# style”) and / … */ (“JavaDoc style”). We prefer /// because it’smore compact. / and / add two content-free lines to a multiline doccomment. The /// syntax is also easier to read in some situations, such aswhen a doc comment contains a bulleted list that uses to mark list items.

If you stumble onto code that still uses the JavaDoc style, consider cleaning itup.

PREFER writing doc comments for public APIs.

Linter rules: package_api_docs,public_member_api_docs

You don’t have to document every single library, top-level variable, type, andmember, but you should document most of them.

CONSIDER writing a library-level doc comment.

Unlike languages like Java where the class is the only unit of programorganization, in Dart, a library is itself an entity that users work withdirectly, import, and think about. That makes the library directive a greatplace for documentation that introduces the reader to the main concepts andfunctionality provided within. Consider including:

  • A single-sentence summary of what the library is for.
  • Explanations of terminology used throughout the library.
  • A couple of complete code samples that walk through using the API.
  • Links to the most important or most commonly used classes and functions.
  • Links to external references on the domain the library is concerned with.

You document a library by placing a doc comment right above the librarydirective at the start of the file. If the library doesn’t have a librarydirective, you can add one just to hang the doc comment off of it.

CONSIDER writing doc comments for private APIs.

Doc comments aren’t just for external consumers of your library’s public API.They can also be helpful for understanding private members that are called fromother parts of the library.

DO start doc comments with a single-sentence summary.

Start your doc comment with a brief, user-centric description ending with aperiod. A sentence fragment is often sufficient. Provide just enough context forthe reader to orient themselves and decide if they should keep reading or lookelsewhere for the solution to their problem.

  1. /// Deletes the file at [path] from the file system.
  2. void delete(String path) {
  3. ...
  4. }
  1. /// Depending on the state of the file system and the user's permissions,
  2. /// certain operations may or may not be possible. If there is no file at
  3. /// [path] or it can't be accessed, this function throws either [IOError]
  4. /// or [PermissionError], respectively. Otherwise, this deletes the file.
  5. void delete(String path) {
  6. ...
  7. }

DO separate the first sentence of a doc comment into its own paragraph.

Add a blank line after the first sentence to split it out into its ownparagraph. If more than a single sentence of explanation is useful, put therest in later paragraphs.

This helps you write a tight first sentence that summarizes the documentation.Also, tools like Dartdoc use the first paragraph as a short summary in placeslike lists of classes and members.

  1. /// Deletes the file at [path].
  2. ///
  3. /// Throws an [IOError] if the file could not be found. Throws a
  4. /// [PermissionError] if the file is present but could not be deleted.
  5. void delete(String path) {
  6. ...
  7. }
  1. /// Deletes the file at [path]. Throws an [IOError] if the file could not
  2. /// be found. Throws a [PermissionError] if the file is present but could
  3. /// not be deleted.
  4. void delete(String path) {
  5. ...
  6. }

AVOID redundancy with the surrounding context.

The reader of a class’s doc comment can clearly see the name of the class, whatinterfaces it implements, etc. When reading docs for a member, the signature isright there, and the enclosing class is obvious. None of that needs to bespelled out in the doc comment. Instead, focus on explaining what the readerdoesn’t already know.

  1. class RadioButtonWidget extends Widget {
  2. /// Sets the tooltip to [lines], which should have been word wrapped using
  3. /// the current font.
  4. void tooltip(List<String> lines) {
  5. ...
  6. }
  7. }
  1. class RadioButtonWidget extends Widget {
  2. /// Sets the tooltip for this radio button widget to the list of strings in
  3. /// [lines].
  4. void tooltip(List<String> lines) {
  5. ...
  6. }
  7. }

PREFER starting function or method comments with third-person verbs.

The doc comment should focus on what the code does.

  1. /// Returns `true` if every element satisfies the [predicate].
  2. bool all(bool predicate(T element)) => ...
  3.  
  4. /// Starts the stopwatch if not already running.
  5. void start() {
  6. ...
  7. }

PREFER starting variable, getter, or setter comments with noun phrases.

The doc comment should stress what the property is. This is true even forgetters which may do calculation or other work. What the caller cares about isthe result of that work, not the work itself.

  1. /// The current day of the week, where `0` is Sunday.
  2. int weekday;
  3.  
  4. /// The number of checked buttons on the page.
  5. int get checkedCount => ...

Avoid having a doc comment on both the setter and the getter, as DartDoc will showonly one (the one on the getter.)

PREFER starting library or type comments with noun phrases.

Doc comments for classes are often the most important documentation in yourprogram. They describe the type’s invariants, establish the terminology it uses,and provide context to the other doc comments for the class’s members. A littleextra effort here can make all of the other members simpler to document.

  1. /// A chunk of non-breaking output text terminated by a hard or soft newline.
  2. ///
  3. /// ...
  4. class Chunk { ... }

CONSIDER including code samples in doc comments.

  1. /// Returns the lesser of two numbers.
  2. ///
  3. /// ```dart
  4. /// min(5, 3) == 3
  5. /// ```
  6. num min(num a, num b) => ...

Humans are great at generalizing from examples, so even a single code samplemakes an API easier to learn.

DO use square brackets in doc comments to refer to in-scope identifiers.

Linter rule: comment_references

If you surround things like variable, method, or type names in square brackets,then dartdoc looks up the name and links to the relevant API docs. Parenthesesare optional, but can make it clearer when you’re referring to a method orconstructor.

  1. /// Throws a [StateError] if ...
  2. /// similar to [anotherMethod()], but ...

To link to a member of a specific class, use the class name and member name,separated by a dot:

  1. /// Similar to [Duration.inDays], but handles fractional days.

The dot syntax can also be used to refer to named constructors. For the unnamedconstructor, put parentheses after the class name:

  1. /// To create a point, call [Point()] or use [Point.polar()] to ...

DO use prose to explain parameters, return values, and exceptions.

Other languages use verbose tags and sections to describe what the parametersand returns of a method are.

  1. /// Defines a flag with the given name and abbreviation.
  2. ///
  3. /// @param name The name of the flag.
  4. /// @param abbr The abbreviation for the flag.
  5. /// @returns The new flag.
  6. /// @throws ArgumentError If there is already an option with
  7. /// the given name or abbreviation.
  8. Flag addFlag(String name, String abbr) => ...

The convention in Dart is to integrate that into the description of the methodand highlight parameters using square brackets.

  1. /// Defines a flag.
  2. ///
  3. /// Throws an [ArgumentError] if there is already an option named [name] or
  4. /// there is already an option using abbreviation [abbr]. Returns the new flag.
  5. Flag addFlag(String name, String abbr) => ...

DO put doc comments before metadata annotations.

  1. /// A button that can be flipped on and off.
  2. @Component(selector: 'toggle')
  3. class ToggleComponent {}

  1. @Component(selector: 'toggle')/// A button that can be flipped on and off.class ToggleComponent {}

Markdown

You are allowed to use most markdown formatting in your doc comments anddartdoc will process it accordingly using the markdown package.

There are tons of guides out there already to introduce you to Markdown. Itsuniversal popularity is why we chose it. Here’s just a quick example to give youa flavor of what’s supported:

  1. /// This is a paragraph of regular text.
  2. ///
  3. /// This sentence has *two* _emphasized_ words (italics) and **two**
  4. /// __strong__ ones (bold).
  5. ///
  6. /// A blank line creates a separate paragraph. It has some `inline code`
  7. /// delimited using backticks.
  8. ///
  9. /// * Unordered lists.
  10. /// * Look like ASCII bullet lists.
  11. /// * You can also use `-` or `+`.
  12. ///
  13. /// 1. Numbered lists.
  14. /// 2. Are, well, numbered.
  15. /// 1. But the values don't matter.
  16. ///
  17. /// * You can nest lists too.
  18. /// * They must be indented at least 4 spaces.
  19. /// * (Well, 5 including the space after `///`.)
  20. ///
  21. /// Code blocks are fenced in triple backticks:
  22. ///
  23. /// ```
  24. /// this.code
  25. /// .will
  26. /// .retain(its, formatting);
  27. /// ```
  28. ///
  29. /// The code language (for syntax highlighting) defaults to Dart. You can
  30. /// specify it by putting the name of the language after the opening backticks:
  31. ///
  32. /// ```html
  33. /// <h1>HTML is magical!</h1>
  34. /// ```
  35. ///
  36. /// Links can be:
  37. ///
  38. /// * https://www.just-a-bare-url.com
  39. /// * [with the URL inline](https://google.com)
  40. /// * [or separated out][ref link]
  41. ///
  42. /// [ref link]: https://google.com
  43. ///
  44. /// # A Header
  45. ///
  46. /// ## A subheader
  47. ///
  48. /// ### A subsubheader
  49. ///
  50. /// #### If you need this many levels of headers, you're doing it wrong

AVOID using markdown excessively.

When in doubt, format less. Formatting exists to illuminate your content, notreplace it. Words are what matter.

AVOID using HTML for formatting.

It may be useful to use it in rare cases for things like tables, but in almostall cases, if it’s too complex to express in Markdown, you’re better off notexpressing it.

PREFER backtick fences for code blocks.

Markdown has two ways to indicate a block of code: indenting the code fourspaces on each line, or surrounding it in a pair of triple-backtick “fence”lines. The former syntax is brittle when used inside things like Markdown listswhere indentation is already meaningful or when the code block itself containsindented code.

The backtick syntax avoids those indentation woes, lets you indicate the code’slanguage, and is consistent with using backticks for inline code.

  1. /// You can use [CodeBlockExample] like this:
  2. ///
  3. /// ```
  4. /// var example = CodeBlockExample();
  5. /// print(example.isItGreat); // "Yes."
  6. /// ```
  1. /// You can use [CodeBlockExample] like this:
  2. ///
  3. /// var example = CodeBlockExample();
  4. /// print(example.isItGreat); // "Yes."

Writing

We think of ourselves as programmers, but most of the characters in a sourcefile are intended primarily for humans to read. English is the language we codein to modify the brains of our coworkers. As for any programming language, it’sworth putting effort into improving your proficiency.

This section lists a few guidelines for our docs. You can learn more aboutbest practices for technical writing, in general, from articles such asTechnical writing style.

PREFER brevity.

Be clear and precise, but also terse.

AVOID abbreviations and acronyms unless they are obvious.

Many people don’t know what “i.e.”, “e.g.” and “et al.” mean. That acronymthat you’re sure everyone in your field knows may not be as widely known as youthink.

PREFER using “this” instead of “the” to refer to a member’s instance.

When documenting a member for a class, you often need to refer back to theobject the member is being called on. Using “the” can be ambiguous.

  1. class Box {
  2. /// The value this wraps.
  3. var _value;
  4.  
  5. /// True if this box contains a value.
  6. bool get hasValue => _value != null;
  7. }