Writing the Docs Site in Mere: Dogfooding at Application Scale
A language can pass every test and still be miserable to build anything real in. The way to find out is to build something real — so Mere's own documentation site was to be generated by a tool written in Mere. The point wasn't the site; it was what the attempt exposed: a prioritized list of which primitives were actually missing, and a friction you only hit by feeding real documents through real code.
By the end of Part IV, Mere runs four ways, holds them to identical output, and can talk to code on either side of itself. Every one of those is verified by tests. But there is a question no test suite answers: is the language actually pleasant to build something in? A language can be correct and still be miserable — the standard library full of holes you only notice when you reach for them, idioms that turn out awkward the third time you write them. The only way to find out is to build something real, and the project’s chosen something was pointed: generate Mere’s own documentation site with a tool written in Mere.
Why the docs site, and why that shape
The choice is deliberately self-referential. Mere’s documentation — a tutorial, a language reference, a standard-library reference, a page of idioms, and more, some thousands of lines of Markdown across nine files — needs to become a browsable website. Writing the static-site generator that does that conversion in Mere makes the exercise prove two things at once: the docs get built, and the language demonstrates it can carry a real tool. “A tool written in Mere generates the documentation for Mere” is a claim of practicality you can point at, not assert.
And most of the pieces already existed, as contrib libraries built along the way: a
Markdown-to-HTML converter, a {{KEY}} template engine, a command-line argument
parser, a JSON reader. The site generator is largely a matter of wiring those
together — read the Markdown files, convert each, wrap them in a template with a
navigation bar, write out an index and a stylesheet. On paper it looks like
assembly, not invention.
The real product is the gap analysis
What makes dogfooding valuable is not the artifact; it is what the attempt reveals. Sitting down to actually build the generator turned a vague sense that “the standard library has gaps” into a specific, prioritized list of exactly which ones bite.
Some gaps needed real operating-system access and so had to become new built-ins:
listing the files in a directory, and making a directory tree, are POSIX calls the
language couldn’t yet make. Others turned out not to need the compiler at all —
joining paths, taking a basename, a dirname, an extension are all a few dozen lines
of ordinary Mere, so they became a small contrib/path library rather than
built-ins. Front-matter parsing — the --- title: ... --- block at the top of each
document — needed only a minimal slice of YAML, key-and-value with no nesting,
another short library. That triage is the actual deliverable of the exercise: not
“the stdlib is incomplete,” which is a shrug, but “these two things need OS
primitives, these four are pure library code, in this order” — a work-list produced
by real need instead of guessed at in the abstract.
The decisions the build forced were all in the same key: take the minimal honest version and defer the rest. Front matter is a reduced YAML, not the whole sprawling spec — and notably not Mere source evaluated as configuration, which was rejected outright as too powerful and too large an attack surface. The generator starts as a single command-line program, to be split into a reusable library only if someone later wants the pieces separately. Each is the discipline the language preaches, now applied to the language’s own tooling under the pressure of a real task.
The friction only real documents produce
Then there was the bug you cannot design your way around, only discover. The
template engine marks its substitution points with {{KEY}}. Mere’s documentation,
being documentation about a programming language, contains {{KEY}} sequences as
literal text in examples — and the template engine, seeing them, tried to
substitute. The tool for building the docs collided with the content of the docs.
This is exactly the class of problem the previous episode found by compiling thousand-line programs: it is invisible in any small test, because a small test doesn’t contain the adversarial input that real content does. You do not anticipate that your docs will talk about your own template syntax; you find out when the docs come out mangled. The fix is mundane — an escape convention, or a different delimiter — but the finding is the whole value of dogfooding. Real content is an adversary no test author thinks to write.
The warm-up for the real thing
Building the docs site is the project’s methodology — paper trial, then dogfood — applied at the scale of a whole application, and it closes Part IV on the right note: code generation isn’t proven by passing tests, it is proven by carrying a real program. The site generator showed Mere could build a genuine tool, surfaced the exact stdlib gaps that mattered, and hit the exact frictions that only real use produces.
But generating documentation is a small self-reference — a Mere program processing text about Mere. There is a far larger one waiting, the one this whole series has been building toward: a Mere program that processes Mere itself. If the language can carry a docs generator, can it carry its own compiler — a lexer, a parser, a type checker, an evaluator, a code generator, all written in Mere, compiling Mere? That is self-hosting, and it is the subject of the next part. Next, Part V opens: writing the front end — lexer, parser, and a shared syntax tree — in the language itself.