I’ve been around computers long enough to have encountered one problem many times. It’s a problem that seems to be almost built-in to computers: poor documentation. In this article I’ll focus on documentation of code that you use via its source code, e.g. an API, toolkit etc. There are mental tools you possibly already use for writing your code that can help with writing its documentation, and I’ll go through some of these in this article.
There are many things that can make documentation poor, one of which is thinking that including lots of detail is enough. This detail could have a few different forms, for instance lots of snippets of example code, or long lists of descriptions of methods.
I can understand how documentation like this can happen. The code author has worked hard on some code and has mentally ticked the project off as done. Then they think “Ah, documentation” and have to do this extra, non-code thing. The code is a tempting starting point for this documentation, as it has been the thing they’ve sweated over, it’s clearly visible in their IDE, and it’s the thing that that user will actually use.
However, often this code-first approach leads to poor documentation. The reason why is that the user won’t just learn the details of the code. They will need to create a mental model in their heads that provides scaffolding for the detail:
The mental model will help them to pick which bit of the code to use in which circumstance. As part of this, they will learn to predict what the code will do.
If you present only the detail (or not enough higher-level stuff), the user will have to reverse-engineer all this stuff for themselves. This can be painful, time-consuming and error-prone. It’s also a process that’s familiar to most, if not all, programmers. It is abductive reasoning – the process by which you try to guess at causes from symptoms. It is the process you use a lot when you’re debugging some code. Please don’t make me use my debugging skills to learn your code.
You might read the “code-first == bad” sentiment above, then the map of mental model + detail and conclude that I’m suggesting it’s best to always do a breadth-first traversal of the diagram. This is not what I’m suggesting. I’m suggesting a user-first i.e. user-centric approach. What does your user want to do with the code? How will the code help? What is the best way to get the user to the point where they can accomplish their goal using your code? This is simply user experience (UX) thinking applied to users of your source code.
Different users, or similar users but with different goals, might construct only parts of the mental model that they need, or construct it in different ways. If you can, I suggest that you approach things in a similar way to the I of the SOLID principles of software engineering – interface segregation. Don’t force the user to understand more than they need to, by forcing them to harvest higher-level information from lots of low-level examples, or by bothering about bits of your code they don’t need to use.
It might be that there is no simple way of serialising the web of concepts necessary to explain your code. In order to understand concept A, you first need to understand concept B. However, to understand concept B you first need to understand concept A. There’s a circular dependency between the concepts, which is what makes it hard.
It might be helpful to use a technique that can be used to break circular dependencies in code. You analyse the things involved in the circular dependency, and try to see where the existing things can be broken up into more, smaller, bits. Hopefully this can be done in such a way as there’s no longer a circular dependency.
In the case of source code, the bits might be classes or assemblies. Chopping out a part of a class or assembly into its own new class or assembly might be enough to break the circular dependency.
In the case of concepts, one way to do this is to break the concepts up into introductory parts and then more advanced parts. Hopefully the concepts can be at least introduced without a circular dependency. Once enough things have been introduced, then you can move onto a more advanced part of something, that can now reference an introductory part of something that’s already been covered. You can think of this as gradually lying less and less to the reader. Or, you can think of it of as spiralling outwards – a tight circle through just the introductions of everything, gradually working through increasingly advanced parts of topics.
It’s easy to think of your code as the most important thing, and have documentation as an outgrowth of that, if it even exists at all. However, taking a user-centric approach will probably work well. It’s likely to seem to be more effort in at least the short term, but putting yourself in your user’s shoes will likely lead to them being more successful, which is one ingredient in your being successful too. It can all be helped using techniques that you may have already developed as a programmer.