When I edit a page of documentation, I used to skim past the code blocks. They looked technical, someone had presumably tested them, and my job was the prose around them. That was a mistake. The code samples are often the only part of a page a reader actually uses. Many people jump straight to the first grey box, copy it, and paste it into a terminal. If it fails, the clear paragraph above it doesn't help much.
So now code samples get an editing pass of their own. Here is what I check.
Can it be copied and run as is?
This is the first question, and the answer is "no" more often than you'd think. The usual problems are small:
- A shell prompt (
$or>) at the start of each line, which gets pasted along with the command - Command and output mixed in one block, so the reader copies both
- A step that depends on something the page never mentioned, like being in a certain folder
- Curly quotes that crept in when the text passed through a word processor
The fix for the first two is the same: keep commands and output in separate blocks. Put the command in a block marked as bash, and the output in a block marked as text, introduced with a short line like "You should see something like this:".
Are placeholders obvious?
A sample like git clone https://example.com/project.git is fine as long as readers know which part to replace. When they don't, they either paste it unchanged or guess. I ask writers to use one style for placeholders across the whole set of docs, for example <your-project-name>, and to say right after the block what each placeholder means.
The angle brackets have a useful side effect. If someone pastes the line without replacing them, the shell treats < as a redirect and the command fails straight away, instead of quietly doing the wrong thing.
Does the text match the code?
Prose and code drift apart. A paragraph says "set timeout to 30" while the block below sets timeout: 60, because someone updated one and not the other. I read each sample next to the sentence that introduces it and check names, values and file paths line by line. It's slow and boring, and it catches something more often than I'd like.
Is it the shortest sample that works?
Long samples hide the point. If a page explains one configuration option, the reader doesn't need forty lines of unrelated settings around it. I suggest cutting to the lines that matter, and saying in the text that the rest of the file stays as it is.
Let a script do the dull part
In a docs-as-code setup, the pages are Markdown files in a repository, so some of these checks can run automatically. This small Python script finds every bash or sh block in the files you give it, flags lines that start with a $ prompt, and asks bash -n to check the syntax without running anything:
import re
import subprocess
import sys
from pathlib import Path
FENCE = re.compile(r"^```
(\w+)\s*\n(.*?)^
```\s*$", re.MULTILINE | re.DOTALL)
def check_file(path):
text = path.read_text(encoding="utf-8")
problems = 0
for match in FENCE.finditer(text):
lang, code = match.group(1), match.group(2)
if lang not in ("bash", "sh", "shell"):
continue
line = text.count("\n", 0, match.start()) + 1
if re.search(r"^\s*\$ ", code, re.MULTILINE):
print(f"{path}:{line}: prompt '$' inside a copyable block")
problems += 1
result = subprocess.run(["bash", "-n"], input=code,
capture_output=True, text=True)
if result.returncode != 0:
print(f"{path}:{line}: syntax error\n{result.stderr.strip()}")
problems += 1
return problems
total = sum(check_file(Path(p)) for p in sys.argv[1:])
print(f"{total} problem(s) found")
sys.exit(1 if total else 0)
Save it as check_samples.py and point it at your docs:
python3 check_samples.py docs/*.md
On a test page with a prompt in one block and an unclosed if in another, it reports:
docs/install.md:3: prompt '$' inside a copyable block
docs/install.md:7: syntax error
bash: line 3: syntax error: unexpected end of file
2 problem(s) found
The line numbers point to the opening fence of each block. The script exits with status 1 when it finds something, so it can run as a step in CI and fail the build.
It won't tell you whether a command does the right thing, only that it parses. That still matters: a sample that can't even be parsed is one nobody tested.
Then run them for real
The last check is the one no script replaces. Before a page goes out, I follow it from top to bottom in a fresh terminal, copying every block exactly as a reader would. Whatever breaks is an edit, even if the prose around it is perfect.
Top comments (0)