This site's database is a folder of HTML files in a git repository. That sentence gets raised eyebrows in 2026, so this article is the full argument: what a request actually costs here, what I get for free, what I knowingly gave up, and where the escape hatch is.
A request costs almost nothing, by construction
When you opened this page, no server of mine ran. There is no server of mine. The URL maps to a file; Cloudflare's edge cache handed you pre-computed bytes from a machine near you. Time-to-first-byte is a cache hit — tens of milliseconds — versus the 200–500ms a typical origin-plus-database render pays before the first byte moves. The site cannot go down on my account, cannot be slow under load, and a traffic spike costs me exactly nothing.
The rule that makes this possible: content here changes only when I publish, so everything is computed when I publish. Reading time, the RSS feed, the sitemap, the card grid, even the little plotter line above this article — all frozen into files by the publish engine. The browser draws; it doesn't derive.
What a commit buys that a database sells
- Transactions — a post, its card, the feed, and the sitemap change in one atomic commit. The site is never half-updated.
- Point-in-time recovery — the history is git history; rollback is a revert.
- An audit log — every publish is signed, dated, and diffable.
- Replication and backup — GitHub holds the data, Cloudflare serves copies of it from hundreds of locations.
- A schema — one denormalized record per post in a manifest, from which every listing surface is regenerated. No foreign keys, but also no drift.
Those are the features people install a database to get. A repository ships them as a side effect.
Request-time computation is the only thing that earns a server. Everything else is publish-time work wearing a costume.
What I gave up, on purpose
No request-time computation means no per-reader anything: no personalization, no view counters, no server-side search, and — the one that matters — no comments. Publishing takes about a minute of commit-and-deploy, so nothing goes live in seconds. Concurrent authors would collide in git; there's one of me, so that's a constraint I don't pay for. And listing pages grow linearly with posts — the static remedy is generated pagination, which the manifest makes a non-event.
Search sounds like it belongs on that list, but doesn't: an index built at publish time and queried in the browser holds comfortably into the thousands of posts. Static search is a solved problem; comments are not.
Comments will be a sidecar, not a migration
When comments come, the posts stay files. The plan is a deliberately boring standalone service — one worker and one small database that do comments and nothing else, keyed by the post's slug, rendered in a dialog on the page. The blast radius of that decision is one feature: if the comment service dies, the site doesn't notice. The content never migrates anywhere.
That's the whole architecture: files for everything that changes at publish time, a scoped sidecar for the first thing that truly changes at request time, and a hard rule for telling the two apart. Boring on purpose — and this page loaded before you finished reading the tab title.