Writing Quality Method Docs

We’ve all been there: looking up documentation for a method in a library and only finding a single sentence or worse nothing at all. So what makes for good documentation at the method level? Here are the things I look for:

  1. Code example
  2. Expected usage
  3. Suggested alternatives
  4. Links to external resources
  5. Edge cases
  6. Inputs/outputs

Documentation tooling

While you can write your docs as regular comments above a method, there are often tools that can read your comment and method and generate helpful documentation pages. These might be built into the language, package manager, or a third-party tool.

One I like for Ruby is YARD. While it can infer a lot from just the method definition, you can make more metadata machine-readable by leaning into it’s system of tags.

One advantage of writing docs that are machine-readable is that it makes it easier for other tools to leverage them. For example, the Solargraph Ruby language server relies on YARD annotations

screenshot of YARD-generated docs for an internal library, highlighting various documentation best practices

Code example

You should default to having a code example for every method, not matter how trivial. It really helps others understand how the method is being used. If there’s one thing I want to see in docs, this is probably it! The choice to leave off an example should be justified rather than the other way around.

You can have more than one example! If there are different ways of calling a method, consider showing examples for each.

In many documentation systems you would just indent code or wrap it in markdown fencing. YARD wants you to explicitly tag it with @example.

Expected usage

The prose should be more than just a sentence that repeats the method’s name. It should describe behavior and give tips about expected usage like suggesting that this method is expected to be overridden in subclasses or that it was designed to work with another related method.

Suggested alternatives

Beyond usaage, it’s good to mention suggested alternatives to a particular method. This is particularly valuable if there are a series of similar methods and the user might accidentally use the wrong one. For example there might be both findandfind_by_idmethods that search by UID and document id respectively. Call these out!

This can sometimes come in the form of a warning of the style “you probably came here looking to do X, you really want method Y to do that”. In YARD, the @note tag can be good for these types of call outs.

Is this method based on a standard algorithm? Does it talk to some external API? Does it work off some standardized data? Consider adding a link to external resources in your module docs. In YARD, the @see tag is good for this.

Edge cases

Both the prose and examples should call out edge cases. Does this method behave strangely when passed nil? Say something about it! Show it in the code sample. The code example doesn’t just need to show the happy path. If it’s particularly surprising, it can also be worth highlighting with something like a @note tag in YARD.

Inputs and outputs

One of the most basic things to document are a method’s inputs and outputs. In a typed language you can get some of this for free but even then there’s always some extra context that will need a human touch. Keep in mind that there is more to inputs and outputs than just method arguments and return values.

YARD provides @param, and @return tags for “pure” inputs and outputs. One thing that can be particularly useful is documenting the keys of an options hash. Often these can be much harder to infer by just looking at the code. What are the possible options what do they do? Do they affect each other? The YARD @option tag is great for this.

Consider whether your method relies on some kind of global state such as global or ENV variables. Make sure to document that or users might be in for a surprise! This often works best in the prose section but you might also need to demonstrate this in the code examples. If using YARD, the @note tag can also be a good use.

Does the method potentially raise an error (this might be raised directly in the method or triggered further down the method tree)? If so, what kind and under what conditions? Can it be multiple different kinds? In YARD, use the @raise tag for this.

Does your method have side effects such as making a network call or writing to the file system? Make sure to document this! The prose description is usually the best place to do this. If the side effect can be dangerous or unexpected (e.g. mutating another object) then also consider a @note tag.

When not to write method docs

Writing quality method docs is expensive! It always takes me much longer than expected to document a class. And then there is the added maintenance burden of maintaining the docs as the underlying code changes.

In my experience, I’ve found that method docs are usually most worthwile on library or library-like code. This could be a gem that you maintain or just a file in your lib/ directory.

This kind of code tends not to churn as much (usually under some kind of semantic versioning system). It’s also code where users care a lot about the public behavior and less about the internals. Library code is often more abstract, and users are not as familiar with it.

All of these mean that library code gets a lot of value from method docs, despite the cost. In application code though, I find that the costs typically don’t outweigh the benefits.