8

Programmers can write comments in code that can be automatically turned into API documentation (like Javadoc). All I have to do is add some comments explaining what a class or method does and what arguments it takes, and software turns those comments and the signatures from the code into reference documentation. For example, I can write something like this in my code:

/* Finds the value for the given ID.
 * @param id  the item to look up
 * @return    the value
 */
 public String findValue(int id) {...}

Sounds great, but my readers tell me that my documentation isn't very helpful. I can't write a book in code comments but I want my documentation to be useful, so what should I be doing to make better API reference docs?

(I'm asking this on behalf of somebody else. Having already answered that person, I wanted to also answer it here.)

2
  • 3
    Now if only all programmers would join WritersSE and read this question/answer!
    – dmm
    Mar 13, 2014 at 16:45
  • @dmm "all" is a stretch goal, but feel free to pass a link around to folks you know. :-) Mar 13, 2014 at 16:47

4 Answers 4

13

Start with the style guidelines from Oracle for Javadoc. While those guidelines are written for the Javadoc tool (and the Java language) in particular, the principles there apply to the corresponding tools for other languages. (I've seen this kind of documentation for C++, C#, and JavaScript APIs.) This answer augments that style guide.

I'm going to critique your example as a way of illustrating some principles. Let's start with the description you wrote:

Finds the value for the given ID.

For a method named findValue, this doesn't tell us very much. Especially when we can see from the signature that it takes an argument named id (which your documentation says is "the item to look up"). We could have figured all that out from the signature itself; this documentation doesn't add anything. Don't expend effort writing documentation that's already obvious.

So what should you write for the description? Start by clarifying what "find" means. Is this a lightweight operation that returns a value from an in-memory map? Is it making a database call? Is it calling a service on a slow connection? Don't reveal implementation if it's not part of the contract, but do give readers some hints about what to expect. For example:

Fetches the value for the object of the given ID, either from a local cache (if previously fetched) or from a remote data store.

Here you've added information not obvious from the signature. You've told the reader that a first fetch may be slow but subsequent ones may be faster -- but you've made no specific promises, and you've implied that it still might be slower than a simple in-memory lookup. (Maybe that's why you named this findValue instead of getValue.) You've told the caller "hey, maybe you don't want to make this call inside a tight loop that has to be really fast".

But you're not done. You should be asking yourself some questions about this interface. What happens if the ID isn't recognized? What happens if there's no object mapped to that ID? What happens if you can't complete the operation for some reason (like the network is down)? Your code probably accounts for these cases, so tell the reader what to expect.

Compare your documentation to the following:

/* Fetches the value for the object of the given ID, either from a local
 * cache (if previously fetched) or from a remote data store. 
 *
 * @param id  the item to look up (a positive integer returned from
 *            createEntry)
 * @return    the value of id if set, or null if there is no value, or
 *            INVALID_ID if id is not recognized
 * @throws AccessException  if the data could not be accessed; this is an
 *            internal error that may require administrator attention
 */
 public String findValue(int id) throws AccessException {...}

First, you may have noticed that I modified your code to add the exception. That's because there wasn't a good answer for the question about what to do if you can't contact the database -- we hadn't thought of that problem and hadn't accounted for it. Ideally you're writing your documentation while you write the code, so you can catch issues like this and make the necessary changes before you release your code. If that wasn't the case here, then you'd instead need to write some more documentation to explain what happens in that case. (And obviously another writer shouldn't come along later and modify your code like this; I'm assuming in this answer that you have control over this.) The process of documenting an interface can reveal problems in that interface, so start early.

I added two things to your parameter description for id. First, that it's a positive integer; it turns out that in your code (which I hypothetically read while writing this answer), IDs can't be negative. The signature doesn't convey this (it just says it's an integer), so document it. Second, I said where IDs come from. (In the actual documentation this would be a link to the createEntry method documentation.) This isn't necessary but might be helpful, particularly if invalid IDs are a problem for your users.

I added the information about invalid IDs and null values to the documentation for the return value. You could instead add it to the description of what the method does; there are reasonable arguments to be made for both ways. But explain it somewhere.

Note that the documentation of the (new) exception says both what it means (without revealing implementation details) and what might need to be done about it. In this case the caller can't do anything to fix the error, but he might want to notify his user or log the problem. We leave that decision up to him.

You were concerned about having to write a book to improve your API documentation, but all you really needed were another couple well-thought-out phrases. You don't want a book; good API documentation tells the reader all and only what he needs to know. This can be done concisely, and your readers will thank you for not making them read through a bunch of extra text.

Summary

Here are some key points to writing good API reference documentation:

  • Document the contract, not the implementation.1
  • Explain fuzzy verbs. What does "find" mean, versus "get"? Set users' expectations.
  • Document restrictions on arguments or return values that aren't fully conveyed by the signature (like that an integer has to be positive, or in the range 1-100, etc).
  • Cover failure and not just success. Can arguments be invalid? Can your code behave in abnormal ways even if the inputs are valid? How do you signal errors or other problems?
  • Be thorough but not verbose. Don't repeat information that's clear from the signature.

1 To do this you need to determine what the contract actually is -- what promises are you making to your users? This is a large software-design topic beyond the scope of this question.

4
  • 1
    Explain and document the contract ("Don't repeat information"? ☺)? BTW, deciding what implementation details are part of the contract can be difficult; abstractions can leak in unexpected ways (e.g., uncached accesses might become less costly when the remote data store is moved to a VM on the same computer, then disabling caching might improve average performance and hacks to provide temporal locality.can become pessimizations).
    – user5232
    Mar 13, 2014 at 15:17
  • @PaulA.Clayton oops, good catch -- fixed now. :-) I added a note about the difficulties of determining what the contract actually is; there's a larger software-design discussion there, but it feels like scope creep here. But it's important to note, so thank you for pointing it out. Mar 13, 2014 at 15:23
  • Definitely scope creep. Sadly I suspect the topic is too broad and opinion based for any SE (though a more constrained question might work on Programmers and might already have been asked). Document scope (and organization [e.g., Q&A vs. wiki]) is a hard problem for communication in general. (Stop encouraging chatty comments! ☺)
    – user5232
    Mar 13, 2014 at 19:09
  • 3
    Speaking as a programer by trade, I would LOVE to see documentation of the same quality as your example.
    – Fox Cutter
    Mar 13, 2014 at 22:57
