Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

How can I write better code-based reference documentation for programming interfaces?

+1
−0

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.)

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.
Why should this post be closed?

0 comment threads

4 answers

You are accessing this answer with a direct link, so it's being shown above all other answers regardless of its score. You can return to the normal view.

+2
−0

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.

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

This post was sourced from https://writers.stackexchange.com/a/11012. It is licensed under CC BY-SA 3.0.

0 comment threads

+1
−0

One possibility on why the readers didn't find the documentation helpful may be that they are unfamiliar to the API, and the reference documentation is the only documentation available.

Reference manuals are there for the people who already are familiar with the API, and only have to look up the specifics of a function. It is almost always not suitable for people who are completely new to the API (an exception is e.g. a collection of math functions that are independent of each other).

This is a trap that especially projects using auto-generated reference manuals tend to fall into: They think they auto-generate reference documentation, therefore their API is adequately documented. But if your only documentation is the reference, then your API is not adequately documented.

As an example, take an imaginary graphics library.

If you look at the reference manual for the drawLine function, then you might find something like this:

void drawLine(Canvas c, GraphicsContext gc, Point start, Point end)

Draws a line on a given canvas, using the settings from the given graphics context.

Arguments:

  • Canvas c: The canvas to draw on. If Null, the function does not draw anything, but still updates the optional graphics context passed as second argument.

  • GraphicsContext gc: The graphics context to use. If Null, the default graphics context of the supplied canvas is used. If no canvas is given either (the first argument is Null, too), the call returns immediately without doing anything.

    The graphics context is used to determine the line style and drawing mode, and optionally the start and/or end point. On return, the initialPoint and currentPoint values of the graphics context are updated as described in the next two parameters.

    See the type documentation for GraphicsContext for details.

  • Point start: The point where to start the line. If Null, the currentPoint from the graphics context is used. If not Null, the value is stored in the graphics context as initialPoint on return.

  • Point end: The point where to end the line. If Null, the initialPoint from the graphics context is used. On return, the currentPoint of the graphics context is set to this value.

Now if you are familiar with the graphics library, this documentation contains everything you need. But if you are not familiar with it, it will raise more questions than answers: What is a canvas, and how do I get one? What is a graphics context? What's that thing about initialPoint and currentPoint?

Now for sure, you can find all this in the reference documentation; you'll probably start by looking up the Canvas and GraphicsContext types, which will then lead you to functions that can be used to create those types and manipulate the GraphicsContext, probably involving other concepts like devices, leading you to the documentation of the corresponding types and functions for those … basically you'll have to discover the structure of the API on your own.

And that's where a programmer's manual comes in: This does not tell you every detail of the API, but it tells you the high-level aspects. It presents you the key concepts of the API in a logical and easy to grasp manner, tells you which steps you have to do in which order to get to the point where you can issue drawing commands, tells you abut the most important differences between drawing on the screen and drawing in a file, etc.

Basically, the programmer's manual allows you to get familiar with the API, while the reference library tells you the details of every single function and type, assuming you are already familiar with the API.

The two manuals are different because they are for different audiences. Someone new to the API will mainly use the programmer's manual. Someone familiar with the API will exclusively use the reference manual.

And most importantly, a programmer's manual cannot be generated from comments on the specific functions. Sorry, you'll not get to avoid the work.

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

0 comment threads

+1
−0

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.

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

0 comment threads

+1
−0

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.

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

This post was sourced from https://writers.stackexchange.com/a/10997. It is licensed under CC BY-SA 3.0.

0 comment threads

Sign up to answer this question »