Docs as code¶
Practical 7.3 docs-as-code-annex.pdf · PDF
In May you handed over a working server. Today Marta, who built it, leaves Tramuntana Apps. On Monday at 8:00, Pau the intern gets a phone call: the design share is not reachable. What does he open?
If the answer is server-manual-final-v3.docx, sitting in somebody's Downloads folder, there is a second question nobody can answer: which version of the server does it describe? Documentation that nobody can trust is worse than none. This page shows how professionals keep it trustworthy — with the same tools you already use for code.
What you will learn
- What docs as code means: plain text, versioned with Git, reviewed, built by one command
- Convert a Markdown file into other formats with Pandoc
- Follow the pipeline Markdown → Pandoc → Typst → PDF and say what each tool contributes
- Write a small template and a
build.shthat produces the PDF - Read a build error: which file, which line, what is wrong
- Why "the PDF is the document" is a trap, and what a page another technician can follow looks like
The idea: documentation written like code¶
Docs as code means writing documentation with the same tools as code: plain text files, kept in version control, reviewed by another person, and turned into the final format by a program.
| Ingredient | What it gives you |
|---|---|
| Plain text (Markdown) | Any editor opens it, on any system. Git can show what changed, line by line. |
| Version control (Git) | Who changed what, when and why. Any old version can be recovered. |
| Review (pull request) | A second person reads it before it becomes official. |
| A build (one command) | The PDF is produced by a program, the same way every time. Nobody edits the PDF. |
You already have the first three: you write your reports in Markdown, and the last two Git pages gave you a repository and pull requests. Today adds the fourth.
The pipeline¶
Back to the kitchen (we keep it all page)
In Unit 1 the computer was a kitchen. Here is a new dish. The Markdown files are the ingredients. The template is the plate: how the result looks. The script build.sh is the recipe: the exact steps, always the same. The PDF is the dish. If the dish is wrong, you do not repair the dish — you change an ingredient and cook again.
flowchart LR
subgraph SRC ["In the repository (the ingredients)"]
MD["<b>docs/*.md</b><br>the text"]
TPL["<b>template.typ</b><br>the plate"]
end
subgraph BUILD ["In build/ (ignored by Git)"]
BODY["<b>body.typ</b><br>text in Typst markup"]
ALL["<b>tramuntana-docs.typ</b><br>template + text"]
PDF["<b>tramuntana-docs.pdf</b><br>the dish"]
end
MD -->|"pandoc"| BODY
TPL -->|"cat"| ALL
BODY -->|"cat"| ALL
ALL -->|"typst compile"| PDF
Two tools, two different jobs:
Pandoc is a converter between document formats: it translates notation (Markdown into HTML, Word, Typst...) without changing the content.
Typst is a typesetting program: it lays out text on pages — margins, fonts, numbered headings, contents — and produces a PDF.
Careful: Pandoc and Typst do not do the same thing
Pandoc translates between notations. Typst lays out pages. Our recipe uses each one for what it does best.
Three short stories¶
- Markdown, March 2004: John Gruber, a writer, wanted text that reads well as plain text and still becomes a web page. Aaron Swartz helped shape the syntax. The first tool was one Perl script.
- Pandoc, 2006: John MacFarlane, a philosophy professor at Berkeley, wrote a converter between document formats for his own use. It now reads and writes dozens of them; people call it the Swiss army knife of documents.
- Typst: two students in Berlin, Laurenz Mädje and Martin Haug, wanted typesetting that is fast and readable. They opened the source code on 21 March 2023.
And since you can read licences now: Pandoc is GPL (copyleft), Typst is Apache 2.0 (permissive) — see Software by licence and purpose.
Installing the two tools¶
Pandoc comes from the Debian repositories. Typst is not packaged in Debian 13: it is one executable file, downloaded from the project's releases page and copied to ~/.local/bin, a folder you add to the PATH (the list of folders where the shell looks for commands, from Unit 5).
sudo apt install -y pandoc curl xz-utils
cd ~
curl -LO https://github.com/typst/typst/releases/latest/download/typst-x86_64-unknown-linux-musl.tar.xz
tar -xf typst-x86_64-unknown-linux-musl.tar.xz
mkdir -p ~/.local/bin
cp typst-x86_64-unknown-linux-musl/typst ~/.local/bin/
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
pandoc --version | head -1 ; typst --version
Your versions may be newer. If you get typst: command not found, the PATH line has not reached this terminal: run source ~/.bashrc or open a new terminal, and check with echo $PATH.
One source, three formats¶
Try Pandoc alone on your README:
cd ~/tramuntana-docs
pandoc README.md -o /tmp/readme.html
pandoc README.md -o /tmp/readme.docx
pandoc README.md -t typst
The first two write a web page and a Word file (Pandoc guesses the format from the extension). The third prints Typst markup on the screen:
$ pandoc README.md -t typst
= Tramuntana office server
<tramuntana-office-server>
Technical documentation of the Tramuntana Apps office server, Palma
\(Mallorca).
The Markdown heading # Title became the Typst heading = Title. The words did not change at all. Same content, another notation: that is all a converter does.
The template: the plate¶
The template holds the design of the PDF, and nothing else. Create template.typ, with your name, and leave an empty line at the end:
// Design of the PDF. The text comes from docs/*.md
#set document(title: "Tramuntana office server")
#set page(paper: "a4", margin: 2.5cm, numbering: "1 / 1")
#set text(lang: "en", size: 11pt)
#set heading(numbering: "1.1")
#show link: underline
#let horizontalrule = line(length: 100%)
#align(center + horizon)[
#text(size: 24pt, weight: "bold")[Tramuntana office server]
Technical documentation
Your Name · 1st DAM · June 2027
]
#pagebreak()
#outline()
#pagebreak()
- Lines starting with
#setare rules for the whole document: paper and margins, text size, numbered headings. - The centred block is the title page.
#outline()writes the table of contents by itself, from the headings.
Where is the text of the documentation? Not here. This file is only the plate.
The build script: the recipe¶
Create build.sh:
#!/usr/bin/env bash
# Markdown -> Typst -> PDF
set -e
mkdir -p build docs/img
pandoc docs/*.md -t typst -o build/body.typ
cat template.typ build/body.typ > build/tramuntana-docs.typ
cp -r docs/img build/
typst compile build/tramuntana-docs.typ
echo "Built build/tramuntana-docs.pdf"
| Line | What it does |
|---|---|
set -e |
Stop at the first command that fails, so a broken step does not produce a half-made PDF |
mkdir -p build docs/img |
Make sure both folders exist |
pandoc docs/*.md -t typst -o build/body.typ |
All the chapters, in the order of their file names, converted to Typst — that is why they are called 01-..., 02-... |
cat template.typ build/body.typ > ... |
Glue: template first, text after |
cp -r docs/img build/ |
The pictures travel with the text, so their paths still work |
typst compile ... |
Typeset the glued file into a PDF next to it |
Make it executable, build, and commit only the sources:
chmod +x build.sh
./build.sh
git status --short
git add template.typ build.sh
git commit -m "Add build script and template"
git status --short must not list build/: it is in your .gitignore since the Git basics page. If you get Permission denied on ./build.sh, the chmod +x is missing.
Careful: the PDF is not the document
The PDF is a product, like a compiled program. The document is the Markdown in the repository. Fix a typo directly in the PDF or in a .docx export, and the next build erases your fix. That is also why build/ is ignored by Git: anyone can cook the dish again.
Reading a build error¶
Break the template on purpose: change #outline() to #outlin() and run ./build.sh.
$ ./build.sh
error: unknown variable: outlin
┌─ build/tramuntana-docs.typ:17:1
│
17 │ #outlin()
│ ^^^^^^
Read every error in three parts:
| Part | Here |
|---|---|
| What is wrong | unknown variable: outlin — Typst does not know that word |
| Where | Line 17 of the glued file. The template comes first, so it is line 17 of template.typ |
| The exact place | The ^ characters point at it |
Repair it without even opening the editor: git restore template.typ, then ./build.sh again. The album pays off.
| Message | Usual cause |
|---|---|
unclosed delimiter or unexpected ... |
A missing ] or ) in the template, near the line shown |
pandoc: docs/*.md: withBinaryFile: does not exist |
You are not in ~/tramuntana-docs, or docs/ has no .md yet |
| An error in a line past the end of the template | The problem is in a Markdown chapter — often a table whose rows do not all have the same number of pipe characters |
Writing a page someone can follow¶
Good documentation is not long. It lets the reader act. The test for every page: could Pau do this at 8:00 on a Monday with nobody to ask? If a page only makes sense to the person who wrote it, it is a diary, not documentation.
There are two kinds of page — do not mix them in one paragraph:
| Kind | What it is | Example |
|---|---|---|
| Reference | Looked up, not read from the start | The table of shares and permissions, the list of ports |
| How-to | Followed step by step | Add a new employee, restore a deleted file |
A service page has five parts: Purpose · Installation · Configuration · Check · Sources. Here is what one looks like:
## File sharing (Samba)
**Purpose:** one shared folder per department, reachable from Windows and Linux.
```bash
sudo apt install samba
```
| Share | Path | Who can write |
|--------|-------------------|----------------|
| dev | /srv/shares/dev | group `dev` |
**Check:** `systemctl is-active smbd` answers `active`.
**Sources:** `man 5 smb.conf`, read 01/06/2027.
Three rules make the difference:
- "Install Samba and configure it" is not documentation. Which command? Which file? Which lines? Configuration goes in a fenced code block, or it loses its line breaks.
- Every command was run by you. Copy commands and output from your own terminal. An untested command in a manual is worse than no command.
- Never a password. Write where the secret is kept, never the secret itself.
In Practical 7.3 (the button at the top) you build this whole pipeline and write docs/05-services.md for one service of your own ubu-srv, then commit and push it. That repository is the start of the assessed documentation of the unit.
Key takeaways¶
- Docs as code: documentation in plain text, kept in Git, reviewed with pull requests, and built by one command.
- Pandoc translates between formats (Markdown → HTML, Word, Typst) without changing the content; Typst lays out pages and produces the PDF.
- The template holds the design, the Markdown holds the text, and
build.shis the recipe that joins them, the same way every time. - The PDF is a product: never edit it, change the source and build again;
build/stays in.gitignore. - A build error says what, where (file and line) and the exact place.
- A good page lets a stranger act: reference or how-to, tested commands in code blocks, a Check, sources, and no passwords.
Quick check¶
1. In docs as code, which one is the real document?
- The Markdown files in the repository
- The PDF produced by the build
- The Word file exported by Pandoc
- The web page generated from the text
The source is the Markdown in Git. The PDF, the .docx and the web page are products built from it: if you edit them, the next build erases your change.
2. What is Pandoc's job in the pipeline of this page?
- Translate the Markdown into Typst markup
- Lay out the pages and produce the PDF
- Upload the finished PDF to the forge
- Check the spelling of every chapter
Pandoc is a converter: it changes notation, not content. The layout into pages is the job of Typst, and uploading is the job of git push.
3. Why is the build/ folder listed in .gitignore?
- It can be generated again from the sources
- It contains the passwords of the server
- Git is not able to store PDF files
- Typst refuses to write into a repository
Everything in build/ is produced by ./build.sh from the Markdown and the template. Like any build output, it is a product, so it stays out of the history.
4. The chapter files are called 01-overview.md, 02-software.md and so on. Why the numbers?
- The pattern docs/*.md is read in name order
- Git only accepts files that start with a number
- Typst counts the files to number the pages
- Pandoc needs a number to detect Markdown
The shell expands docs/*.md in alphabetical order of the names, so the numbers fix the order of the chapters in the PDF.
5. The build stops with error: unknown variable: outlin, at line 17 of build/tramuntana-docs.typ. Where is the mistake?
- At line 17 of template.typ, a misspelt command
- In the last Markdown chapter of docs/
- In Pandoc, which must be reinstalled
- In build.sh, which lacks permission to run
The glued file starts with the template, so its first lines are the template's lines. The error says what (an unknown word), where (line 17) and the ^ marks the exact place: #outlin() instead of #outline().
6. You spot a typo in the finished PDF of the server manual. What do you do?
- Fix the Markdown source, commit and run the build again
- Edit the PDF with a PDF editor and save it
- Export a Word file with Pandoc and fix it there
- Leave it, because the PDF cannot be changed
In docs as code you never repair the dish: you change the ingredient and cook again. A fix made in the PDF or a .docx disappears at the next build.
7. Pau will use your service page alone on a Monday morning. Select the two things the page must have.
- The exact commands you ran, in a code block
- A check command with the output Pau should expect
- The administrator password, so Pau can log in
- A long paragraph explaining Samba in general
A page is good when a stranger can act with it: tested commands in code blocks and a Check that proves the service works. Never write a password, only where it is kept; and a table plus three tested commands beat two pages of prose.
Next: the brief asks for an inventory with costs. A list of numbers that must add up is not a job for Markdown — it is a job for a spreadsheet. Office and collaborative tools.