Claudedogged the blog


My blog was a piece of shit. I fixed it.
Not metaphorically. Literally. No syntax highlighting. No tags. No reading time. No way to share a post. No prev/next navigation. Just markdown files sitting in a folder like it's 2009 and I'm proud of myself for using git.
I coined a term for what I did about it: claudedogging. Like dogfooding, except instead of eating your own cooking, you make the AI eat it and tell it to take notes. I described what I wanted. Claude wrote the PRs. I reviewed and merged them. Already called this on Threads. You're welcome.
clau·de·dog (v.) /ˈklɔd.dɒɡ/
verb — claudedogged, claudedogging, claudedogs
1. To delegate the implementation of a software feature, refactor, or project entirely to Claude Code; to describe a desired outcome and allow an AI agent to write the branches, commits, and pull requests while the author acts as reviewer. "I claudedogged the whole tag system this weekend."
2. (by extension) To ship a non-trivial amount of functionality in a single session by directing rather than writing. "Didn't write a line of it — just claudedogged it."
Origin: Portmanteau of Claude (Anthropic AI) and dogfooding (the practice of using your own product). Spiritually adjacent to rawdogging — except the opposite: instead of suffering through something with no assistance, you hand it entirely to the AI and feel nothing. Coined by @josheche on Threads, March 2026.
Related: claudedogger (n.) — one who claudedogs. claudedogging (n.) — the act of claudedogging.
Eight features. One session. Here's what got shipped:
Syntax highlighting
Catppuccin Mocha. Fira Code. Ligatures. If you don't know what any of that means, your code blocks look bad and you've accepted it. I haven't.
The whole pipeline is a unified processor with one extra plugin:
export default async function markdownToHtml(markdown: string) {
const result = await unified()
.use(remarkParse)
.use(remarkGfm)
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeShiki, { theme: "catppuccin-mocha" })
.use(rehypeStringify, { allowDangerousHtml: true })
.process(markdown);
return result.toString();
}
That's it. One .use() call. Shiki does the rest.
Reading time
Word count divided by 200, floored at one minute. Here's the entire function:
export function readingTime(content: string): number {
return Math.max(1, Math.round(content.trim().split(/\s+/).length / 200));
}
One line. I don't know why this took me this long either.
Tags
Frontmatter tags: on every post, pill links, filtered tag pages. This is a solved problem from 2006. I just hadn't done it.
Prev/Next navigation
Bottom of the post. Sorted by date because that's the only sort order that makes sense. You're welcome again.
Related posts
Scored by tag intersection. Top three show up if there's any overlap. No overlap, nothing renders. It doesn't try to be smart. It just works.
const relatedPosts = allPosts
.filter((p) => p.slug !== slug)
.map((p) => ({
...p,
score: (p.tags ?? []).filter((t) => postTags.includes(t)).length,
}))
.filter((p) => p.score > 0)
.sort((a, b) => b.score - a.score)
.slice(0, 3);
Filter self out. Score by overlap. Sort. Take three. Done.
Share links
HN, Threads, Reddit, LinkedIn, X, email, copy link, copy post as markdown. The copy MD one is my favorite. A lot of AI interfaces eat markdown directly. That's just true and useful.
The share targets are just a links array with encoded URLs:
const links = [
{
label: "HN",
href: `https://news.ycombinator.com/submitlink?u=${encodedUrl}&t=${encodedTitle}`,
},
{
label: "Threads",
href: `https://www.threads.net/intent/post?text=${encodedTitle}%20${encodedUrl}`,
},
{
label: "Reddit",
href: `https://reddit.com/submit?url=${encodedUrl}&title=${encodedTitle}`,
},
{
label: "LinkedIn",
href: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`,
},
{
label: "X",
href: `https://x.com/intent/tweet?url=${encodedUrl}&text=${encodedTitle}`,
},
{
label: "Email",
href: `mailto:?subject=${encodedTitle}&body=${encodedUrl}`,
},
];
RSS
/feed.xml. There's a link in the footer. If you use RSS you already know why this matters. If you don't, I'm not explaining it.
Scroll progress bar
Black bar at the top. Fills as you scroll. You know what it does.
Did Claude make mistakes? Yes. THREE. A missing date field in a sort function, hero post not rendering tag pills, clipboard error not being caught. I caught all three in code review. Fixed in minutes.
The bar for "AI screwed up" is apparently "did a thing any junior dev would do on a first pass." THAT'S the failure mode. Not "it hallucinated a function that doesn't exist" or "it deleted the database." It missed a field. I caught it. We moved on.
The PRs are clean. The features work. The blog is no longer embarrassing.
That's claudedogging.
Related Posts
Profile switching for clean account separation, and audio notifications so you stop babysitting the terminal. Both take five minutes to set up.
The real shift was not becoming a power user. It was realizing the model was no longer the main variable. The surrounding system was.
Canceling Cursor was not a dramatic decision. The overlap phase was already over. Claude Code is the default and has been for a while.
