Building a Full-Stack App with Claude Code in One Day: What Worked and What Didn't

The spec document was three pages. The app that came out of it had a FastAPI backend with 7 API routes, a SQLite database with 12 tables, a Telegram bot process, Monetag ad integration, sound effects, internationalization in two languages, and a deterministic daily challenge system that gives every player in the world the same board.

Claude Code built all of it in one day. I wrote zero lines. The app went live on Telegram that evening as @SpeedTapGameBot.

This post walks through how that actually happened — the spec, the generation, the problems, the fixes. If you're wondering whether Claude Code can handle more than toy examples, this is the evidence.

The Spec That Started Everything

The single most important thing I did was write the spec before touching Claude Code. Not pseudocode. Not wireframes. A plain-English description of every part of the app.

The spec covered: what the game is (tap numbers 1 through 50 in order, as fast as possible), what each screen shows (home, game, result, leaderboard), how scoring works (elapsed time in seconds), when ads appear (after three free plays, after game over, after daily challenge), how sharing works (preset messages with the player's time), and what the daily challenge is (same board for everyone, generated from a date-based seed).

Why this matters: Claude Code is good at translating clear descriptions into code. It's bad at guessing what you want from vague instructions. "Build me a game" produces garbage. "Build a 5x5 grid that displays numbers 1-25 in random positions, where tapping the correct next number replaces it with a number from 26-50, with a running timer and three-strike game over" produces a working game.

The three pages of spec took about an hour to write. That hour saved an entire day of back-and-forth clarification.

What Claude Code Generated

I handed the spec to Claude Code in the terminal. One prompt — the full spec, pasted in. What came back was a complete project structure:

SpeedTap/ ├── frontend/ │ ├── index.html │ ├── css/style.css │ └── js/ │ ├── main.js # Entry point │ ├── game.js # Core game logic │ ├── ui.js # Screen management │ ├── api.js # Backend communication │ ├── telegram.js # Telegram WebApp SDK │ ├── i18n.js # English + Korean │ ├── share.js # Share message generation │ ├── sound.js # Web Audio effects │ └── ads.js # Monetag integration ├── backend/ │ ├── main.py # FastAPI app + 7 routes │ ├── database.py # SQLite schema + queries │ ├── bot.py # Telegram bot handlers │ └── daily.py # Deterministic seed logic └── deploy/ ├── speedtap-app.service # systemd for FastAPI └── speedtap-bot.service # systemd for bot

Nine frontend modules, four backend files, two deployment configs. The whole thing compiled and ran on the first try — no missing imports, no syntax errors, no structural issues. The game board appeared, numbers were tappable, the timer ran.

This is the part that still surprises me. Not that the code was perfect (it wasn't — we'll get to the bugs). But that a single prompt produced a coherent multi-file project where the pieces actually fit together. The frontend calls the right backend routes. The backend writes to the right database tables. The bot process reads from the same database the API writes to.

The Three Bugs That Almost Killed It

The game worked on first run. Shipping it took another few hours because of three platform-specific bugs that had nothing to do with the game logic.

Telegram's invisible cache. I pushed a JavaScript fix to Cloudflare. Reloaded the app in Telegram. Nothing changed. The old version was still running. Pushed again. Still nothing. Telegram's mobile WebView caches JavaScript aggressively and ignores HTTP cache headers entirely. ES module imports get cached by URL — once game.js is cached, even a fresh HTML page won't refetch it.

The fix is ugly but works: version parameters on every import.

import { Game } from "./game.js?v=14";

Every deploy means bumping that number across every import statement. Not elegant. Only reliable method I've found.

Buttons that die silently. The quit button and sound toggle worked during my first test. By the time I showed it to someone else, they were completely unresponsive. No error, no crash — just dead buttons.

Root cause: event listeners were registered after an await call in the initialization function. If the nickname modal resolved slowly, the buttons existed in the DOM but nothing was listening to clicks. Users who dismissed the modal quickly had working buttons. Users who took a second didn't.

Fix: move all button bindings into a synchronous function called before any async work starts.

Dark mode blindness. Telegram injects CSS variables based on the user's theme. Light-mode users saw a blank grid — white numbers on white cells. The game was literally invisible to half the potential audience.

Fix: force the entire app to dark mode. Override all Telegram theme variables. Set color-scheme: dark. Pick one theme and commit.

The pattern across all three bugs: None of them were code quality issues. The game logic was fine. These were platform quirks — Telegram WebView caching, Telegram CSS injection, async timing in a WebView context. Claude Code couldn't predict them because they only manifest in the actual Telegram environment, not in a browser or a test runner. The fix loop was always the same: discover the bug in production, describe it to Claude Code, get a fix, test in Telegram, deploy.

The Piece I Cared About Most

The daily challenge system is the feature I spent the most time specifying, because getting it wrong would mean some players see easier boards than others.

The solution is a deterministic seed. Take today's date, hash it with SHA-256, use the hash as a random seed. Same date produces the same seed produces the same board. Every player in the world gets identical number positions and identical refill order. No server coordination needed — two clients with the same seed independently generate the same game.

def generate_daily_seed(date_str): raw = hashlib.sha256( f"speedtap_salt:{date_str}".encode() ).hexdigest() return int(raw[:8], 16) def get_daily_board(date_str): rng = random.Random(generate_daily_seed(date_str)) board = list(range(1, 26)) rng.shuffle(board) return board

Claude Code wrote this implementation. I understood it well enough to verify that two calls with the same date actually produce the same board. Tested it three times with different dates. Each time, identical output. That's the level of code review a non-developer can do — you can't write it from scratch, but you can verify the behavior matches the spec.

What I'd Change About the Process

Looking back at that one-day build, two things would've made it smoother.

Test on Telegram earlier. I spent the first four hours building and testing in a desktop browser. Everything worked perfectly. The three bugs above only appeared when I loaded the app inside Telegram's WebView for the first time. If I'd tested there after the first hour instead of the fourth, I would've caught the caching and dark mode issues with less code to debug.

Split the spec into phases. I gave Claude Code the entire spec at once. It generated everything at once. That meant when the button-binding bug appeared, I had 9 frontend modules to search through. If I'd built in three phases — core game first, then social features, then ads — each debugging session would've had a smaller surface area.

For the Phase 2 build (difficulty modes, Stars payments, 1v1 challenges), I applied both lessons. Tested in Telegram after every feature. Built one feature at a time. Phase 2 was more complex than the MVP but produced fewer debugging headaches.

Can You Actually Do This?

If you're reading this and thinking "I could never build a full-stack app in a day" — you're probably right, for the first one. My first project took significantly longer. SpeedTap was project five or six. By that point, I knew how to write specs that Claude Code understands, I had deployment pipelines already configured, and I'd seen enough platform quirks to anticipate some of them.

The first project takes a weekend. The second takes a day and a half. By the fifth, a day is realistic for an MVP. The speed comes from pattern recognition, not from becoming a better coder. You learn what to specify, what to skip, and where the platform will surprise you.


More guides in this series cover specific parts of the stack — Oracle Cloud setup, Cloudflare configuration, API cost optimization. If you've built something with Claude Code and hit a wall not covered here, drop it in the comments.

Related guides in this series:

Disclaimer: This blog documents practical workflows based on personal experience. Nothing here is financial, legal, or professional advice.

Comments