Saltar al contenido principal

Dart eficaz: Documentación

Comentarios y documentación claros y útiles.

Es fácil pensar que tu código es obvio hoy sin darte cuenta de cuánto confías ya en el contexto que tienes en la cabeza. Las personas nuevas en tu código, e incluso tu futuro yo olvidadizo, no tendrán ese contexto. Escribir un comentario conciso y preciso solo toma unos segundos, pero puede ahorrarle horas a una de esas personas.

Todos sabemos que el código debe autodocumentarse y que no todos los comentarios son útiles. Pero la realidad es que la mayoría de nosotros no escribe tantos comentarios como debería. Es como hacer ejercicio: técnicamente puedes hacer demasiado, pero es mucho más probable que estés haciendo muy poco. Intenta mejorar en esto.

Comentarios

#

Los siguientes consejos se aplican a los comentarios que no deseas incluir en la documentación generada.

DA formato a los comentarios como oraciones

#
bien dart
// Not if anything comes before it.
if (_chunks.isNotEmpty) return false;

Escribe con mayúscula la primera palabra a menos que sea un identificador que distinga mayúsculas de minúsculas. Termínalo con un punto (o supongo que con "!" o "?"). Esto se aplica a todos los comentarios: comentarios de documentación, líneas internas (inline), incluso TODOs. Incluso si es un fragmento de oración.

NO uses comentarios de bloque para la documentación

#
bien dart
void greet(String name) {
  // Assume we have a valid name.
  print('Hi, $name!');
}
mal dart
void greet(String name) {
  /* Assume we have a valid name. */
  print('Hi, $name!');
}

Puedes usar un comentario de bloque (/* ... */) para comentar temporalmente una sección de código, pero todos los demás comentarios deben usar //.

Comentarios de documentación

#

Los comentarios de documentación son especialmente prácticos porque dart doc los analiza y genera páginas de documentación hermosas a partir de ellos. Un comentario de documentación es cualquier comentario que aparece antes de una declaración y utiliza la sintaxis especial /// que dart doc busca.

USA comentarios de documentación /// para documentar miembros y tipos

#

Regla de linter: slash_for_doc_comments

Usar un comentario de documentación en lugar de un comentario regular permite a dart doc encontrarlo y generar documentación para él.

bien dart
/// The number of characters in this chunk when unsplit.
int get length => ...
mal dart
// The number of characters in this chunk when unsplit.
int get length => ...

Por razones históricas, dart doc admite dos sintaxis de comentarios de documentación: /// (estilo "C#") y /** ... */ (estilo "JavaDoc"). Preferimos /// porque es más compacto. /** y */ agregan dos líneas sin contenido a un comentario de documentación multilínea. La sintaxis /// también es más fácil de leer en algunas situaciones, como cuando un comentario de documentación contiene una lista con viñetas que utiliza * para marcar los elementos.

Si te encuentras con código que todavía utiliza el estilo JavaDoc, considera limpiarlo.

PREFIERE escribir comentarios de documentación para las APIs públicas

#

Regla de linter: public_member_api_docs

No tienes que documentar absolutamente cada librería, variable de nivel superior, tipo y miembro, pero deberías documentar la mayoría de ellos.

CONSIDERAR escribir un comentario de documentación a nivel de librería

#

A diferencia de lenguajes como Java donde la clase es la única unidad de organización del programa, en Dart, una librería es en sí misma una entidad con la que los usuarios trabajan directamente, importan y piensan. Eso hace que la directiva library sea un gran lugar para la documentación que introduce al lector en los conceptos y funcionalidades principales proporcionadas en su interior. Considera incluir:

  • Un resumen de una sola oración sobre el propósito de la librería.
  • Explicaciones de la terminología utilizada en toda la librería.
  • Un par de muestras de código completas que expliquen cómo usar la API.
  • Enlaces a las clases y funciones más importantes o utilizadas con más frecuencia.
  • Enlaces a referencias externas sobre el dominio del que se ocupa la librería.

Para documentar una librería, coloca un comentario de documentación antes de la directiva library y de cualquier anotación que pueda estar adjunta al principio del archivo.

bien dart
/// A really great test library.
@TestOn('browser')
library;

CONSIDERA escribir comentarios de documentación para las APIs privadas

#

