Documents first
Two things I want from this site:
- It should rank when someone searches my name, and the writing should be findable on its own merits.
- It should be fun — a desktop metaphor, draggable windows, a bookshelf you click into. The kind of thing that makes you poke at it.
These goals are in direct conflict, and pretending otherwise is how you end up with a beautiful site nobody can find.
Why they conflict
An OS-metaphor interface is a client-side application. The natural implementation is one URL, a JavaScript bundle, and content injected into windows on demand. Which means:
- A crawler requesting your page receives an empty shell.
- There is one URL, so there is one indexable page, so there is nothing to rank for any specific topic.
- Nothing is linkable. Nobody can send someone else to a thing on your site.
Every one of those is fatal to goal 1. And the failure is silent — the site looks perfect to you, because you have JavaScript.
The resolution
Separate the content from its presentation, and make the server responsible for the content.
Every piece of content on this site is a real URL that returns a complete,
semantic HTML document. /writing/documents-first is a full article, server-
rendered, no JavaScript required. That is the canonical representation.
The desktop shell is a layer on top. It intercepts link clicks, fetches the target URL, and paints that same document into a window instead of doing a full page load. Same content, different presentation.
So a crawler gets a document. A human gets a desktop. And critically — if the JavaScript fails to load, or is disabled, or a browser I've never tested chokes on it, the site degrades to a perfectly good document site rather than a blank page.
Isn't that cloaking?
No, and the distinction is worth being precise about, because getting it wrong is a manual-action-from-Google kind of mistake.
Cloaking is serving different content to crawlers than to users. Keyword- stuffed text for the bot, a sales page for the human. That's deceptive, and it's penalised.
Progressive enhancement is serving the same content with different presentation. That's just... the web working as designed. CSS changes presentation. JavaScript changes presentation. Nobody has ever argued that a stylesheet is cloaking.
The test I hold myself to: if I diff what the crawler receives against what the human ultimately reads, the text should be identical. Only the chrome around it differs.
Enforcing it
Good intentions decay. So the invariant is a test rather than a note in a README:
#[tokio::test]
async fn content_is_in_the_server_rendered_html_not_injected_by_js() {
for path in INDEXABLE_PATHS {
let (_, body) = get(path).await;
assert!(body.contains("<h1"), "{path} must render an <h1> server-side");
}
}
If I ever get clever and move content ownership into the shell, that test goes red. It's a tripwire aimed squarely at future-me, who will absolutely be tempted.
There's a second one I like more. The sitemap is generated from a single route manifest, and a test walks every URL the sitemap advertises and asserts it returns 200:
for url in sitemap_urls {
assert_eq!(get(url).await.0, StatusCode::OK,
"sitemap advertises dead URL {url}");
}
A sitemap listing URLs that 404 is worse than having no sitemap — it burns crawl budget and signals a stale, unmaintained site. That's exactly the kind of rot that accumulates silently over years and that no human ever notices.
The build order falls out of this
Because the shell is additive, it's also last. The document layer ships first and completely. Then content. Then the interactive bits. Then, only once all of that is genuinely done, the fun part.
That's the opposite of the order I want to work in. It's also the order where, if I lose interest in month three — which, historically, I do — what's left standing is a complete, working, findable site rather than a half-built desktop metaphor with no content in it.
The previous version of this site stalled twice. I'd like this one not to.