Skip to content

Latest commit

 

History

History
372 lines (277 loc) · 13.7 KB

0000-api-doc-frontpage-styleguide.md

File metadata and controls

372 lines (277 loc) · 13.7 KB
  • Feature Name: api_doc_frontpage_styleguide
  • Start Date: 2016-07-25
  • RFC PR: (leave this empty)
  • Rust Issue: (leave this empty)

Summary

This RFC is a companion to RFC 1574 concerning API documentation conventions. This time we focus on the format and style of the "front page" of documentation as generated by rustdoc. The goal of this RFC is to outline a clear presentation style for crate documentation that is helpful to new users of a crate. We also clarify the relationship between the rustdoc front page and the project's readme. These are the guidelines we will be using for the rust-lang crates, but are also meant to help guide community crate authors.

Motivation

A user's first experience with a crate is often the front page of the crate's documentation. Taken together with the API documentation and readme, the front page is a critical piece of understanding a crate's purpose, how to use it, and its limitations. This RFC furthers our roadmap goal of providing easy access to high quality crates. A crate may be otherwise high quality but shoddy front page documentation can lead people to pass over it.

Detailed design

The front page of the crate should cover three main points. In this RFC, we cover each in turn.

Introduction

The purpose of the introduction is to help a user quickly figure out whether this is the right crate for them.

In designing the introduction, consider a user searching for a crate to fulfill some use case, landing on the front page of several similar crates, and trying to determine which one to pursue further. The introduction is responsible for concisely conveying what the crate does and why somebody might want to use it. By the end of the introduction the reader should either be confident that this is not what they were looking for, or eager to read the example code and capabilities that follow the introduction.

Here is an example of a decent introduction from the log crate:

A lightweight logging facade.

A logging facade provides a single logging API that abstracts over the actual logging implementation. Libraries can use the logging API provided by this crate, and the consumer of those libraries can choose the logging framework that is most suitable for its use case.

If no logging implementation is selected, the facade falls back to a "noop" implementation that ignores all log messages. The overhead in this case is very small - just an integer load, comparison and jump.

A log request consists of a target, a level, and a body. A target is a string which defaults to the module path of the location of the log request, though that default may be overridden. Logger implementations typically use the target to filter requests based on some user configuration.

Based on the introduction, this crate is not for somebody hoping to compute a logarithm or a Laplacian of Gaussian. If you are writing a library that wants to log messages in a way that is governed by the user, or if you are an application needing to tap into the log output of your dependencies, you should keep reading.

Another example from the rand crate:

Utilities for random number generation

The key functions are random() and Rng::gen(). These are polymorphic and so can be used to generate any type that implements Rand. Type inference means that often a simple call to rand::random() or rng.gen() will suffice, but sometimes an annotation is required, e.g. rand::random::<f64>().

See the distributions submodule for sampling random numbers from distributions like normal and exponential.

In this introduction it would be valuable to touch on a fundamental question that many people evaluating a random number library will need to know: where does the randomness come from? Is it from the operating system? Is there a pseudorandom number generator? Is it cryptographically secure?

First Example

The purpose of the first example is to help the reader imagine themselves as a user of your crate. It gives them a sense of how high level or low level the crate's primary abstractions are and how some of the verbally described features play out in practice.

Ideally this example should only demonstrate your crate and core Rust functionality. Avoid large examples with lots of features; those belong in the examples directory.

Let's look at a good first example from the log crate. Notice that the author has focused on getting started, showing how to import and use the crate, and a few simple uses of common functionality.

#[macro_use]
extern crate log;

pub fn shave_the_yak(yak: &Yak) {
    info!(target: "yak_events", "Commencing yak shaving for {:?}", yak);

    loop {
        match find_a_razor() {
            Ok(razor) => {
                info!("Razor located: {}", razor);
                yak.shave(razor);
                break;
            }
            Err(err) => {
                warn!("Unable to locate a razor: {}, retrying", err);
            }
        }
    }
}

Capabilities

The core capabilities of a crate are the main reason people will use your crate. In the next section, you can document what each of these core capabilities are and how to use them. For example, in a crate about random numbers, you may have sections about: generating random numbers, fitting numbers to statistical distributions, use in cryptography, thread-local random numbers, and so on.

It is helpful to introduce and give a clear description for each capability separately to help your readers understand each concept individually before they begin to combine capabilities.

Here is a good example from the rand crate:

Thread-local RNG

There is built-in support for a RNG associated with each thread stored in thread-local storage. This RNG can be accessed via thread_rng, or used implicitly via random. This RNG is normally randomly seeded from an operating-system source of randomness, e.g. /dev/urandom on Unix systems, and will automatically reseed itself from this source after generating 32 KiB of random data.

