This blog ran on Hugo with the PaperMod theme for four or five years. On June 15 I threw the whole thing away and rewrote it in Astro.
The interesting part isn’t the framework choice. It’s how the rewrite happened: I typed /goal in Claude Code, described what I wanted, and went to bed. The next morning, a working Astro site was sitting in a new repository. The migration commit tells the story in one line: 184 files changed, 95,175 insertions — every published post carried over, every URL intact, every comment thread still attached.
Why leave Hugo at all
Hugo itself was never the problem — it’s fast and it’s stable. The problem was living on top of PaperMod. Updates slowed to a crawl, known bugs sat unfixed for months, and every release I did pull was a small gamble on what my overridden partials would collide with. And fixing things myself meant fighting Hugo’s template DSL — which isn’t really Go, even if you write Go: logic lives inside markup, there’s no component model, composition happens through partials and dict gymnastics, and when something breaks the error points into the theme’s internals instead of your code. Over the years my workarounds accumulated as a pile of overridden partials I was afraid to touch.
The honest reason I stayed so long: a full rewrite was a multi-weekend project, and the blog worked. That math changed.
The overnight migration
I started from AstroPaper (v6 at the time, since upgraded to Astro 7) rather than a blank page — a solid, minimal base to mutate. The overnight /goal run handled the tedious parts that used to make these projects die in a branch:
- 80 published posts (plus two dozen drafts) with frontmatter mapped from Hugo’s format to Astro content collections —
date→pubDatetime, AI summaries →description, tags and drafts preserved - Filenames kept as slugs, so every URL stays
/posts/<slug>/— nothing 301s, nothing breaks - Comment threads preserved by pinning each page’s comment key to the old Hugo permalink, trailing slash and all — a one-line detail that would have silently orphaned years of comment history
- KaTeX, RSS, sitemap, Pagefind search, and OG image generation (Satori, with a CJK font embedded so Chinese titles render) re-wired
I reviewed the result over coffee and filed nitpicks. That was the migration.
Astro 7 is quietly very fast
I started on Astro 6 and upgraded to 7 mid-rewrite — a single commit. It’s worth a section, because the toolchain got noticeably better underneath me:
- The compiler is now Rust. Astro 7 ships
@astrojs/compiler-rs, a Rust rewrite of the old Go-to-WASM compiler, with Rolldown in the dependency tree. A full production build of this site —astro checktype-checking every component, building 80+ posts, generating OG images, and indexing everything for Pagefind search — finishes in about half a minute on my laptop. astro devis a daemon now. The dev server detaches and keeps running withastro dev stop/status/logsto manage it, instead of holding a terminal hostage.- One sharp edge: Astro 7 collapses HTML whitespace more aggressively than 6. A space between a text node and an inline element on the next line silently disappears — I shipped a “labeledGreat Circle” to production before catching it. Use explicit
{" "}at those boundaries.
Deployment matches the pace. The repo pushes to GitHub, and Cloudflare Workers picks up every commit, builds it with bun, and has it live on the edge about a minute later. My publishing flow is literally /ship — Claude writes the conventional commit, pushes, and by the time I’ve switched tabs to check, the deploy is usually already green. The old Hugo site deployed fast too, to be fair; the difference is that now everything else is this fast.
Three weeks of taste, one sentence at a time
The real payoff came after. The git log since June 15 is ~50 small commits, and almost every one of them was a sentence to Claude rather than an evening of my own:
Typography took four iterations to get right — and that’s the point: iterating was cheap. System fonts first, then Berkeley Mono as the Latin face, then Space Grotesk for everything, and finally where it landed today: Inter for body text, Space Grotesk for headings, Berkeley Mono for code — with CJK always falling through to system fonts so Chinese readers never download a webfont.
The palette went through the same loop. I started with AstroPaper’s colors, tried a blue/teal pair that never felt right, studied a design I admire, and ended with warm stone neutrals and pure ink accents — black on paper in light mode, white on warm black in dark. No borrowed blues.
Code blocks became macOS windows via a custom Shiki transformer — traffic-light dots, the filename in the title bar, click a line to copy that line, click the header to copy the block.
Posts got infrastructure Hugo never had:
- Every post serves a raw
.mdendpoint with a copy button, because half of us are pasting articles into an LLM anyway - An AI summary streams in at the top with a typewriter reveal
- A copyright card at the end with the license, permalink, and share links
- KaTeX CSS loads only on pages that actually contain math; the homepage CSS got cut by a third
The flights page became a real data product. It started as a static table; now a pipeline in the repo computes my actual flown distances from ADS-B position data — 107 of my 113 flights show genuine track miles instead of great-circle estimates, each labeled with its data source, with rankings and an AI-written analysis of my flying habits.
Even the chores automate themselves: a GitHub Action refreshes my U.S. visa wait-times post from travel.state.gov every three hours. It has made more commits than I have.
The comments are mine, all the way down
My comment system is now entirely my own implementation. The backend is Cloudflare Workers + D1 with Telegram moderation — I wrote about building it in March. During this rewrite the frontend became a native Astro component in my own codebase: no embedded widget, no iframe, no third-party JavaScript. The git log shows what that ownership buys — four styling passes on reply indentation and the input area until it felt right, each a ten-minute conversation instead of a fork of someone else’s plugin.
The point
None of the individual features here are remarkable. What’s remarkable is the cost curve. A bespoke blog used to be a tax paid in weekends; now the expensive part is knowing what you want. I spent three weeks deciding — fonts, colors, how a code block should feel — and almost no time implementing. The implementation happened while I slept, or while I was doing something else, one sentence at a time.
Hugo is still a great tool, and I’d still recommend it to anyone who wants a blog that just works. But if you’ve been putting off the rewrite that makes the site truly yours — the excuse is gone.