Six months. Two SaaS apps. Roughly 400 sessions. And the stuff that actually keeps my code from catching fire? Not in a single tutorial.
TL;DR: Claude Code tutorials teach you setup and features. Production teaches you discipline, context management, and when to kill your own sessions. The gap between “it works in a demo” and “it ships to users” is where most devs lose weeks. This is the stuff I learned the hard way so you don’t have to.

The Tutorial Checklist (and Why It Peaks at Week Two)
Every guide follows the same arc. Install Claude Code. Create a CLAUDE.md. Learn the slash commands. Maybe set up a multi-worktree flow if the author is feeling ambitious. The demo works. The reader feels powerful. The tutorial ends.
Two weeks later, reality kicks the door in.
I know because I lived the arc myself. My first month with Claude Code was the honeymoon phase — everything felt magical, every generated function felt like free labor. Then the Convex migration happened.
I asked Claude to refactor my auth middleware from a custom Supabase flow to Clerk. Straightforward task. Clear instructions. Claude produced 380 lines of clean, typed, well-documented code. It even added comments explaining _why_each function existed.
One problem: it imported from @clerk/nextjs/server instead of @clerk/nextjs/api — a distinction that matters if you're running Convex server functions, which don't have access to the Next.js request object. The code compiled fine locally. The tests passed because they were mocking the wrong context. I deployed, the dashboard went white, and I spent 4 hours debugging an import path.
No tutorial prepared me for that. Because tutorials test features. Production tests assumptions.
Your CLAUDE.md Is Probably Too Long
The single most popular tutorial topic in the Claude Code ecosystem is “how to write a good CLAUDE.md.” And almost every example I’ve seen makes the same mistake: they’re novels.
I get it. You want Claude to know everything about your project. Your stack, your conventions, your testing philosophy, your preferred variable naming scheme, your opinions on tabs vs spaces (spaces, obviously — I’m not an animal).
My first CLAUDE.md was 847 lines. I was proud of it. Documented every Convex function pattern. Every Clerk webhook handler. Every Supabase RLS policy format. I thought I was giving Claude the ultimate context.
What I was actually doing was diluting every instruction to the point of irrelevance.
Claude reads your CLAUDE.md every session. All of it. And if you’ve ever watched the token counter, you know that an 847-line context file eats roughly 3,000 tokens before Claude writes a single character. That’s 3,000 tokens of your context window gone. On a complex refactoring session, you’ll hit the limit 30–40% faster — which means Claude forgets your actual conversation sooner while still perfectly remembering your opinions on semicolons.
My current CLAUDE.md is 127 lines. I cut it by 85%.
What survived:
- Stack declaration (8 lines) — Convex, Clerk, Supabase, Next.js 15, Tailwind. No explanations. Just names and versions.
- Hard boundaries (12 lines) — things that trigger an immediate
git checkout .if violated. Wrong auth provider. Wrong database client. Wrong deployment target. These aren't preferences. They're load-bearing walls. - File structure rules (15 lines) — where things go. Convex functions in
/convex, API routes in/app/api, shared types in/lib/types. Claude loves to invent new directories. This stops it. - The contract template (20 lines) — Goal, Constraints, Output Format, Failure Conditions. Every task starts here. I wrote a full Prompt Contracts walkthrough, so I won’t repeat it — but this single addition cut my revert rate from 1-in-3 to 1-in-10.
Everything else — the style guide, the testing philosophy, the 40 lines about error handling patterns — got moved to task-specific prompts that I paste in only when relevant. Claude doesn’t need your error handling opinions when it’s writing a UI component.
Shorter CLAUDE.md = more room for the actual conversation = fewer mid-task hallucinations. Tutorials won’t tell you this because “write less” isn’t a sexy headline.
The Checkpoint Loop
One task, one session. Commit before, commit after. Kill the session when the task is done.
That’s the whole system. Let me explain why each part matters.
Claude Code sessions have a shelf life. Not a hard limit — you can technically keep going for hours. But somewhere around the 45-minute mark, or after 15–20 back-and-forth messages, the quality drops. Not dramatically. Subtly. Claude starts repeating patterns from earlier in the conversation. It references code it wrote 30 messages ago that you’ve since modified. It “remembers” constraints from the beginning of the session but forgets the refinements you added midway.
I call it context decay. The context window is technically still there, but the model’s attention is spread thin across too many messages, and the most recent instructions start competing with the accumulated cruft of an hour-long session.
And here’s the part I’m embarassed about: during my first month, I was committing once per feature. Sometimes once per day. I trusted Claude’s output enough to keep building on top of it, session after session, no checkpoints.
Then one Thursday afternoon, Claude refactored a Convex query that cascaded into four components, broke server-side rendering on three pages, and I had no clean state to revert to. The last commit was 6 hours of work ago. I spent the evening doing git diff line-by-line, manually cherry-picking which changes to keep.
Now the workflow looks like this:
-
git add -A && git commit -m "checkpoint before claude session" - Open Claude Code. Paste the contract for this specific task.
- Do the thing. Review the output. Run the tests.
-
git add -A && git commit -m "task: clerk webhook handler" - Close the session. Open a new one for the next task.
Not “one feature.” One task. “Add the webhook handler for Clerk user creation” is a task. “Build the entire auth flow” is a project that should be 4–6 sessions.
My git log looks unhinged — 15–20 commits per day, messages like “checkpoint before auth refactor” and “task: add clerk webhook, tests pass.” It’s ugly. It’s also a time machine. When Claude does something wrong and you revert, the next session starts from a known-good state with zero leftover confusion. Fresh session + clean git = Claude operating on reality instead of the wreckage of its previous attempt.
Tutorials show you a single glorious session where everything clicks. Production is fifty short sessions where each one starts clean and ends committed.

