← All writing
Singy Songbook · 2 of 12
May 1, 2026 · 4 min read

From a PRD to a Deployed Multi-User Web App in Four Days

Four days, one document at the start, a deployed app at the end. Most of the work was a back-and-forth with Claude Code. Notes from the week.

What went in

Day 1: Frontend that displays Chinese lyric flashcards. Backend that takes audio recordings and returns per-syllable feedback. Both running on my laptop.

Day 2–3: Postgres on Neon. Sign-in via Clerk. Frontend on Vercel, backend on Fly. Audio uploads to Cloudflare R2. Sentry + PostHog for errors and analytics.

Day 4: Import a song from a YouTube link. Per-song mastery score. Study mode (cycling deck of unmastered cards). Settings page.

13 pull requests. One production outage. App is now live; I use it daily.

How the work moved

Propose, implement, review, merge, pull.

I'd describe what I wanted, usually as a list of a dozen things. Claude would respond with a plan — often splitting the list into 3–4 focused PRs with reasons for the split. Accept or push back. It would write the first PR and stop. Review the diff, merge, pull, repeat.

Corrections saved to memory files became durable across sessions. Seven by end of week. Examples: "explain the why of git commands, not just the syntax." "Don't mock the database in tests."

Notable

Production outage. Startup-sequence problem in the FastAPI lifespan. Local tests passed; the deployed server hung. Fix landed; "boot the actual server for real before merging" added to project conventions. Has caught two regressions since.

Database cost. Wanted to ping the Neon DB every 4 minutes to keep it from sleeping. Math against the free tier showed that would exhaust the compute budget in 2 weeks. Switched to warm-on-boot with tolerance for the first slow request.

Study mode UI bug. Tests passed; first manual run fired the celebration animation before showing a card. Test suite couldn't observe it. Caught by clicking the button.

The rest of the series is one post per moment.

Takeaways

  • Propose, implement, review, merge, pull. The cadence is what kept four days of work from becoming a pile of half-finished branches.
  • Memory files persist preferences and corrections across sessions. Saving a rule once eliminates the need to re-explain it.
  • Compile-time checks don't tell you whether your service starts. For startup-shape changes, boot the actual server before merging.
  • Cost belongs in implementation, not as an afterthought. Ask for the cost check before the code gets written.
  • Tests assert what the author anticipated. The bugs nobody anticipated are caught by clicking the buttons.

Terms

Claude Code — An AI coding assistant from Anthropic. Runs in a terminal, reads and edits files in a project, runs commands, opens pull requests.

Frontend — The part of the app that runs in your browser — what the user sees and clicks.

Backend — The part of the app that runs on a server — handles data, scoring, anything that can't or shouldn't run in the browser.

Postgres — A widely-used relational database. Rows, columns, SQL queries.

Neon — A hosted, serverless flavor of Postgres. Pay for what you use, scales to zero between requests.

Clerk — An authentication service. Handles sign-in, sign-up, sessions, and user records so I don't write that code myself.

Vercel — A platform that hosts frontends, especially Next.js apps. Auto-deploys when I push to GitHub.

Fly — Fly.io. Hosts backend services in containers, similar to AWS or Heroku but simpler.

Cloudflare R2 — Object storage from Cloudflare. A place in the cloud to upload files to. Compatible with AWS S3 but cheaper.

Sentry — Error-tracking service. When the app throws an exception in production, Sentry catches it and notifies me.

PostHog — Product analytics platform. Tracks user actions ("clicked X", "completed Y") so I can see how the app actually gets used.

Pull request (PR) — A proposed batch of code changes. On GitHub, opening one creates a review surface — a list of what changed, comments, and a Merge button I click when I'm happy with it.

Diff — The set of additions and deletions a pull request introduces. I read this before merging.

Memory files — Plain text files Claude Code automatically reads on every new session. Lets me record preferences and lessons once instead of re-explaining them.

FastAPI lifespan — FastAPI is the Python web framework the backend is built with. The "lifespan" is the framework's startup-and-shutdown hooks — code that runs when the server boots and when it stops.