Los comentarios de documentación no son solo para los consumidores externos de la API pública de tu librería. También pueden ser útiles para comprender los miembros privados que se llaman desde otras partes de la librería.

COMIENZA los comentarios de documentación con un resumen de una sola oración

#

Comienza tu comentario de documentación con una descripción breve y centrada en el usuario que termine con un punto. A menudo, un fragmento de oración es suficiente. Proporciona el contexto justo para que el lector se oriente y decida si debe seguir leyendo o buscar en otra parte la solución a su problema.

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

SEPARA la primera oración de un comentario de documentación en su propio párrafo

#

Agrega una línea en blanco después de la primera oración para dividirla en su propio párrafo. Si es útil una explicación de más de una sola oración, coloca el resto en párrafos posteriores.

Esto te ayuda a escribir una primera oración concisa que resuma la documentación. Además, herramientas como dart doc utilizan el primer párrafo como un resumen corto en lugares como listas de clases y miembros.

bien dart
/// Deletes the file at [path].
///
/// Throws an [IOError] if the file could not be found. Throws a
/// [PermissionError] if the file is present but could not be deleted.
void delete(String path) {
  ...
}
mal dart
/// Deletes the file at [path]. Throws an [IOError] if the file could not
/// be found. Throws a [PermissionError] if the file is present but could
/// not be deleted.
void delete(String path) {
  ...
}

EVITA la redundancia con el contexto circundante

#

El lector del comentario de documentación de una clase puede ver claramente el nombre de la clase, qué interfaces implementa, etc. Al leer la documentación de un miembro, la firma está justo ahí y la clase contenedora es obvia. Nada de eso necesita ser detallado en el comentario de documentación. En su lugar, enfócate en explicar lo que el lector aún no sabe.

bien dart
class RadioButtonWidget extends Widget {
  /// Sets the tooltip to [lines].
  ///
  /// The lines should be word wrapped using the current font.
  void tooltip(List<String> lines) {
    ...
  }
}
mal dart
class RadioButtonWidget extends Widget {
  /// Sets the tooltip for this radio button widget to the list of strings in
  /// [lines].
  void tooltip(List<String> lines) {
    ...
  }
}

Si realmente no tienes nada interesante que decir que no pueda inferirse de la propia declaración, entonces omite el comentario de documentación. Es mejor no decir nada que hacer perder el tiempo al lector diciéndole algo que ya sabe.

PREFIERE comenzar los comentarios de una función o método con verbos en tercera persona si su propósito principal es un efecto secundario

#

El comentario de documentación debe centrarse en lo que el código hace.

bien dart
/// Connects to the server and fetches the query results.
Stream<QueryResult> fetchResults(Query query) => ...

/// Starts the stopwatch if not already running.
void start() => ...

PREFIERE comenzar el comentario de una variable o propiedad no booleana con una frase nominal

#

El comentario de documentación debe destacar lo que la propiedad es. Esto es cierto incluso para los getters que puedan realizar cálculos u otros trabajos. Lo que le importa al invocador es el resultado de ese trabajo, no el trabajo en sí.

bien dart
/// The current day of the week, where `0` is Sunday.
int weekday;

/// The number of checked buttons on the page.
int get checkedCount => ...

PREFIERE comenzar el comentario de una variable o propiedad booleana con "Indica si" (Whether) seguido de una frase nominal o de gerundio

#

El comentario de documentación debe aclarar los estados que representa esta variable. Esto es cierto incluso para los getters que puedan realizar cálculos u otros trabajos. Lo que le importa al invocador es el resultado de ese trabajo, no el trabajo en sí.

bien dart
/// Whether the modal is currently displayed to the user.
bool isVisible;

/// Whether the modal should confirm the user's intent on navigation.
bool get shouldConfirm => ...

/// Whether resizing the current browser window will also resize the modal.
bool get canResize => ...

PREFIERE una frase nominal o una frase verbal no imperativa para una función o método si retornar un valor es su propósito principal

#

Si un método es sintácticamente un método, pero conceptualmente es una propiedad, y por lo tanto se le nombra con una frase nominal o una frase verbal no imperativa, también debe documentarse como tal. Usa una frase nominal para tales funciones no booleanas, y una frase que comience con "Indica si" (Whether) para tales funciones booleanas, al igual que para una propiedad o variable sintáctica.