3

As a software developer (C#, .NET, yada, yada), Monica's answer resounded nicely with me. (I don't have enough rep yet to comment on it, so my additions have to go here.)

I would add that I find great value in API documentation that is as explicit as it can be, but not overburdened with meaningless details. Further, it's very important to me that the documentation tells me what the API is supposed to do, not what it actually does. (Some will disagree with me about this; however, if the software doesn't do what the reference says it should be doing, that's a defect, requiring resolution.)

For example, if the return type of a method is List, will the method return null, or is it guaranteed to always return a non-null reference of zero or more items?

If a method takes a reference type, will it throw if the argument is null? If the argument is defined as out or ref, will it throw if the argument has not been initialized? If the argument is an array or list, will it throw if the sequence is empty? Does the method throw if a required configuration setting is missing or invalid? (If so, what configuration setting(s) does it depend on?)

These are things of immense value to a software developer. Having to track them down by wading through potentially hundreds of thousands of lines of code wastes time and money, and increases risk and the chance that I'll make a mistake.

Please, be as explicit as you can be, and no further.

1
  • Thanks for calling out types (like List) that by themselves don't convey all the necessary information. This used to be a problem in Java too (great, it returns a Collection... of what?) before generics entered the language. But even specifying the type of the members of an array/list/collection/etc isn't necessarily sufficient. May 25, 2014 at 3:21
2

Ask your readers to point you to API documentation that they do find helpful.

Get clarification from your readers. Do they mean that the "javadoc" is too vague? incomplete? poorly worded? Or do they mean that they can understand the reference doc, but it doesn't help them write an application using the API.

Here's my edit of the original example. I had to add some assumptions:

/* Finds the [message string] for the given [message ID]. If the [message ID] doesn't
 * exist, returns an empty String
 *
 * @param id  the [message ID] to find
 * @return    the [message string], or if the [message ID] doesn't exist, returns an
 *            empty String
 */
 public String findValue(int id) {...}
  1. By substituting [message ID] for item, I'm trying to point out that the documentation should be specific as possible. If you find that you're repeating the parameter's name in the description, then something may be wrong.
  2. Point 1 also holds for the return value.
  3. Ensure that the developer has handled the case in which there's no corresponding String for [message ID].
  4. Finally, as a good API tech writer, you should watch for programming problems. When a method returns a String, it shouldn't return null if there's nothing to return. Instead, it should return an empty String. This simplifies testing the method's return value.
  5. If not finding the item is a really severe error, meaning that the application should not continue, the method should throw an Exception. This Exception should also be documented.
1

As a user, you use API for certain purposes. You have certain goals you want to achieve, and the API is a tool that should help you achieve them.

Your problem is, how to achieve these goals.

Think of API "hardwareToolbox", which is a common toolbox in your garage. There's a hammer, there's a set of wrenches, a screwdriver, there's a hacksaw and an electric drill. Each of them are classes that have their methods. You also have nails, screws, screw anchors, etc. (all provided by right Factory objects.)

You want to hang a picture on the wall. You have no clue how to do it.

The right procedure is to drill the hole in the wall, put a screw anchor in, using a hammer, and then screw a screw in. As the user you know you need a screw in the wall, but not much more beyond this.

Now let's look at your documentation.

  Class Electric_hammer_drill. Makes holes.
  @methods: drill, hammer.

  method drill: Makes holes in material that doesn't need hammering
  @params: tool, depth, direction
  @return: hole

  method hammer: Makes holes in material that needs hammering
  @params: tool, depth, direction
  @return: hole


  Class Hammer. Applies kinetic impulse to objects.
  @methods: hit.

  method hit:  Applies kinetic impulse to object
  @params: object, strength, location

  Factory Screw_anchor: anchors screw to wall.
  @params: inner_diameter, outer_diameter, length

  Factory Screw: Binds things together
  @params: diameter, length.

  Class Screwdriver: Turns screws.
  @methods turn

  Method turn: Turns screw.
  @params: direction, rotations, hole, screw.

  ...and about 40 other tools and items you don't need.

Do you see where I'm going?

The user will deduce: I need a screw in the wall. To move screw I need screwdriver. Screwdriver requires hole. To make hole I need the drill. By a lot of trial and error I discover wall requires hammering, and a masonry bit for a tool to obtain a hole. (or I just drill one with wood bit, cursing the inefficiency of the API.) Then I try to fit the screw and it doesn't hold, and I'm all frustrated.

What you need is use patterns. Examples. PURPOSES.

Think of this.

  method drill: Makes holes in material that doesn't need hammering, like wood, metal or plastic. Check table@... for right tool for the material.
  @params: tool, depth, direction
  @return: hole

  method hammer: Makes holes in material that needs hammering, specifically concrete. Use masonry bit for a tool.
  @params: tool, depth, direction
  @return: hole

  Factory Screw: Binds things together. Requires hole of matching diameter in flexible materials like wood, threaded hole in metal, or a screw anchor of matching inner diameter in concrete.
  @params: diameter, length.

  Factory Screw_anchor: anchors screw to wall. Requires a hole of outer_diameter in concrete type material. Provides hole of inner_diameter in flexible type material. Should be installed using a hammer. Typical use is allowing installing screws in walls.
  @params: inner_diameter, outer_diameter, length

Basing on such a documentation the user should be able to deduce the correct procedure for installing the screw for the picture frame. It contains purposes, actual practical requisites (as opposed to formal), hints on how to use given tools, when to use them, and if they have specific requisites in specific situations.

Even better is to provide rich examples, but these are to appear outside the direct function documentation. Nevertheless, besides writing what a function does, write its what for (purpose - why'd you ever need that result), actual requisites (not just a list of params it might take, but params it absolutely needs, typical usage patterns (hammer concrete, drill all the rest, a screw anchor needs to be driven by a hammer) and caveats (screw won't hold in raw concrete, needs screw anchor.)

Just telling what a function does is not helpful if I never know why it does it, and how to get it to do it right.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.