Building a Full-Stack Web Project with Claude Code
A concrete, session-by-session walk-through of using Claude Code as a co-developer on a real, multi-module web application — designed around context-window constraints rather than against them.
The example project is FocusBoard, a team kanban with time tracking and reports. It’s big enough to demonstrate the workflow but small enough to be specific.
The Project
FocusBoard lets small teams plan work on kanban boards, track time per card, and see weekly reports.
Features (in build order)
- Auth (email + password, sessions)
- Workspaces & memberships (invite by email)
- Boards & columns
- Cards (CRUD, drag/drop, assignees, labels, due dates)
- Comments + activity log
- Time tracking (start/stop timer per card)
- Real-time updates (across users in a workspace)
- Reports (per-user / per-workspace weekly view)
- Settings, billing-ready hooks, deploy
Stack
- pnpm + Turborepo monorepo
- Next.js 15 (App Router), React Server Components
- tRPC for the API (end-to-end type safety = Claude’s best friend)
- Drizzle ORM + PostgreSQL
- Auth.js (NextAuth v5)
- Tailwind + shadcn/ui
- Pusher Channels for real-time
- Vitest (unit/integration) + Playwright (e2e)
Why this stack for a Claude-driven build: every layer is type-safe end-to-end. The TypeScript compiler is your most reliable code reviewer between sessions.
Repo Layout
|
|
Phase 0 — Foundations (before any feature)
Goal: Set up infrastructure so every later session starts on solid ground. Do this manually (or with create-turbo); don’t burn a Claude session on boilerplate it can’t run anyway.
0.1 Initial Claude session: bootstrap CLAUDE.md
After scaffolding the monorepo, run a single session:
|
|
Then hand-write the strategic docs yourself (Claude can help draft, but the content must come from you):
docs/ARCHITECTURE.md (~1 page):
|
|
docs/DECISIONS.md starts as one entry:
|
|
docs/CONVENTIONS.md captures the patterns that should be uniform across the codebase: error shapes, Zod-first input validation, file naming, “every mutation logs to the activity table,” etc.
0.2 CI as a forcing function
|
|
This is non-negotiable. It’s your asynchronous code reviewer that catches Claude’s drift between sessions.
Phase 1 — Auth (Session 1, ~2 hours)
Why this first: every other feature depends on ctx.user. Build the smallest thing that works end-to-end.
Session 1 prompt
|
|
Review the plan. Push back where needed. Then:
|
|
Context budget for this session (~200K window)
| What | Tokens |
|---|---|
| System + tools | ~12K |
| CLAUDE.md + docs auto-loaded | ~5K |
| Schema, layout, auth.js docs (WebFetch) | ~15K |
| Plan + iteration | ~10K |
| Generated code + edits | ~30K |
| Total used | ~70K |
Plenty of headroom. End the session with a commit and /clear before moving on.
Output of this session
packages/db/src/schema/users.ts— users, sessions, accounts tablesapps/web/src/lib/auth.ts— NextAuth configapps/web/src/app/(auth)/{login,signup}/page.tsxapps/web/src/middleware.tsdocs/features/auth.md— the most important artifact for future sessions- A passing Playwright test
Phase 2 — Workspaces (Session 2)
|
|
Notice: Claude only reads what’s relevant. We don’t load the entire apps/web/ UI tree — workspace creation UI is a separate sub-task.
After backend lands, a second sub-session for UI:
|
|
Phase 3 — Boards & Cards (the meaty feature, 3 sessions)
This is where most large-project pain shows up. Done wrong, you’ll have one giant 4000-line session that runs out of context and produces garbage. Done right, three clean sessions of ~1.5 hours each.
3.1 Contract session
This is the most important session in the whole project. Get it right and the next two are mechanical.
|
|
The output is a contract. It’s small (maybe 300 lines) and readable. The next two sessions implement it without needing to redesign anything.
3.2 Backend session
|
|
This session has exactly the context it needs: the contract + the implementation files. ~40-60K tokens used.
3.3 Frontend session
|
|
The frontend session never touches the database or tRPC implementation — it only consumes the typed client. If the user drags a card and the API throws, that’s a separate bug, fixed in a separate session.
Phase 4 — Cross-cutting Feature: Real-time (Session 7)
Real-time touches every previous feature. This is where developers panic-load the entire codebase. Don’t.
Strategy: an integration ledger
Add to docs/features/realtime.md before writing code:
|
|
Now your session is small:
|
|
Each procedure-touch is small. The doc is the index Claude uses to find work, not the codebase itself.
Phase 5 — Drift Maintenance (every ~5-10 sessions)
After several features, things drift. Run a fresh “audit” session:
|
|
Then fix in a separate session (the audit’s findings become the prompt). Keep audit and fix sessions separate so the audit’s full output doesn’t pollute the fix’s context.
Anatomy of a Single Session
Here’s a concrete session for “add labels to cards” — a small, realistic feature:
Pre-session checklist (yours):
- Last session committed?
-
/clearif continuing same chat - Know which docs/files are relevant
- Have a one-paragraph goal in mind
Prompt 1 — Orient:
|
|
→ Claude proposes a labels table and a card_labels join table.
Prompt 2 — Confirm and scope:
|
|
→ Two files changed, migration generated, tests pass.
Prompt 3 — Contract:
|
|
Prompt 4 — UI:
|
|
Prompt 5 — Test:
|
|
Prompt 6 — Commit:
|
|
Total session: ~90 minutes, ~80K tokens, one focused PR. Claude never sees the timer code, the reports code, or the realtime code — none of which are relevant.
Context Budget Reference
A rule of thumb per session (200K window):
| Bucket | Budget | Notes |
|---|---|---|
| System + auto-loaded CLAUDE.md/docs | ~20K | Keep CLAUDE.md tight |
| Files Claude reads | ~40K | 5-15 files |
| Plan + back-and-forth | ~20K | Plan mode is cheap |
| Generated code + edits | ~50K | The actual work |
| Tool outputs (greps, test runs) | ~30K | Wastes the most |
| Headroom buffer | ~40K | Don’t run to the wall |
When tool outputs balloon (long test failures, big greps), delegate to a subagent — its 50K of grep output collapses into a 500-token summary in your main context.
Even with a 1M context window, you should still work this way. Focused context produces better code, faster, cheaper. The 5-minute prompt-cache TTL means scrolling context still has cost; quality also degrades with noise.
Common Pitfalls (and the fix)
| Pitfall | Fix |
|---|---|
| “Just one more thing” sessions | Hard stop at one feature. Commit. /clear. |
| CLAUDE.md becomes a 500-line wiki | Cap at ~150 lines; push details into docs/features/* and link |
| Claude regenerates files it shouldn’t touch | Be explicit: “Only edit X. List others and stop.” Use plan mode. |
| Tests are flaky / slow → Claude skips them | Fix the tests, not the rule. A green suite is your only objective signal. |
| Two features merged together in one PR | One feature per branch. Even if it feels slower, debugging is 10× faster. |
| Docs go stale | The audit ritual (Phase 5) every 5-10 sessions |
| “It worked when Claude said done” | Always run the feature in the browser. Type-check is necessary, not sufficient. |
TL;DR Workflow
- Phase 0 once: scaffold + CLAUDE.md + ARCHITECTURE.md + CI.
- Per feature:
- Contract session (schema + tRPC signatures + feature doc) — small, careful
- Backend session (implement + tests)
- Frontend session (consume typed client)
- Polish session if needed
- Per session: fresh
/clear, point at 3-8 specific files, plan mode for non-trivial work, commit at the end. - Every ~5-10 sessions: audit ritual to catch drift.
- Always: the type system + CI + Playwright are the safety net. Invest in them early; they pay back tenfold.
The discipline is: the codebase is the truth, the docs are the index, and every session loads only the slice it needs.