bien dart
/// The [index]th element of this iterable in iteration order.
E elementAt(int index);

/// Whether this iterable contains an element equal to [element].
bool contains(Object? element);

NO escribas documentación tanto para el getter como para el setter de una propiedad

#

Si una propiedad tiene tanto un getter como un setter, crea un comentario de documentación solo para uno de ellos. dart doc trata al getter y al setter como un único campo, y si tanto el getter como el setter tienen comentarios de documentación, entonces dart doc descarta el comentario de documentación del setter.

bien dart
/// The pH level of the water in the pool.
///
/// Ranges from 0-14, representing acidic to basic, with 7 being neutral.
int get phLevel => ...
set phLevel(int level) => ...
mal dart
/// The depth of the water in the pool, in meters.
int get waterDepth => ...

/// Updates the water depth to a total of [meters] in height.
set waterDepth(int meters) => ...

PREFIERE comenzar los comentarios de la librería o de los tipos con frases nominales

#

Los comentarios de documentación para clases suelen ser la documentación más importante de tu programa. Describen los invariantes del tipo, establecen la terminología que utiliza y proporcionan contexto a los demás comentarios de documentación para los miembros de la clase. Un poco de esfuerzo extra aquí puede hacer que todos los demás miembros sean más sencillos de documentar.

La documentación debe describir una instancia del tipo.

bien dart
/// A chunk of non-breaking output text terminated by a hard or soft newline.
///
/// ...
class Chunk {
   ...
}

CONSIDERA incluir muestras de código en los comentarios de documentación

#
bien dart
/// The lesser of two numbers.
///
/// ```dart
/// min(5, 3) == 3
/// ```
num min(num a, num b) => ...

Los humanos somos excelentes generalizando a partir de ejemplos, por lo que incluso una sola muestra de código hace que una API sea más fácil de aprender.

USA corchetes en los comentarios de documentación para referirte a identificadores dentro del alcance

#

Regla de linter: comment_references

Si rodeas cosas como nombres de variables, métodos o tipos entre corchetes, entonces dart doc busca el nombre y crea un enlace a los documentos de la API correspondientes. Los paréntesis son opcionales pero pueden aclarar que te estás refiriendo a una función o constructor. Los siguientes comentarios de documentación parciales ilustran algunos casos donde estas referencias de comentarios pueden ser útiles:

bien dart
/// Throws a [StateError] if ...
///
/// Similar to [anotherMethod()], but ...

Para vincular a un miembro de una clase específica, usa el nombre de la clase y el nombre del miembro, separados por un punto:

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

La sintaxis del punto también se puede usar para referirse a constructores nombrados. Para el constructor no nombrado, usa .new después del nombre de la clase:

bien dart
/// To create a point, call [Point.new] or use [Point.polar] to ...

Para obtener más información sobre las referencias que el analyzer y dart doc admiten en los comentarios de documentación, consulta Referencias en comentarios de documentación.

USA prosa para explicar parámetros, valores de retorno y excepciones

#

Otros lenguajes utilizan etiquetas y secciones verbosas para describir cuáles son los parámetros y los retornos de un método.

mal dart
/// Defines a flag with the given name and abbreviation.
///
/// @param name The name of the flag.
/// @param abbr The abbreviation for the flag.
/// @returns The new flag.
/// @throws ArgumentError If there is already an option with
///     the given name or abbreviation.
Flag addFlag(String name, String abbreviation) => ...

La convención en Dart es integrar eso en la descripción del método y resaltar los parámetros mediante corchetes.

Considera tener secciones que comiencen con "El [parámetro]" para describir los parámetros, con "Retorna" para el valor devuelto y "Lanza" (Throws) para las excepciones. Los errores se pueden documentar de la misma manera que las excepciones, o simplemente como requisitos que deben cumplirse, sin documentar el error preciso que se lanzará.

bien dart
/// Defines a flag with the given [name] and [abbreviation].
///
/// The [name] and [abbreviation] strings must not be empty.
///
/// Returns a new flag.
///
/// Throws a [DuplicateFlagException] if there is already an option named
/// [name] or there is already an option using the [abbreviation].
Flag addFlag(String name, String abbreviation) => ...

COLOCA los comentarios de documentación antes de las anotaciones de metadatos

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

Markdown

#