Cryptographic security

An application that requires an entropy source for cryptographic purposes must use OsRng, which reads randomness from the source that the operating system provides (e.g. /dev/urandom on Unixes or CryptGenRandom() on Windows). The other random number generators provided by this module are not suitable for such purposes.

Sample code for each capability should be as simple as possible. In the rand crate, thread safe RNG is demonstrated in 5 lines of code:

use rand::Rng;

let mut rng = rand::thread_rng();
if rng.gen() { // random bool
    println!("i32: {}, u32: {}", rng.gen::<i32>(), rng.gen::<u32>());
}

After this example, a user should immediately be able to use a thread safe random number generator in their own program.

Examples may go inline under each capability, or all together in an Examples section.

More complex source examples should be included as a separate program in the examples directory. Be wary of putting large examples in your doc where users will have to read past the code to get the beginning of your API documentation.

You should avoid examples that require understanding of external crates unless it is necessary. As a hypothetical example, your logging crate should not require the user to have experience with Diesel. It is okay to leave these integrations for the reader to put together themselves.

The readme

This is an unresolved question.

How We Teach This

This RFC will be linked from the Rust API guidelines in a guideline dedicated to front page documentation.

As part of the Libz Blitz, the community and the libs team will evaluate high-profile crates against this guideline and file issues where improvement is required. With front page documentation improving across the ecosystem, users will learn to depend on it and will notice and complain about crates that fall short. Those crates can improve their front page experience by using this RFC as guidance.

Drawbacks

A possible drawback of this approach is that it risks over-specifying a format that is ill-served for a particular crate. For example, a crate with a lot of moving parts may need to use more complex examples because smaller examples lose educational value or overall impact.

Alternatives

Multi-crate guidance

Serde, Tokio, Diesel, and other large projects are factored across many individual crates. We may provide explicit guidance about how to handle front page documentation in this situation, beyond just applying this RFC to each crate individually.

Example size

We may recommend an upper bound on the size of example code that belongs in the front page documentation. For example we may say that anything over 40 lines probably belongs in its own dedicated file in the examples directory.

Example budget

We may recommend only one or two examples of the most basic sort, and links to examples or a website for anything more thorough. During your first introduction to a crate, links are just as edifying as inline examples. During your next 1024 times referring to the rustdoc as reference, you are not forced to scroll past the no longer useful example code.

Usage section

We may recommend a section or just a lone code block that gives the basic mechanics of depending on a crate.

[build-dependencies]
gcc = "0.3"

This saves users the effort of:

  • Remembering the crate name (crate is gcc, repository is gcc-rs);
  • Figuring out what the latest version is;
  • Knowing which section to put the dependency into.

Links to homepage, repo, crates.io

Browsing from a rustdoc front page to any of these other references is currently obnoxious. On docs.rs they provide these links in a sidebar.

selection_043

Some crates provide manual cross-links.

Changelog - API Documentation - Cargo - Repository

We may recommend that crates do something like this in their frontpage. However, I believe that this should be a feature request for rustdoc instead.

Unresolved questions

Readme

What goes in the readme? Do we duplicate all or most of the front page doc in the readme?

There are some things that have traditionally gone in the readme but not the front page doc:

  • Requisite system libraries
  • Code of conduct
  • Instructions for developers
  • CI status
  • Semantic versioning philosophy
  • Supported OS and Rust versions
  • Comparison with other crates
  • Benchmark data
  • License boilerplate

Is there a good reason for this distinction?

Here are some projects with very different readmes and front page docs:

Here are some projects with somewhat similar readmes and front page docs:

Here are some projects with practically identical readmes and front page docs:

Limitations

How best to document a crate's limitations.

Cargo cfgs

Where documentation for features goes. Serde has this on the website and in Cargo.toml but not in rustdoc.

License boilerplate

This crate is licensed under either of Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT) at your option. Unless you explicitly state otherwise, any contribution intentionally submitted bla bla bla.

Does this need to be in the readme and/or the rustdoc?

Getting help

The Serde readme has the following stanza. It is designed to catch new users who may silently struggle and then abandon the crate without getting help.

Serde developers live in the #serde channel on irc.mozilla.org. The #rust channel is also a good resource with generally faster response time but less specific knowledge about Serde. If IRC is not your thing or you don't get a good response, we are happy to respond to GitHub issues as well.

Does this belong in the readme and/or the rustdoc?

Migration notes

Migration from v1 to v2 is part of in the slog front page documentation. Is this an appropriate place?