Whenever I am stuck on a documentation problem, I remind myself this mantra: don’t try to explain everything all at once. Technical documentation is tricky, and it can be tempting to continually tack on explanations and caveats to your docs everywhere. But what you are left with is just a more obscure answer to the original questions your doc sought to answer.
This principle applies to long-form narrative docs, but it can also be used as a heuristic across all types of documentation. Here are a few examples.
Should all copy-pastable code snippets be instantly runnable?
I think no. For starters, the majority of users aren’t going to copy-paste, fire and forget. If they are using the snippet in an app of any realistic size or complexity, the user will already need to edit the snippet, making adjustments to account for local variables, code style, or system requirements.
At the same time, having a runnable code snippet means having every variable defined, which necessitates introducing every concept in the code snippet itself. According to our mantra, this smells like trouble.
On the contrary, if you create a snippet with a variable missing, this communicates two things:
The missing thing is important
I need to go learn what that thing is separately (unless I already know what it means!)
and users will know they need to read some more docs to fill out the rest. You have to be considerate here to make this a pleasant developer experience, though. Make sure to use consistent variable names throughout the documentation, so users know they can associate the concepts by name. Also, make it easy for them to find out more about a concept when something is missing, either by linking to relevant docs from within the code snippet, or from surrounding documentation.
This might be controversial, but I would go as far to say that some code snippets don’t even need to compile. When the user pastes the snippet into their editor, their linter will immediately signal that something needs work, or at the very least they will run into a build error, not a runtime one. Running into these small errors (as long as they are easy to unblock) is actually a method for teaching concepts to developers using your software. As Jonathon Blow, indie game and programming language developer, puts it: “error messages are the API to your programming language.” As users encounter these issues, they learn what aspects are important, as well as the constraints of the system you are documenting.
What about boilerplates?
You might think users that want a boilerplate/starter/template repo would also want everything to be explained all at once. But the opposite is true! These sorts of users actually want to learn as little as possible (not as much as possible). They just want to get up and running as quick as they can, without being forced to delete most of the boilerplate code.
So, when creating a boilerplate, when you are tempted to try and explain concepts within the code itself, what do we tell ourselves? DTTEEAAO!
Putting documentation within your template repos means proliferating copies of the information everywhere. But what happens when that documentation needs updating? It becomes impossible to control the information dissemination, as copies have been scattered everywhere.
If the code in a boilerplate isn’t absolutely required—cut it. At the same time, this is why dynamic boilerplates, like Stripe’s integration builders, are so great. They let docs writers account for more use cases while still being as minimal as possible.
So what makes docs understandable?
Documentation isn’t always about being completely minimalist—you still need to explain the concept, after all—so adding context is still useful. However, you should aim to minimally explain a single concept, while linking and referencing other concepts for readers to jump off to.
Here are a few heuristics for gut-checking DTTEEAAO principles
Use the same variables names for like concepts.
Reduce your boilerplates to a minimal reproduction of a desirable application.
Frequently link to other concepts.
Create multiple entry points into learning a subject. Since you aren’t going to be teaching every concept all at once, this means giving users more pathways to learn the various concepts.