Tienes permitido utilizar la mayor parte del formato markdown en tus comentarios de documentación y dart doc lo procesará en consecuencia utilizando el paquete markdown.

Ya existen muchísimas guías para introducirte a Markdown. Su popularidad universal es la razón por la que lo elegimos. Aquí hay solo un ejemplo rápido para darte una idea de lo que es compatible:

dart
/// This is a paragraph of regular text.
///
/// This sentence has *two* _emphasized_ words (italics) and **two**
/// __strong__ ones (bold).
///
/// A blank line creates a separate paragraph. It has some `inline code`
/// delimited using backticks.
///
/// * Unordered lists.
/// * Look like ASCII bullet lists.
/// * You can also use `-` or `+`.
///
/// 1. Numbered lists.
/// 2. Are, well, numbered.
/// 1. But the values don't matter.
///
///     * You can nest lists too.
///     * They must be indented at least 4 spaces.
///     * (Well, 5 including the space after `///`.)
///
/// Code blocks are fenced in triple backticks:
///
/// ```dart
/// this.code
///     .will
///     .retain(its, formatting);
/// ```
///
/// The code language (for syntax highlighting) defaults to Dart. You can
/// specify it by putting the name of the language after the opening backticks:
///
/// ```html
/// <h1>HTML is magical!</h1>
/// ```
///
/// Links can be:
///
/// * https://www.just-a-bare-url.com
/// * [with the URL inline](https://google.com)
/// * [or separated out][ref link]
///
/// [ref link]: https://google.com
///
/// # A Header
///
/// ## A subheader
///
/// ### A subsubheader
///
/// #### If you need this many levels of headers, you're doing it wrong

EVITA usar markdown en exceso

#

En caso de duda, formatea menos. El formato existe para iluminar tu contenido, no para reemplazarlo. Las palabras son lo que importa.

EVITA usar HTML para el formato

#

Puede que sea útil usarlo en casos raros para cosas como tablas, pero en casi todos los casos, si es demasiado complejo para expresarlo en Markdown, es mejor no expresarlo.

PREFIERE bloques de código delimitados por comillas invertidas

#

Markdown tiene dos formas de indicar un bloque de código: sangrar el código cuatro espacios en cada línea, o rodearlo con un par de líneas de "cercado" de tres comillas invertidas (triple-backtick). La primera sintaxis es frágil cuando se usa dentro de cosas como listas de Markdown donde la sangría ya tiene un significado o cuando el propio bloque de código contiene código sangrado.

La sintaxis de las comillas invertidas evita esos problemas de sangría, te permite indicar el lenguaje del código y es consistente con el uso de comillas invertidas para el código en línea.

bien dart
/// You can use [CodeBlockExample] like this:
///
/// ```dart
/// var example = CodeBlockExample();
/// print(example.isItGreat); // "Yes."
/// ```
mal dart
/// You can use [CodeBlockExample] like this:
///
///     var example = CodeBlockExample();
///     print(example.isItGreat); // "Yes."

Escritura

#

Nos consideramos programadores, pero la mayoría de los caracteres en un archivo fuente están destinados principalmente a ser leídos por humanos. El inglés es el idioma en el que codificamos para modificar los cerebros de nuestros compañeros de trabajo. Como ocurre con cualquier lenguaje de programación, vale la pena esforzarse por mejorar tu dominio.

Esta sección enumera algunas pautas para nuestros documentos. Puedes aprender más sobre las mejores prácticas para la escritura técnica, en general, en artículos como Technical writing style.

PREFIERE la brevedad

#

Sé claro y preciso, pero también conciso.

EVITA las abreviaturas y acrónimos a menos que sean obvios

#

Muchas personas no saben qué significan "i.e.", "e.g." y "et al.". Ese acrónimo que estás seguro de que todos en tu campo conocen puede no ser tan conocido como crees.

PREFIERE usar "this" en lugar de "the" para referirte a la instancia de un miembro

#

Al documentar un miembro de una clase, a menudo necesitas referirte al objeto sobre el cual se está llamando al miembro. El uso de "the" puede ser ambiguo. Prefiere tener algún calificador después de "this", un "this" solo también puede ser ambiguo.

dart
class Box {
  /// The value this box wraps.
  Object? _value;

  /// Whether this box contains a value.
  bool get hasValue => _value != null;
}