Back to Journal
ProductionThe Dev Playbook

Walking Skeleton: First Deploy to Cloudflare

Getting the thinnest possible version of the site live. A walking skeleton with CI/CD and a domain.

March 18, 20265 min read

Sprint 1's goal is a walking skeleton: the thinnest vertical slice that proves the whole pipeline works end to end. For The Dev Playbook, that means code compiles, pages render, MDX works, and it's live on a real domain that real humans can visit.

Whether or not real humans actually visit is a problem for Future Me.

What's a Walking Skeleton?

The term comes from Alistair Cockburn. A walking skeleton is a tiny implementation that performs a small end-to-end function. It doesn't need to be pretty. It needs to walk.

For a static site, the skeleton is:

  1. Build system: Vite compiles, React Router pre-renders
  2. Routing: all pages resolve (even if they're stubs that say "coming soon")
  3. Content pipeline: MDX compiles to React components with syntax highlighting
  4. Deployment: push to main, site rebuilds on Cloudflare Pages
  5. Domain: thedevplaybook.com resolves and serves the site with SSL

That's it. No design system. No animations. No content beyond one test post. Just proof that the whole chain works before I invest time in anything that sits on top of it.

The Tech Stack in Action

The MDX pipeline was the riskiest piece. If this didn't work, the entire content strategy needed rethinking.

// vite.config.ts - the MDX pipeline
export default defineConfig({
  plugins: [
    tailwindcss(),
    mdx({
      remarkPlugins: [remarkGfm, remarkFrontmatter, remarkMdxFrontmatter],
      rehypePlugins: [[rehypePrettyCode, { theme: "github-dark" }]],
    }),
    reactRouter(),
  ],
});

The approach: MDX files are compiled to React components at build time by @mdx-js/rollup. Frontmatter gets extracted by remark-mdx-frontmatter and exported as a named export. Syntax highlighting happens at build time via Shiki, so there's zero client-side JavaScript for code blocks.

I spent an embarrassing amount of time debugging an issue where the dev server wouldn't pick up new loader exports. Turns out it's a React Router HMR limitation and you have to restart the dev server. The kind of thing that's obvious in retrospect and infuriating for the 45 minutes you don't know it.

Cloudflare Pages Setup

Cloudflare Pages gives me everything I need for free:

  • Git-push deploys: push to main, site rebuilds automatically
  • Global CDN: static files served from 300+ edge locations
  • Free tier: unlimited bandwidth, 500 builds/month
  • Custom domain: thedevplaybook.com with automatic SSL

Configuration is almost suspiciously minimal:

SettingValue
Build commandnpm run build
Output directorybuild/client
Node version20

Three settings. That's the whole deploy config. I initially tried to set up a Cloudflare Worker for server-side rendering before realizing I was over-engineering a static site. Deleted the worker, shipped pure static assets, and moved on. Sometimes the best architecture decision is the one you undo.

What I Learned

The walking skeleton approach works because it front-loads the integration pain. Every "how do these pieces fit together?" question gets answered in Sprint 1, when the stakes are low and the codebase is small. By the time I'm building real features in Sprint 2, the pipeline is proven and I can focus on content instead of infrastructure.

// Sprint 1 deliverable: a site that does almost nothing,
// deployed to production, proving everything works.
// Peak engineering.