Declare Your Adjacent Surfaces (or Lose a Day)
Before any Claude Code task, write three lines like this:
## Adjacent Code
- /convex/payments.ts → handles Stripe webhook, writes to "payments" table
- /app/dashboard/revenue.tsx → reads from "payments" table, groups by month
- /convex/notifications.ts → triggers on payment insert via Convex scheduled function
That’s it. File paths and a one-liner about what they care about. Two minutes. Paste it in your prompt contract under “Adjacent Code.”
Why? Because every tutorial demos Claude Code on isolated features. “Let’s build a todo app!” “Let’s add authentication!” In production, nothing is isolated. Your Clerk webhook talks to your Convex mutation which triggers a Supabase edge function which sends an email via Resend which logs to your dashboard component. Touch one, and three others flinch.
I learned this the expensive way. Claude did a correct refactoring of my payment flow — clean code, passed every test — that silently broke the dashboard’s revenue chart. Because it didn’t know (and I didn’t tell it) that the dashboard was reading from the same Convex table with a different query pattern. The fix took 20 minutes. Finding the bug took a day and a half.
Claude doesn’t need to read adjacent files. It just needs to know they exist. That’s enough for it to flag “changing this table schema will affect these 3 files” instead of silently bulldozing your dashboard.
Error Recovery Is Where You Actually Lose Time
Tutorials show the happy path. Feature request → Claude generates code → tests pass → ship it. The unspoken assumption is that this flow is the norm.
In my experience, the flow works clean maybe 60% of the time. The other 40% is error recovery — Claude generates code, something breaks, and now youre debugging with Claude, which is a fundamentally different skill than building with Claude.
The problem with debugging sessions: Claude becomes apologetic and over-corrective. Tell it “the webhook handler throws a 500 on the Clerk user.created event” and it’ll rewrite the entire handler instead of fixing the one line that’s wrong. I’ve watched Claude delete working code to “start fresh” more times than I can count because its error-recovery instinct is to nuke from orbit.
My workaround is ugly but effective. When something breaks:
- Don’t describe the error in the same session. Kill the session. Start a new one.
- Paste the error message and the specific file, not the task description. “This function in
/convex/webhooks.tsthrowsTypeError: Cannot read properties of undefined (reading 'email_addresses')when receiving a Clerkuser.createdevent. The incoming payload looks like this: [paste payload]. Fix only this function. Do not modify any other files." - Lock everything else. Add “DO NOT modify any file except
/convex/webhooks.ts" to the constraint. Claude in fix mode is trigger-happy.
The difference between “hey Claude, the auth is broken, can you fix it” and a scoped, single-file, error-specific prompt is the difference between a 5-minute fix and an hour-long rabbit hole where Claude rewrites your auth from scratch.
Tutorials don’t teach error recovery because it’s not photogenic. But it’s where at least a third of your Claude Code time actually goes.
What I Tried and Ditched
Not everything survived. Some stuff I was convinced would work, spent real time on, and eventually abandoned. Worth documenting so you don’t repeat the experiment.
“Think hard” on every prompt. For a while I added “think hard” or “think step by step” to every single Claude Code prompt because some Reddit thread said it improved output quality. It does — on complex architectural decisions. On “add a loading spinner to this button”? It just makes Claude write a 400-word plan for a 3-line change. Now I only use it for tasks with real ambiguity. Maybe 1 in 5 prompts.
Multi-worktree parallel sessions. The concept is beautiful — three Claude instances working on three features simultaneously, each in its own git worktree. I set this up, got it running, and used it for exactly one afternoon. The problem isn’t technical, it’s cognitive. Reviewing three concurrent outputs, keeping track of which worktree is ahead, merging back without conflicts — it’s project management overhead disguised as productivity. If you have a team, maybe. Solo? You’re just generating merge conflicts faster. But I digress — I did name the worktrees after Pokémon for about 48 hours, and honestly that part was fun.
Using Claude to review its own code. Y Combinator’s CEO has a whole prompt for this — four-stage review system, architecture check, code quality pass, the works. I ran it for two days. The output was thorough and I genuinely learned something from the structured format. But the math didn’t work: 30–45 minutes of review dialogue per feature, on top of the generation time. I was spending more time supervising the reviewer than I would have spent reviewing the code myself. Claude catching its own mistakes sounds elegant. In practice, you become a QA manager for a robot that doesn’t remember the review next session anyway.
Personality instructions in CLAUDE.md. “You are a senior TypeScript engineer who values clean architecture.” “Respond concisely.” “Be opinionated.” I had 15 lines of this stuff. Removed all of it after realizing it had zero measurable impact on code quality. Claude doesn’t code differently because you told it to be senior. It codes differently when you give it the right constraints and context. Save those tokens.
Every one of these felt smart at the time. Thats the trap — the Claude Code ecosystem is full of advice that sounds reasonable in a blog post and falls apart at session 200.
🔄 Update: There’s actually a 6th thing no tutorial covers, and its arguably the most important: security. Claude Code has had a /security-review slash command since August 2025. You type it, it scans your code for vulnerabilities. Twelve words to explain. Zero tutorials mention it. Anthropic just built an entire enterprise product around it and crashed $15 billion in cybersecurity stocks in the process. The tutorials are still teaching you how to set up CLAUDE.md.
Tutorials sell the magic. Production charges for the cleanup.
I write about Claude Code, AI automation, and the unsexy parts of shipping real products with AI tools. Next up: why I rebuilt my entire OpenClaw setup for $15 after Anthropic killed the old one — and the architecture that’s actually working. Hit subscribe (not just follow — the email is what gets you the article, the follow button is basically a participation trophy) if you want that in your inbox.
Top comments (0)