My CLAUDE.md is 1,200 Lines — Here's What I Learned Building a 1M-Line SaaS with AI
I'm a solo founder. Over the past 114 days, I've built a production SaaS that's now 1.1 million lines of code across 320+ features. My total AI spend is about $1,470. A traditional estimate for the same codebase: 28 people, 18-24 months, $6.4M-$14M.
The backbone of this entire operation is a single file called CLAUDE.md. It's 1,200 lines long. I've never looked at anyone else's. Every line exists because something broke.
Here's what I learned.
What a CLAUDE.md Even Is
If you haven't used Claude Code, quick context: it's Anthropic's CLI tool that gives Claude direct access to your codebase. It can read files, write code, run commands, execute builds, and commit changes.
CLAUDE.md is a file you put in your project root. Claude reads it at the start of every session. Think of it as persistent memory — the instructions, context, and rules that carry across conversations. Without it, every session starts from zero. With it, Claude picks up where you left off with full awareness of your architecture, conventions, and hard-won lessons.
At scale, this file becomes the most important file in your entire project.
It's Not a Template. It's Scar Tissue.
I didn't sit down one day and write a 1,200-line instruction manual. The file grew organically. Every rule in it traces back to a specific moment where something went wrong and I had to prevent it from happening again.
Claude kept hardcoding dark theme colors instead of using theme variables. That became a UI standards section. Claude kept deploying to Vercel manually and creating duplicate projects. That became a "NEVER run vercel deploy" rule. Claude kept adding code to already-massive files. That became an extraction protocol.
I've seen people share CLAUDE.md templates online. I'm sure some are useful. But the real value isn't in copying someone else's rules — it's in accumulating your own. Your file should read like a changelog of lessons learned, not a best practices guide you downloaded.
The rules that stick are the ones that come from pain.
The Estimation Framework
This was one of the earliest additions and it changed how I think about every feature.
Traditional development estimates are worthless when you're building with AI agents. A feature that a team would scope at "2-3 weeks" might take 90 minutes. But you can't just say "AI is fast" — you need calibrated numbers.
Here's what I measured over months of building:
- Single agent, focused task: 3,000-5,000 lines per hour
- Three parallel agents, independent workstreams: 5,000-8,000 lines per hour
- Full sustained session (me + Claude): 8,000-15,000 lines per day
The estimation method is dead simple. Count the outputs. A typical API route is 80-150 lines. A service module is 200-400. A UI component is 150-300. Sum the expected lines, divide by output rate, add 15-30 minutes for wiring and type fixes.
The reference data point that anchors everything: I built a 28-file feature — about 6,100 lines of code — spec'd, built, wired, and tested in 90 minutes using three parallel agents. The traditional estimate in the spec document said 16-21 days. It was wrong by more than 10x.
There's a line in my CLAUDE.md that I'm particularly attached to: "Never say days when you mean hours." If the line count says 2 hours, don't say "1-2 days" out of habit. Say 2 hours. Mean it.
The Build Methodology
This section was born from a security audit that found 42 bugs, 9 of them critical. I ran the audit on my own codebase after a month of fast building. The results were humbling.
The core problem: Claude would build UI for database fields that didn't exist yet. Or build an API that returned data in a shape the frontend didn't expect. Or assume what a database column contained without checking — and discover later it was encrypted ciphertext.
So I formalized a four-step sequence that's now mandatory for every feature:
Step 1: Design Audit. Before writing any code, trace the data flow end-to-end. Does the database column exist? What does the data actually look like? Query a real record. Don't assume — verify. Check for encryption, encoding, or summarization that changes the raw value. Who consumes this API? What are the edge cases?
Step 2: Build in one pass. Schema, then API, then interface types, then UI, then wiring. In that order. Don't build UI for fields that don't exist in the database yet.
Step 3: Verify before commit. Type check with zero errors. Lint with zero warnings. Build succeeds. Non-negotiable.
Step 4: Ship and monitor. Commit, push, watch CI, watch deployment. Fix if either fails.
The "query real data" part of Step 1 has saved me more times than I can count. One of the critical bugs from the audit was a feature reading encrypted ciphertext and passing it straight to the AI as if it were plaintext. The fix was trivial. The lesson was permanent: never assume what's in a field until you've looked at it.
Fix the System, Not the Prompt
This is the rule I wish I'd learned on day one instead of day sixty.
When Claude misbehaves — wrong tool, lost context, off-topic response — the instinct is to add a behavioral rule to the prompt. "Don't do X." "Always check Y before Z." "Never use approach A when B is available."
This doesn't scale. A prompt with 20 rules creates contradictions. Claude follows rule 14 and breaks rule 7. You add rule 21 to patch the conflict, which creates a new conflict with rule 3.
The fix is almost never more instructions. It's structural:
- Wrong tool available? Remove it from the tool set.
- Wrong data in context? Don't inject it.
- Ignoring conversation state? Make the state more prominent structurally, not as a sentence buried in a paragraph.
- Doing X when it should do Y? Give it the right tool for Y, not a rule saying "don't do X."
The principle: if the right tools and context are present, the right behavior follows. If the AI can't see something, it can't misuse it. If the data is structured, it can't misread it.
I wrote this as a rule in my CLAUDE.md after weeks of prompt-patching failures. It's the single most transferable lesson in the entire file.
The Extraction Protocol
Two of my core files grew past 4,000 lines each. At that scale, every change risks breaking something else, and Claude starts losing track of what's where.
The rule is simple: before adding ANY code to a large file, extract the section you're about to touch first, then add your feature to the extracted module.
Each large file has an extraction map in the CLAUDE.md — a table showing what sections exist, where they should be extracted to, and whether the extraction has happened yet. When Claude needs to add a feature that touches message rendering, it sees the map, extracts message rendering to its own module if it hasn't been already, then adds the feature there.
The key insight: features may take longer this way. That's fine. Shrinking large files is the priority. Every change should make the file smaller, not bigger.
This is the kind of rule you'd never put in a template because it's specific to your codebase. But the principle is universal: at scale, your CLAUDE.md needs to encode your codebase's structural debt and the plan for fixing it.
Lint Zero
My lint configuration enforces --max-warnings 0. Zero warnings allowed. This is a hard zero.
At 1.1 million lines of code, my codebase is cleaner than most projects at 10,000 lines. That's not because I'm disciplined — it's because the rule is in the CLAUDE.md and Claude follows it. Every change, every file, every session.
The CLAUDE.md includes specific fix patterns for common warning types. Unused variables: remove or prefix with underscore. Missing hook dependencies: wrap in useCallback, don't blindly add all deps (that's the number one cause of infinite render loops). The file teaches Claude how to fix warnings, not just that they need to be fixed.
Zero tolerance sounds extreme until you've lived with it for four months. Then it just feels normal.
Tool-First Intent Detection
Early on, I wrote 70+ regex patterns to detect what users were trying to do based on their messages. Pattern matching against natural language input to route to different code paths.
I deleted all of them.
The AI is already good at understanding intent. That's literally what it does. Writing regex to pre-empt it is fighting the tool instead of using it.
Now the rule is: before writing any code that detects user intent, ask yourself if this should be a tool that Claude calls instead. If you're writing if (message.includes('...')) or creating arrays of regex patterns or building detect* functions that parse user input — stop. Give the AI a tool and let it decide when to use it.
There are exceptions. Security checks, auth validation, rate limiting, input sanitization — deterministic code is correct for deterministic problems. But intent detection is not a deterministic problem.
The Communication Protocol
Claude reports status using structured formats:
BLOCKER: [Description]
NEED FROM KABLE: [Question/decision needed]
SPEC REVISION NEEDED: [filename]
ISSUE: [What's unclear]
SUGGESTED: [Recommendation]
COMPLETED: [Component]
TESTED: [Yes/No]
NEXT: [What's next]
This sounds minor. It's not. When you're running three parallel Claude sessions building different parts of a feature, structured status reporting is the difference between coordination and chaos. I scan the output, see the format, know instantly whether I need to make a decision, unblock something, or move on.
It also forces Claude to think about what it's actually reporting. "Completed" requires a test status. "Blocker" requires a specific ask. No hand-waving.
The Phase Shift
At 1 million lines and 287 features, I stopped building.
There's a section in my CLAUDE.md that says the current phase is "Test, Fix, Polish, Ship." It explicitly says to resist suggesting new features. If something feels like it needs a new system, the question is: "Is this worth building right now, or should we ship what we have?"
This was necessary because the most dangerous thing about building fast with AI is accumulating surface area. You can ship ten features in a day. Each one adds edge cases, state management, integration points, and potential bugs. At some point, the responsible thing is to stop and make what you have solid.
The phase shift is written into the CLAUDE.md as a temporary operating mode. Claude reads it and adjusts its behavior accordingly — defaulting to testing and fixing rather than proposing new systems. When it's time to build again, I'll update the file.
The Numbers
For anyone who wants the trajectory:
| Date | Lines | Milestone |
|---|---|---|
| Dec 20 | 193K | Tracking started |
| Jan 24 | 464K | Core features complete |
| Feb 28 | 737K | Enterprise and integrations |
| Mar 30 | 1,000K | 1M milestone, phase shift |
| Apr 13 | 1,122K | 320+ features, polish mode |
Average output: roughly 8,000 lines per day over the full period. Peak days exceeded 30,000 lines. Total AI cost: about $1,470.
The CLAUDE.md grew in proportion. It started as maybe 50 lines of basic context. Now it's 1,200 lines encoding architecture decisions, security rules, UI standards, estimation frameworks, extraction maps, and operational protocols.
The Meta-Lesson
A CLAUDE.md is not documentation. It's not a README. It's not a style guide.
It's a relationship. Between you and an AI that has no persistent memory unless you give it one. Every rule is a handshake: "This is how we work together. This is what I learned. Don't make me learn it again."
The file works because it grows from real problems. It encodes judgment, not just instructions. And it compounds — each rule makes the next session better, which means the next 5,000 lines of code are cleaner, faster, and more correct.
If you're building with Claude Code and your CLAUDE.md is still short, you're either early in the journey or you're not learning from your mistakes yet. Both are fine. Just keep adding to it.
What's in Your CLAUDE.md?
I'm curious what other builders are putting in theirs. Different codebases, different problems, different scar tissue.
Here's a sanitized version of the key excerpts from mine — the framework sections without the internal architecture details: My CLAUDE.md on GitHub Gist
And if you're building something substantial with Claude Code and haven't started a CLAUDE.md yet: start today. Write down the first thing that went wrong. That's your first rule.
The file will grow from there.
Top comments (0)