I've been thinking for a long time about anti-patterns I see when following software tutorials, so I put together this list of things I think differentiate good tutorials from poor ones.
I'm happy to hear any feedback on this list or hear about other things I should include.
There’s probably a million of those already.
Yeah, I see what you're saying. I had this article[0] in mind, which is a well-written article and ranks #1 for "organize front end code django" on Google. None of the other results are close in relevance or quality.
But I guess that example could be confusing for readers who aren't familiar enough with Django to know why structuring the frontend is a hard problem that not many people write about.
[0] https://www.saaspegasus.com/guides/modern-javascript-for-dja...
1. Many tutorials reference many languages. (I frequently write tutorials for students that include bash, sql, and python.) Providing the prompts `$`, `sqlite>` and `>>>` makes it obvious which language a piece of code is being written in.
2. Certain types of code should not be thoughtlessly copy/pasted, and providing multiline `$` prompts enforce that the user copy/pastes line by line. A good example is a sequence of commands that involves `sudo dd` to format a harddrive. But for really intro-level stuff I want the student/reader to carefully think about all the commands, and forcing them to copy/paste line by line helps achieve that goal.
That said, this is an overall good introduction to writing that I will definitely making required reading for some of my data science students. When the book is complete, I'll be happily buying a copy :)
>Many tutorials reference many languages. (I frequently write tutorials for students that include bash, sql, and python.) Providing the prompts `$`, `sqlite>` and `>>>` makes it obvious which language a piece of code is being written in.
I think it's fine to show the prompt character, but I think it's the author's job to make sure that copy/paste still works. I've seen a lot of examples that use CSS to show the prompt or line number without it becoming part of copied text, and I'm highly in favor of that.
I think if I had to choose between breaking copy/paste and making the language obvious with the prompt character, I'd exclude the prompt, but I think that's a matter of taste.
>Certain types of code should not be thoughtlessly copy/pasted, and providing multiline `$` prompts enforce that the user copy/pastes line by line. A good example is a sequence of commands that involves `sudo dd` to format a harddrive. But for really intro-level stuff I want the student/reader to carefully think about all the commands, and forcing them to copy/paste line by line helps achieve that goal.
Yeah, I agree about preventing the reader from copy/pasting something dangerous.
In tutorials that require a reboot, I'll never include a reboot command bunched in with other commands because I don't want the user to do it by mistake. And I agree for something like `dd`, you'd want to present it in a way to make it hard for the reader to make mistakes or run it thoughtlessly.
This is unfortunately not compatible with writing the tutorial in markdown to be rendered on github.
I hardcore oppose this kind of thing, for the same reason I oppose people putting obstacles in the way of curl-to-bash.
Adding the prompt character doesn’t make people think, it just makes people press backspace. Frequently I’m reading a tutorial because I’m trying to assemble headless scripts for setting up a VM and I really just need verbatim lines I can copy/paste so I know I’ve got the right arguments.
My books are Python related, so there is code that runs in putting and code that runs in other environments.
I guess I'm not really writing "tutorials" in the sense of webpages, so I'm less concerned with copy paste working and more concerned with clarity.
I've written several in-house tutorials to our internal product, and I've come to internalize your advice somehow.
IMO it's mostly about empathy. I remember myself as a newbie perplexed by this product and try to write to my younger self. I also make a point to always spend a lot of time with new hires and get their feedback and their viewpoint.
> Write for beginners
I prefer "write for your audience". For example, in the first lesson for my canvas library[1], I'm aiming at an audience of front-end developers - they should know how to code a very basic HTML page and start a local server to view it.
Of course, my lesson fails here. I'll add a line near the top of the copy to say who the intended audience is, and a link to the MDN frontend developer course[2] for any complete beginners who (somehow) found the lesson.
> Make code snippets copy/pasteable
Maybe I'm an outlier but, when I blindly copy-paste code, I rarely learn about that code: it's just some magic that I feed into the computer to get results. I learn better when the tutorial forces me to type out the code (and I do nasty things in my tutorial to persuade learners to type rather than copy-paste).
[1] - https://scrawl-v8.rikweb.org.uk/learn/first-lesson
[2] - https://developer.mozilla.org/en-US/docs/Learn_web_developme...
#2 is too far. If people want to misuse your stuff, shame on them, but don't boobie trap it!
Like in your case, you're writing for frontend developers who are not new to making webpages, but they're beginners at using the canvas element.
Actively deprecate.
I don't expect authors to write a new tutorial whenever the syntax for doing something changes. But it would be unbelievably helpful if you could just stick a banner on the top of a piece saying "this doesn't work anymore" as soon as that's true (which, unfortunately, is usually about three months after you write it)
I do not know of a case where a recording is not better.
"Oh, but what if you have to update what you do"
Then when you practice it for your presentation, record it and put that in.
"Oh, but I don't want to edit the video"
If you have to edit the video to take out the useless parts, you are just leaving the useless parts in during the live coding.
"Oh, but I enjoy it"
The presentation is for the audience.
"What if they ask a question"
If they ask a question about what you did, you can easily replay it.
If they ask a question beyond that, you can also dive into live coding, but that's the exception.
Sometimes this advice of describing what something is and why you'd use it is helpful. Other times I already know what it's for and just want to use it, and don't like having to slog through descriptions that don't help with implementation. There is no universal right or wrong answer here, since it will depend on who is reading your tutorial, what their level of expertise is, and what they need.
A tutorial can provide the best of both worlds by putting the background stuff of each section in a sidebar, so the reader can easily skip it or delve more deeply if they wish.
Especially one that got my heart:
> Some authors design their tutorials the way you’d give instructions for an origami structure. It’s a mysterious sequence of twists and folds until you get to the end, and then: wow, it’s a beautiful swan!
> A grand finale might be fun for origami, but it’s stressful for the reader."
Yes, a lot of tutorials are puzzle games. You need to guess other tools to install, which are obvious to the author, but not everyone.
I would add a few things:
* Have two types of examples - minimal and the most typical (one is to show the essence without distraction, the second to serve as a practical starting point) * Always provide the full code sequence to run code; if there are any assumptions, these should be listed explicitly (e.g. "it needs Node 22"); you hinted a bit, but since many tutorials miss that, it deserves attention.
Even better, if it is possible to combine it into one point and make it runnable. For example, for a package I had been developing, livelossplot, I made Jupyter Notebooks runnable in Colab:
* https://colab.research.google.com/github/stared/livelossplot... * https://colab.research.google.com/github/stared/livelossplot...
For example, if I'm P. Shirley and I've written a book (with code) about following light rays between a virtual camera and virtual objects. I have refactored some code to be more clear. Of course, I have iterated on this code to verify it remains correct, but the related text isn't affected. I have to remember where to paste this new code. Unless the documentation had some kind of reference to the specific file, lines, and/or function to render into the text.
What's the advice for best practices here?
As an actual example of this, here's a plugin for Remark that allows us to import specific functions from a rust source file. https://github.com/ratatui/ratatui-website/blob/main/src/plu... If the source code fails to compile, we get a CI error (in the rust checks). If the source code changes in a way where the import is no longer correct, we get a CI error (in the npm checks).
In my opinion, the open source nature really is the key thing here. Most of the issues are small things (typos, confusing verbage, etc). So it get's refined as people read it.
> 5. Make code snippets copy/pasteable
I like to use the prompt to identify the language and also the user (root `#` vs. user shell `$`) to perform the action under. I advise against copying random commands off the internet especially those that run as root and may break your system (like `apt` and `--yes` together...). Of course, today, it is often assumed that you have sudo setup and then `sudo ...` is used to indicate the use of root privileges. It is an interesting convention although in my opinion this sometimes hides the actual intent.
I prefer:
# echo 3 > /proc/sys/vm/drop_caches
over echo 3 | sudo tee /proc/sys/vm/drop_caches
The example given in the article is exactly one where attention is required: It adds a PPA as root, thereby giving "full control" to the PPA author. Never straightout copy-paste such stuff.> 6. Use long versions of command-line flags
I am so much more used to the short versions hence I prefer `grep -RF` over `grep --dereference-recursive --fixed-strings`. Also, my go-to documentation about these commands (POSIX) doesn't even know about the long options.
I know that the advice to prefer the long options is repeated pretty often so it might just be me :)
> 7. Separate user-defined values from reusable logic
Ever followed some "enterprise-grade" software setup tutorial? Sometimes they start by assigning 20 variables to some generic values that you don't change most of the time and then go about to reference them on the next 20 pages or such making it close to impossible to know what is really going on. Also I find that for simple cases this greatly complicates the input process.
I think the example doesn't really show the difference because it has some variables (just not in shell syntax) in the code already like `YOUR-API-TOKEN`. Of course it is better to write `$API_TOKEN` rather than `YOUR-API-TOKEN`. I often prefer to see the example value in the example i.e. not `YOUR-API-TOKEN` but some real string (if feasible). In many cases I won't change a value in the beginning to see how far I get with a “known valid input”. Also, I often prefer to pattern-match some real value against what I have available, e.g. I have a key which I think could be the API key but if the format is totally different from the tutorial I may realize that this is an "app token" rather than "API key" and that I may need to get a different value if it fails to run with my input or such.
> 9. Let computers evaluate conditional logic
If dosed correctly, I think this is good advice. I'd strongly prefer the conditional boxes from the “bad” example over the “good” monster shell script command. In my opinion a tutorial shouldn't optimize for speed of execution but rather foster the understanding by the reader. There is no help if I find this post 10 years later and it helpfully outputs “ERROR: Unsupported platform” on my Debian 16 system. If the text instructions are given as in the “bad” example, I quickly realize that all of this is outdated and I should probably try with the most recent package name (example-package2) rather than meddle with some script.
It is not just you.
I also prefer short options for most “standard” commands (e.g. everything POSIX). I don’t see any value in educating readers in the long options of grep, or ls, or cat.
OTOH using long options may be useful for less common commands, or if the commands are specific to the software the tutorial is about.
Some times it is better to let the user think for themselves rather than provide a "paste this magic script". If you are going to use a script, you are kindof taking away the purpose of a tutorial; to learn each individual thing step by step, not how to run another black box.
For this reason I am building a new way of writing coding tutorials, with the possibility of writing and running code snippets into the web browser including graphical apps: https://exalib.com
I also find this sometimes.
A high quality tutorial on the other hand, is often one where the author bothered to set up a fresh VM and follow their own guide step by step to confirm that everything works as described. This way the author avoids unintentionally forgetting to mention additional dependencies that have to be installed and additional config files that need to be tweaked in order for the steps to work.
I'll nitpick a bit.
You can't tell your user to "echo 'awesomecopter' | sudo tee /etc/hostname" if you may have windows users.
And even for purely linux user, "Good: Give the reader a bash snippet that evaluates conditional logic for them" is quite a scary wall of code for a beginner that as no idea why copy/paste that.
All in all, don't confuse "giving the shortest path to a working solution" and "teaching", which both can be in a tutorial, but they have different goals and you should choose accordingly.
I think the "Teach one thing" is probably the best advice in the article, and too often ignored. I remember as a kid reading Swinnen's book to learn Python and having to understand OOP with his example using ions was a terrible experience.
And if you think "well you should have known that, they teach it in school", you are missing the point.
That being said, writing an excellent tutorial takes a lot of work. Way more than a good-enough one. Choose wisely.
But again, it's a LOT of work to do that.
E.G:
To write "https://www.bitecode.dev/p/back-to-basics-with-pip-and-venv", I need to be able to rely on "https://www.bitecode.dev/p/installing-python-the-bare-minimu..." which in turn needs to rely on "https://www.bitecode.dev/p/ultra-beginners-first-steps-for-t...".
That's almost 9000 words, or 10% of the average novel size just to give beginners a good chance of success on one single topic.
Between doc, tutorials, FOSS and forums, the entire software industry is just standing on a gigantic pile of billions of hours of free labor.
One thing that doesn't help all this is more and more tutorials going on Youtube (I admit I've made a couple, which were topic focused) is that so many people just want an entire soup to nuts answer instead of the tools to piece together their own solution from the parts, which makes gaining traction and getting that information to people a lot harder.
But without a general change in how people look at learning I dunno what fixes that problem.
A couple more of my own:
TEST YOUR DOCS
As @codetrotter suggests in another comment, run through your own tutorial with a fresh VM and follow it closely; you'll almost certainly find something to improve.
But this works better if you have someone else try it out instead: get on a video call with them, have them share their screen and speak their thoughts out loud, and most importantly, don't help them. You're trying to find out if they can complete this without additional help. (Yes, it's a usability test.)
BETTER SOFTWARE MAKES SIMPLER TUTORIALS
The article's rule about "let computers evaluate conditional logic" is a good one, but there are multiple places to fix that problem. Sometimes it's in the tutorial, sometimes it's in the software product itself. When the tutorial makes the reader do complex work, it may be a sign that the software has UX issues. You may not be in a position to fix it, but you can probably file an issue.
For the example in the article: could you make the same package name work on all three Debian versions? It'd certainly improve the user experience, and probably make the installation process more resilient.
Students hate it when your code doesn't work.
I usually supply a GitHub repo or gist, with either a full app, or a playground. In many cases, I’ll have a full app, already running, with just one source file, that we’ll be modifying. This may be in a tagged GitHub repository or gist, with tags, denoting steps[0]. In other cases, I’ll supply a zip file, with everything in it[1].
I can take a month, developing a tutorial that can be run in an hour.
Teaching is hard. A good trainer spends many hours, preparing for relatively short classes. It’s sort of like playing a concert; you only get one chance, so preparation is key. Also, in tech, the students are often more accomplished than the teacher. It can be a tough room.
That said, I have attended some valuable tutorials, in which the teacher did a terrible job, preparing. It’s just that I don’t work that way, myself.
The main reason that I write tutorials, is to teach myself. I don’t think anyone else really pays much attention to my work[2]. I have presented a few “official” classes[1], but mostly, I work on my own. I have, actually, been teaching for decades, in small, short, seminar form; mostly for non-technical stuff.
[0] https://github.com/LittleGreenViper/DemoCustomChartApp/tags
[1] https://github.com/ChrisMarshallNY/ITCB-master
[2] https://littlegreenviper.com/miscellany/ (not every series is a tutorial).
Yes.
> But this works better if you have someone else try it out instead
1000x yes.
Yoz, you are wise and while what you say may seem like an obvious thing, it isn't. I was once in the unfortunate position of having to write manuals for software I did not design, so I went through it thinking I was the "average user." As it turns out, I was under the false impression that my documentation was perfectly understandable to anyone, to the point where they could sit down, rtfm and go.
Boy was I wrong.
I later got into an area of work where I had to help individuals with cognitive disabilities adapt to new workflows in factory settings. One of the exercises I had to do to earn my certification in this field was to imagine an individual that had never seen a broom or dustpan before, then use only photos to explain how to sweep a floor. It's far more challenging than you think. I realized that not only was I making the mistake of explaining things to myself, but I was also assuming that I could put myself in someone else's shoes and interpret my own instructions with fresh eyes. This, as it turns it, was folly.
That experience propelled my documentation forward by leaps and bounds, though. In my current position, I came up with over a dozen single-page instructions to resolve issues customers were having with our products that, previous to my working here, generally took an hour-long phone call, eating up time and resources on both ends. Each page was tested with a coworker or willing customer that had never worked with the product before with welcomed feedback. The result was a significant drop in time wasted on these otherwise simple fixes, allowing us to redirect our big guns at the more complex issues that arise.
For anyone interested in the kinds of pitfalls we fall into when we think we can bridge the gap between two minds with our "exact" instructions, check out this video. I think it does a wonderful job of conveying these problems in communication in a really fun way.
Exact Instructions Challenge - PB&J: https://www.youtube.com/watch?v=FN2RM-CHkuI
If you take _nothing_ else away from the article and/or this thread, take this away!
I pride myself on the quality of docs that I produce and my "secret" is enlisting guinea pigs. Before linking somebody to the doc(s) they need, I'll send them a message akin to "if you get stuck at literally any point or encounter literally any error or shell output that does not match what's in the docs, stop and message me. We'll work it out together and the docs will be better for the next person"
Do that a few times and you have a high-quality document that'll cover the process and any known edge cases.
The critical bit is that the person that needs the doc is already not having an awesome day; make it clear to them that you're here to help and not in a "come to me after you've spent 2 hours trying to make it work" way.
if err != nil {
// handle errors
}
Or: try {
// something that can fail
} catch (Exception e) {
// handle exceptions here
}
It's usually laziness from the author, but not stating _what types_ of error may occur and how should typically handle them is a plague on documentation and tutorials.Naw, copying and pasting won't develop touch typing nor muscle memory skills. The prompt is sort of a context indicator for the command, as other comments indicate. Also if you're using one of those horrid browsers with CSS support who knows if there are characters that do not display but are included in what is copied. Security risk? Well, you started a chonky browser with a bad security record, so uh additional security risk. Also they probably should be able to type out the commands by hand at the speed of thought, or the commands should be put into a Makefile or script or done via configuration management. Making it easy to copy and paste? Well, if you want to train people to mindlessly copy-n-paste who knows what to who knows where, I guess. Doesn't sound like that would train people to engage deeply with the material. If there's a bigger code example I just put a "=> foo.lisp" link '(this is for Gemini (no, not the Google whatever nonesense (McCarthy invented AI to get money)), the modern web being too much yuck these days) so they can download the whole file.
> Use long versions of command-line flags
What are these? I'm on OpenBSD. More seriously, long flags may or may not help±quick! what does --fake-super for rsync(1) do?—and taking a peek into the manual (another reason I'm on OpenBSD) is sort of a spaced repetition to refresh you on what the flag does, and maybe to scan the list of options to see if anything new jumps out at you (or, at a certain age, what you've forgotten). Close engagement with the material (howeversomuch the learner hates this, as such brain exercise uses up precious energy) is probably a good thing.
(Yes, I know that OpenBSD supports but generally does not document various wacky --from-backside-of-gnu flags.)
> Teach one thing
I am too fond of puns (and have ulterior motives, given the excess of car sitting in America, and the consequent loss of blood to the brain) to apply this in any meaningful way.
While I'm unfamiliar with how it's implemented there, I can certainly see this working with multiple rounds of prompting, and possibly some external data sources and human curation to ensure that the terminology index is actually useful to readers.
It’s an excellent example of clear, useful writing, about a topic that could use all the help it can get.
Specify up front date and software version. I can't stress this enough.
As the CADT release cycle becomes ubiquitous we're approaching a situation where many tutorials are outdated by the time you finish them.
Cascade of Attention Deficit Teenagers
Especially so when it comes to the different package names on different Debian versions: what originally was a oneliner is now fifteen lines long with six different branches. What should the reader do if the copy-pasted code fails? How do they know which part failed? What is an "/etc/os-release"? Yes, the happy path becomes simpler, but the reader will struggle to recover from any errors.
I'm cursed (blessed?) with fish memory so after a few days I forgot everything I wrote.
Tutorials are "best-effort" and often don't dwell on the rabbit-holes they create when a poor soul runs astray of the sunny path.
Example of a good tutorial:
Python's powerline shell: https://github.com/b-ryan/powerline-shell
The README is succinct. Well put together.
Unfortunately if you don’t use that, your beginners will have hard time to believe you are “guru” that they seek. Like if they find other guys tutorial with fancy words they will flock there - looking for some magic trick or some revelation that will hopefully come down on them while reading bunch of hard words without understanding.
Most tutorials are like that for this reason. If you want to teach people author is right - if you are cynical and just want to earn money on a tutorial that’s a different thing. One upside - people quitting “hard” course most likely will be embarrassed to ask for return of money as they would feel it is their fault they did not learn as topic is hard.
If they feel topic was easy and they did not learn, that’s probably guy making tutorial not knowing enough.
My cynical take :)
Those are near perfect.l tutorial.
Things change a lot between Version 3 and 6 of something, and something written 5 years ago may no longer apply or be valid today.