What I Learned Building This Blog

What I Learned Building This Blog

· #aticaa · #meta · #astro · 한국어

This blog was planned on a Friday night and published on Saturday.

Speed is not the point of this story. This post is a record of how many times we got it wrong in a single day. The plan was wrong, the code was wrong, the monitoring was wrong. What caught each mistake in time was structure and review. Here it is, in order.

The plan said Astro 5

The plan we finalized the night before said “Astro 5 + Cloudflare.” The next day, scaffolding the project, the latest stable release turned out to be Astro 7.0.7. Two major versions had passed.

The lesson is simple. A plan records direction; versions are values you check at execution time. The moment you install the version written in the plan, you’re living today on yesterday’s snapshot.

Astro 7 handed us a trap right away. The footer was written as RSS · Privacy · Contact, but the rendered page showed the spaces collapsed, like RSS ·Privacy. Starting with v7, Astro handles template whitespace with JSX rules — spaces between inline elements split across lines get removed. The fix was an explicit expression ({' · '}). If we hadn’t checked the rendered screen with our own eyes, we’d have found out after publishing.

This blog’s first local render — one post and two Views on the ledger timeline

Web fonts: from CDN to self-hosting

For display type I picked Hahmlet, a Korean serif. At first I dropped in a CDN link, and it got flagged immediately — a font CDN’s CSS varies by browser (UA), so you can’t pin it with SRI (integrity hashes). If the CDN is ever compromised, you’re exposed.

Switching to @fontsource/hahmlet and bundling the font into the build solved three things at once: the SRI problem, third-party IP logging (privacy), and CDN outage dependency. A Korean typeface’s ten-thousand-plus glyphs ship as a hundred-some woff2 slices split by Unicode range, so only the blocks that actually appear on a page get downloaded. Self-hosting costs less than you’d think.

We ran review before pushing. The most painful finding was this one:

Cloudflare’s auto-trailing-slash responds to any request ending in .html with a 307 redirect to the extensionless path. Every report link currently published on the site and in the RSS feed is a redirect source.

Alongside Markdown posts, this blog publishes analysis reports (Aticaa View) built as self-contained HTML, in their original form. We had uploaded those files under names like index-ko.html and linked those URLs directly — so every click and every crawler was taking a redirect hop. We moved the files into a {slug}/ko/index.html directory layout and got clean canonical URLs.

For a moment I wondered whether this conflicted with the “publish untransformed” principle. The conclusion: untransformed is a promise about content; paths belong to serving.

One more from the same review: these reports are static files outside the framework’s route graph, so they don’t get into the sitemap automatically either. That’s also when we added a build-time assertion that checks the files referenced by the metadata (JSON) actually exist. Ten lines that stop broken links from riding a green build into production.

The human’s seat

I built this blog with an AI partner. From scaffold to deploy, she did most of the execution (our house Claude — her name is Tia), and I set direction and made the calls.

The workflow we settled on: work on a branch per issue, two self-reviews before push (bug hunting + structural cleanup), and a PR review after push. Then, at the very end, something funny happened — when Tia tried to merge the PR, the tooling blocked her. The guard: an agent can’t self-merge a PR it authored on automated reviews alone.

I think that’s the right design. However dense the automation gets, a human presses the publish button. Seeing that principle enforced at the tool level was, if anything, reassuring.

I first met Claude in 2024, and started using Claude Code in December 2025, from Opus 4.5. And now — a blog built, with GitHub–Cloudflare integration wired up, in two days. The accumulated relationship (context) is what makes the work flow. Naming my agents was the right call after all.

What remains

Search-engine registration is next, and a few posts are queued up. This post itself will be published by a single push — we built that pipeline together yesterday.

The record continues. See the full list.