Skip to content
KoishiAI
ไทย
← Back to contents
Chapter 4 / 12 · April 24, 2026

Vibe Coding and Extensions

Working with more flow, using the agents, skills, MCP servers and plugins that extend what Claude Code can do.

This guide is All Rights Reserved — free to read, copying/republication requires permission.

Chapter 4 | Vibe Coding Written for: every level — beginners getting started, and experienced hands who want more throughput

What vibe coding is, and why it changed the field

Vibe coding is a term Andrej Karpathy (formerly head of AI at Tesla and at OpenAI) coined in 2025. It means writing software by telling an AI in plain language what you want and letting it produce the code. Your job is to set the direction, test, and give feedback — not to type every line by hand.

The difference between a traditional developer and a vibe coder

Traditional developerVibe coder with Claude
Writes every function themselvesStates the requirement and lets Claude write it
Has to remember the syntax of every languageHas to know what they want; no syntax to memorise
Googles for solutions one at a timeStates the problem; Claude proposes solutions with trade-offs
Debugs entirely aloneHands the error log to Claude to analyse and fix
Has to know every library APIStates the goal; Claude picks a fitting library
Roughly 50-100 lines an hourRoughly 500-2000 lines an hour (depending on the work)

Vibe coding does not mean “you need to know nothing”

The most common misunderstanding is that vibe coding means needing no programming knowledge at all. The reality:

⚠️ What you still need to know even while vibe coding
Know what output you want: “an API that returns JSON” beats “make it work”
Know enough to review: you can read the main logic and tell what the code does
Know the red flags: a console.log that should not be there, a hardcoded password
Know when to stop: if Claude has looped five times without a fix, the method has to change
Know the boundaries: work that needs high security needs a more careful review than usual

What kinds of work does it suit?

Strong fit ✅Moderate fit ⚠️Handle with care 🔴
Prototype & MVPCore business logicAuthentication / Authorization
Internal toolsAPI integrationPayment / Financial system
Automation scriptsPerformance-critical codeCryptography
Data processingMulti-threaded codeCompliance / HIPAA / GDPR
CRUD featuresDatabase schema designMission-critical infrastructure
Boilerplate codeComplex algorithmsSmart contract / Blockchain

The right mindset for vibe coding

Think of yourself as a product manager, not a programmer

Your role shifts from “the person who writes the code” to “the person who sets the direction”. You need to know what you want, state the requirement clearly, and review the result. Claude takes the implementation.

💡 An analogy that helps
You = the architect: you decide how many rooms the house has, where they go, what it is built from
Claude = the builder: knows how to build, knows the techniques, works fast
If the architect is vague, the builder builds the wrong thing — and that is not the builder’s fault

The laws of vibe coding

Law #1: Clear beats cleverA plain, clear prompt gives a better result than one that sounds clever but is vague
Law #2: Small beats largeSplitting the work into small pieces beats sending everything at once
Law #3: Test everythingNever assume Claude is right; test everything before it goes anywhere near production
Law #4: Context is goldClaude works far better when it is given good context
Law #5: Fail fastIf Claude cannot fix it in three rounds, change the method or split the problem apart
Law #6: Commit oftenCommit every time you have working code, in case Claude breaks everything on the next round

Writing prompts for vibe coding

The most important skill a vibe coder has is not writing code — it is writing a good prompt. Here is a framework that works in practice.

The CLEAR framework for a good prompt

C — ContextSay what the project is, what the tech stack is, and where in the system this sits
L — Language/LibraryName the language, the framework and the version explicitly
E — Expected OutputSay what you want, in what shape, and how long
A — AssumptionsState the assumptions Claude should hold, such as “assume the user is already authenticated”
R — RestrictionsThe prohibitions and the limits, such as “no any”, “must run on Node 18”

Example: a bad prompt versus a good one, side by side

Example 1: building a login form

❌ Bad
make me a login form
✅ Good
Build a Login Form with React + TypeScript
Use React Hook Form for validation
Fields: email (format validation), password (min 8 chars)
Show a loading state while submitting
Errors: shown under the offending field, not in an alert
On success → redirect to /dashboard
Use Tailwind CSS, support dark mode

Example 2: fixing a bug

❌ Bad
why doesn’t this code work
(pastes 200 lines)
✅ Good
In @src/api/user.ts, function getUserById
when userId is null I get the error:
“Cannot read properties of null”
Expected: return null when the user does not exist
Using Prisma + TypeScript strict mode

Example 3: building a new feature

❌ Bad
add search to the system
✅ Good
Add Search to the Product List Page
Tech: Next.js 14, Prisma, PostgreSQL
Requirements:
- Search by: product name, brand, category
- Debounce 300ms (do not hit the API on every keystroke)
- Show a loading skeleton while the search runs
- The URL follows the search term (?q=xxx)
- Works together with pagination

Prompt patterns used often in vibe coding

Pattern 1: “Act as” — give Claude a persona

Telling Claude to play a specific role gives its answers the perspective and the standard you are after:

> Act as a Senior Backend Engineer specialising in Node.js
  and PostgreSQL performance optimization
  Review @src/db/queries.ts and identify every N+1 query,
  then propose a fix for each using Prisma
 
> Act as a Security Auditor
  Audit @src/api/auth.ts for every vulnerability
  Sort them by severity (Critical/High/Medium/Low)
 
> Act as a UX Developer who owns Accessibility
  Review @src/components/Form.tsx
  so it meets the WCAG 2.1 AA standard
Pattern 2: Step-by-step — break the work into stages

For complex work, spell out the stages you want instead of only naming the end result:

> Work through this one step at a time, waiting for my approval before the next:
 
  Step 1: analyse @prisma/schema.prisma
  and list every relationship in it
 
  Step 2: identify the three most expensive queries
  in @src/server/actions/*.ts
 
  Step 3: propose an index strategy for each of those queries
 
  Step 4: create a Prisma migration with a rollback plan
Pattern 3: Constraint-first — state the limits before the goal

Say what has to be avoided before you say what you want, and Claude will not have to redo the work:

> Constraints (read before doing anything):
  - Do not install new dependencies
  - Do not touch files outside src/features/payment/
  - Must run on Node 18 (no newer APIs)
  - Error messages must be in Thai
 
  Task: build a retry mechanism for the payment API
  If the API fails, retry 3 times with exponential backoff
  Log every retry attempt
Pattern 4: Example-driven — show the result you want

Instead of describing it in words, show the input and output you expect:

> Build a formatCurrency function matching these examples:
 
  Input → Expected Output:
  1000      → "1,000 baht"
  1500.50   → "1,500.50 baht"
  0         → "0 baht"
  -500      → "-500 baht"
  1000000   → "1,000,000 baht"
 
  Use TypeScript, and handle null/undefined as well
  Write unit tests covering every case with Vitest
Pattern 5: Iterative refinement — shape it in a loop

You do not need a perfect result on the first try. Iterate:

# Round 1: a draft
> Build a rough Product Card component first
  It does not have to look good yet, just get the structure right
 
# Round 2: add the detail
> Good. Now add:
  - Hover effect
  - Loading skeleton state
  - Error state for when the image fails to load
 
# Round 3: responsive
> Make it responsive:
  - Mobile: a list (1 column)
  - Tablet: a 2-column grid
  - Desktop: a 3-column grid
 
# Round 4: accessibility
> Add aria-label, role, keyboard navigation

A vibe coding workflow that holds up

This workflow comes out of several hundred hours of real work. It is built to give the best result without putting the code at risk.

Phase 1: Setup — every time, before you start (5 minutes)

1Always create a new branch
Never work on main directly. Branch every time, even for small work
git checkout -b vibe/feature-name
# example: git checkout -b vibe/user-profile-page
2Read CLAUDE.md before you start
Always have Claude read the project’s context first
> Read @CLAUDE.md and tell me what we are working on
  and what rules this project has that I need to watch for
3Set the scope explicitly
Tell Claude what this session covers and what is out of scope
> This session we are building the User Profile Page
  Scope: show user data, edit profile, upload avatar
  Out of scope: email change, password change (a separate session)
  Start with the data layer, then the UI

Phase 2: Build — the main working cycle

Repeat this cycle for every feature until it is finished:

ADescribe — say what you want
Use the CLEAR framework to write a clear prompt: context, expected output, constraints
BClaude builds — let it finish
Do not interrupt. Let Claude finish the work, then review the whole thing
CCheck — read the code
Read the main logic, look for unhandled edge cases and security issues, before you run anything
DRun — run it and test it
Run the tests, run the dev server, click through it, watch the console for errors
EFeedback — tell Claude the result
If it works, commit and move on. If there is an issue, tell Claude, with a clear error log
FCommit — record every milestone
Commit whenever a sub-feature works. Do not wait for everything to be finished
# a real session
 
# A: Describe
> Build a Prisma query that fetches a User Profile
  include: posts (5 most recent), followers count, following count
  exclude: password, resetToken
  type-safe with TypeScript
 
# D: Run - run the query test
npx ts-node -e "const {getUserProfile} = require('./src/lib/user'); getUserProfile(1).then(console.log)"
 
# E: Feedback - when there is an error
> I get this error:
  PrismaClientKnownRequestError: Invalid `prisma.user.findUnique()`
  The column `User.followersCount` does not exist
  Our schema has no such column; it has to be counted from the relation instead
 
# F: Commit once it passes
git add -A && git commit -m "feat(user): add getUserProfile query with relations"

Phase 3: Review — every time, before you merge

This step matters. Do not skip it even when you are in a hurry. Use Claude to review Claude’s own code:

# Self-review with Claude
git diff main...HEAD | claude -p \
"Review this code as a Senior Developer
 Check:
 1. Security vulnerabilities
 2. Whether the error handling is complete
 3. Edge cases that may have been missed
 4. Performance issues
 5. Unnecessary TypeScript any
 Give me a list ordered from high to low priority"
 
# Targeted security check
git diff main...HEAD | claude -p \
"Check security only:
 - SQL Injection
 - XSS
 - IDOR (Insecure Direct Object Reference)
 - Missing authorization checks
 - Sensitive data exposure"

Real vibe coding, from start to finish

Every example in this section is a real prompt and workflow that works on a Next.js + TypeScript + Prisma project. Adapt the tech stack as you need.

Example 1: building a new REST API endpoint

The situation: you need an API that lets a user view their Order History, with pagination.

# Prompt 1: analyse before building
> Look at @prisma/schema.prisma and @src/types/
  then design an API endpoint for GET /api/orders
  Requirements:
  - Pagination: ?page=1&limit=10
  - Filter: ?status=pending|completed|cancelled
  - Sort: ?sort=date_desc|date_asc|amount_desc
  - Return: orders array + totalCount + totalPages
  Tell me whether the schema is missing any field
  before you implement anything
 
# (Claude analyses it and reports that the schema is missing an index)
 
# Prompt 2: create the migration first
> Create a Prisma migration adding the indexes you recommended
  Name the migration: add_order_query_indexes
 
# Prompt 3: build the service layer
> Create a getOrderHistory function in src/server/services/order.ts
  Takes: userId, page, limit, status?, sort?
  Return type: { orders: OrderWithItems[], totalCount: number }
  Use a Prisma transaction for the count + fetch
  Handle: invalid userId → throw NotFoundError
 
# Prompt 4: build the API route
> Create src/app/api/orders/route.ts
  - GET handler calling getOrderHistory
  - Validate query params with zod
  - Auth check with getServerSession
  - Error handling: 400, 401, 404, 500
  - Rate limiting: 60 requests/minute
 
# Prompt 5: write the tests
> Write unit tests for getOrderHistory
  covering these cases:
  - Happy path: the correct orders come back
  - Empty result: the user has no orders
  - Invalid userId: throws
  - Pagination: page 2 returns the right data
  Use Vitest + a Prisma mock
Example 2: fixing a performance bug

The situation: the Dashboard page takes 8 seconds to load and has to come down under 2 seconds.

# Prompt 1: analyse the problem
> The Dashboard page in @src/app/dashboard/page.tsx
  loads very slowly and I need to profile it
  Please:
  1. Add console.time/timeEnd to measure each section
  2. Point out which part looks slowest from the code
  3. Recommend a better profiling approach
 
# (Running it produces the data: the DB query takes 7 seconds)
 
# Prompt 2: analyse the query
> Here is the slow query (7 seconds):
  [paste the query code]
 
  The problems I can see:
  - It fetches every user and then filters in JS
  - There is no index on created_at
  - It does not use pagination
 
  Please rewrite it properly:
  - Filter in the DB, not in JS
  - Add the indexes it needs
  - Add pagination
 
# Prompt 3: fetch in parallel
> The Dashboard fetches four things separately:
  totalRevenue, newUsers, activeOrders, topProducts
 
  Right now they run one after another (sequential)
  Refactor them to run in parallel with Promise.all
  and cache the results with unstable_cache (Next.js)
  TTL: 5 minutes
Example 3: refactoring old code

The situation: there is an old function written badly, and it needs refactoring without any change in behaviour.

# Prompt 1: analyse first
> Analyse @src/utils/pricing.ts
  and tell me:
  1. What each function does
  2. What code smells are in there
  3. Whether there are any bugs hiding in it
  Do not fix anything yet, just analyse
 
# Prompt 2: write the tests before refactoring
> Write comprehensive tests for
  every function in @src/utils/pricing.ts
  before we refactor, so we know behaviour does not change
  Cover the edge cases the analysis turned up
 
# Prompt 3: refactor one function at a time
> Refactor the calculateDiscount function in pricing.ts
  Goals:
  - Remove the duplicated code
  - Pull out the pure functions
  - Add the missing TypeScript types
  - Do not change behaviour (every test must still pass)
  One function at a time; wait for my approval before moving on
Example 4: building a whole feature from scratch (mini project)

The situation: build a Notification System, from the database schema up to the UI.

# Session 1: Database Design
> Design a Notification system for this project
  Requirements:
  - Types: order_update, payment_success, promo, system
  - Status: unread/read/archived
  - Delivery: in-app (first), email (later)
  - Scalability: some users may have 10,000+ notifications
 
  Produce:
  1. The Prisma schema
  2. The migration
  3. Seed data for testing
 
# Session 2: Backend
> Build the full notification service:
  - createNotification(userId, type, data)
  - getNotifications(userId, { unreadOnly, limit, cursor })
  - markAsRead(userId, notificationIds[])
  - markAllAsRead(userId)
  - deleteNotification(userId, notificationId)
 
  Use cursor pagination (not offset)
  Add an optimistic update for markAsRead
 
# Session 3: API Routes
> Build the API routes:
  GET  /api/notifications
  POST /api/notifications/read
  POST /api/notifications/read-all
  DELETE /api/notifications/:id
 
  Auth required on every route
  Rate limit: 100 req/minute
 
# Session 4: Real-time with Server-Sent Events
> Add real-time notifications:
  - GET /api/notifications/stream (SSE endpoint)
  - Client subscribes with EventSource
  - Push immediately when a new notification arrives
  - Reconnect automatically if the connection drops
 
# Session 5: UI
> Build a NotificationBell component:
  - Badge showing the unread count
  - Dropdown listing the 10 most recent
  - Mark as read on click
  - A "Mark all as read" button
  - A "View all" link to the /notifications page
  - Animate when a new notification arrives

Fixing bugs the vibe coding way

Debugging with Claude has an art to it. Giving it correct, specific information is the key.

The shape of a bug report Claude can fix quickly

# A bug report template for Claude
> Bug Report:
 
  ## Environment
  - OS: macOS 14.3
  - Node: 20.10
  - Browser: Chrome 122 (for a frontend bug)
 
  ## Error Message (all of it)
  TypeError: Cannot read properties of undefined (reading "map")
    at ProductList (./src/components/ProductList.tsx:45:23)
    at renderWithHooks (...)
 
  ## Steps to Reproduce
  1. Log in with a normal account
  2. Go to /products
  3. Click the "On Sale" filter
  4. The error appears immediately
 
  ## Expected
  A list of sale products
 
  ## Actual
  The page breaks, error in the console
 
  ## Code that is probably involved
  @src/components/ProductList.tsx lines 40-50
 
  ## What I have already tried
  - console.log products before .map → it is undefined
  - The normal filter (without On Sale) → works fine

Systematic debugging techniques with Claude

Method 1: binary search the bug

> Problem: the checkout flow misbehaves and I do not know which step
 
  Add logging so we can find where the problem is:
  Add a console.log at the beginning of:
  - validateCart()
  - calculateTotal()
  - createPaymentIntent()
  - updateInventory()
  - sendConfirmationEmail()
 
  Log format: "✓ [function name] completed: [result]"
  or "✗ [function name] failed: [error]"

Method 2: hypothesis testing

> Bug: some users can log in, some cannot
 
  My hypotheses:
  H1: Email is case-sensitive (john@test.com vs John@test.com)
  H2: Some special character in the password is not escaped correctly
  H3: The session expires too quickly
 
  Please:
  1. Analyse @src/lib/auth.ts and say whether H1, H2, H3 are possible
  2. Add logging to test each hypothesis
  3. Propose a fix for the most likely one

Method 3: rubber duck debugging

> I will explain the logic as I understand it; find the flaw:
 
  My understanding:
  1. The user clicks "Add to Cart"
  2. The frontend calls POST /api/cart
  3. The server increments the quantity of the existing product
  4. It returns the new cart
  5. The frontend updates state
 
  Bug: adding the same product twice in quick succession
  gives quantity 1, not 2
 
  (Claude usually spots the race condition quickly)

Advanced techniques for vibe coding

Technique 1: the two-Claude technique

Use two Claudes on the same job — one builds, the other reviews — to catch the blind spots:

# Claude number 1: build the implementation
> Build an authentication middleware for Express
  [implementation...]
 
# Claude number 2 (a fresh session): review
> Here is an authentication middleware that was just written:
  [paste the code from Claude number 1]
 
  Review it as a Security Expert:
  - The vulnerabilities you can see
  - Possible attack vectors
  - Missing edge cases
  Do not look only at the logic; look at it from an attacker's angle

Technique 2: documentation-driven development

Write the documentation first and have Claude implement against it. The code lands closer to the spec:

# Step 1: write the API doc first
> Write the API Documentation for the
  Payment Webhook endpoint
  in OpenAPI 3.0 format
  Cover: request format, response codes,
  error cases, security requirements
 
# Step 2: have Claude implement against the doc
> Implement the endpoint against this API doc:
  @docs/api/payment-webhook.yaml
  Do not add or remove behaviour relative to the spec
  If the spec is unclear, ask instead of assuming

Technique 3: test-first vibe coding

Have Claude write the tests first and implement afterwards. The result covers far more:

# Step 1: write the test spec
> Write test cases for coupon validation:
  - expired coupon → error
  - coupon used past its limit → error
  - coupon does not match the user tier → error
  - price below the minimum → error
  - happy path → returns the discount amount
  - both percentage and fixed-amount coupons
 
  Use Vitest, mock Prisma
  Do not implement the actual function yet
 
# Step 2: let the tests be the spec for the implementation
> Now implement the validateCoupon function
  so that every test in @src/__tests__/coupon.test.ts passes
  Do not modify the tests

Technique 4: scaffolding plus customisation

Have Claude generate the boilerplate first and adjust it afterwards. That is faster than doing everything in one pass:

# Step 1: generate the scaffold quickly
> Generate CRUD boilerplate for the Product entity
  - Service layer (create, read, update, delete, list)
  - API routes
  - TypeScript types
  - Basic tests
  Use the same pattern as @src/features/user/
 
# Step 2: layer on the business logic
> The Product scaffold is in place
  Now add the specific business rules:
  - The price must not fall below cost (see product.cost)
  - Products in the "featured" category need at least 3 images
  - When stock drops below 10, send an alert to the admin

Technique 5: progressive enhancement

Start from an MVP that works, then enhance one layer at a time. Feedback comes fast and the risk stays low:

# Layer 1: make it work first (no frills)
> Build search for products
  Version 1: a basic SQL LIKE search
  Nothing fancy, it just has to work
 
# Layer 2: add the UX
> Now add:
  - Debounce 300ms
  - Loading state
  - A "No results" empty state
 
# Layer 3: performance
> Optimize:
  - Add a full-text search index
  - Cache the results of frequent searches
 
# Layer 4: advanced features
> Add:
  - Search suggestions
  - Recent searches
  - Filters combined with search

Anti-patterns to avoid

Vibe coding has pitfalls that come up again and again. Knowing them in advance saves a lot of time.

Anti-pattern 1: prompt spaghetti

Prompting over and over without understanding the problem, which leaves the code messier each round.

❌ Prompt spaghetti
> why is it erroring
> still erroring after the fix
> try another approach
> still nothing, try again
(ten rounds later, the code is worse)
✅ The right way
> Stop, reset, git stash
> Describe the problem again from the start
> Give the exact error plus context
> One step at a time, approval before moving on
(fixed in one or two rounds)

Anti-pattern 2: context overload

Handing Claude every file at once, which confuses it and fills the context fast.

❌ Context overload
> Read every file in src/
and tell me what could be refactored
(50 files, 5,000 lines)
Claude: confused, vague answers
✅ Focused context
> Look at @src/features/payment/ only
what could be refactored
focus on error handling
(5 files, 300 lines)
Claude: clear answers you can act on

Anti-pattern 3: not committing before a large change

This is the most painful mistake there is. Claude makes the change, everything breaks, and there is no way back:

⚠️ The iron rule: always commit first
Before letting Claude do anything that touches several files → git commit
Before letting Claude do a large refactor → git commit
Before agentic mode (working autonomously) → git commit
If you forgot to commit and Claude has already changed the files → git stash to keep the old work
The commit message does not have to be pretty; “WIP before Claude refactor” is enough

Anti-pattern 4: vibing without tests

❌ No testing culture
“Claude wrote it, it is probably fine”
“No time to write tests”
“We will test it at launch”
→ Bugs found in production
→ Hard to fix, because there are no tests
✅ A testing culture
Every new feature has at least a happy-path test
Claude writes the tests along with the implementation
Tests run before every commit
→ Bugs found in development
→ Refactoring can be done with confidence

Anti-pattern 5: not reading the code Claude wrote

Claude can produce code that works but has a problem hiding in it. Not reading it at all is a risk not worth taking:

Read at minimumFunction signatures, return types, the error handling pattern
What to flagHardcoded values, TODO comments, a console.log someone forgot to delete
Red flagsThe any type, eval(), raw SQL strings, a missing auth check
If you cannot follow itHave Claude explain the main logic first, then approve

Vibe coding for people with no programming background

Vibe coding is not limited to developers. Someone who has never written code can use Claude to build what they need — but the approach is different, and worth understanding.

What to have ready before you start

Know what you want: you do not need to know how it is implemented, but you do need to know what the output looks like.

Have the environment ready: Node.js installed, VS Code, a terminal you can open.

Be patient and ready to iterate: the first result may not be complete; it will need refining.

Know how to copy and paste: know how to copy an entire error message across to Claude.

A workflow for non-developers

1Say what you want in plain language
No technical vocabulary needed. Say it the way you understand it; Claude will ask if it is unclear
> I need a program that:
  - Takes a CSV file with customer names and purchase amounts
  - Calculates the total per customer
  - Emails me a summary every Sunday
  I am on Windows and I do not know much about coding
2Let Claude recommend the approach
Claude will propose something that fits your level of knowledge
3Ask Claude to explain every step
You do not have to guess what to do. Ask at any point
> Tell me step by step what I need to do
  and explain why each step is needed
  If I have to type something in the terminal, say exactly what
4Copy the whole error across to Claude
When an error appears, copy all of it; do not translate it
> I ran it and got this error:
  [paste the entire error]
  I followed every step you gave me

Tools a non-developer can build with vibe coding

ToolDifficulty to vibe codeBenefit
Excel → Google Sheets automationEasySaves time summarising data
Automated email digestEasyNo summarising by hand
PDF → text extractionEasySearchable documents
A personal data dashboardModerateKPIs at a glance
A chatbot answering FAQsModerateLess support workload
A simple websiteModerateNo freelancer needed
An internal admin toolModerateEasier data management

Measuring how a vibe coding session went

At the end of each vibe coding session, review how it went. That is how the skill compounds.

Metrics worth tracking

Prompt efficiencyHow many prompts did it take to get the result you wanted? Fewer is better
Iteration countHow many rounds before the code worked? Target: under 3 per feature
Test coverageWhat percentage of the resulting code is covered by tests? Target: over 70%
Bug rateAfter deploying, how many bugs a week come from vibe-coded work?
VelocityHow many features a day, compared with before Claude?
Token costHow many tokens on average per feature? Can that come down?

Session retrospective — reviewing after the work

Use Claude to run the retrospective at the end of a big session:

> Run a retrospective for this session:
 
  We worked [X] hours and built [Y] features
  We got stuck on [describe problems]
  We used roughly [Z] prompts overall
 
  Please analyse:
  1. Which prompt gave the best result, and why?
  2. Which prompt wasted the most time?
  3. If we did this again, what would we change?
  4. Is there a pattern worth adding to CLAUDE.md?

Glossary for this chapter

Vibe codingWriting software by telling an AI in plain language what you want and letting it produce the code
Prompt engineeringThe craft of writing prompts that make an AI understand and do exactly what you meant
CLEAR frameworkContext, Language, Expected Output, Assumptions, Restrictions
IterationRepeating to improve: a draft on the first round, more detail on the next
ScaffoldThe basic structure of the code, generated quickly, with no business logic in it yet
Progressive enhancementStarting with a simple version and enhancing it a layer at a time
Two-Claude techniqueOne Claude builds, another reviews, to catch the blind spots
Test-firstWriting the tests before implementing, which produces more correct code
Anti-patternAn approach that looks sensible but tends to cause trouble later
Prompt spaghettiPrompting over and over without understanding the problem, which makes the code messier
Context overloadSending so much that Claude gets confused and the context fills up
Rubber duck debuggingExplaining the problem out loud to someone else, which often surfaces the cause on its own
Binary search debugFinding a bug by halving the problem: test the first half, and if it is fine the bug is in the second
RetrospectiveReviewing what was done in order to learn from it and improve

Chapter 4 | Vibe Coding — continued (page 20 onward) Written for: every level — debugging techniques, ways out for non-coders, and the AI Wiki and RAG for developers

How to stop Claude looping on a bug

The most frustrating thing in vibe coding is Claude going round in circles on a bug without ever fixing it. Try one approach, no; try a second, still no; back to the first. That can go on for hours. This section is about getting out of the loop systematically.

Why does Claude loop?

Understanding the cause is what makes the way out obvious. Loops come from four main causes:

Cause 1: missing contextClaude does not know what has already been tried, so it proposes the same thing again without realising, because the conversation has grown too long
Cause 2: the problem is somewhere elseYou are fixing A, but the real cause is B, so Claude keeps fixing A without knowing the problem lives elsewhere
Cause 3: hallucinationClaude is not sure of the answer but still tries to generate one, so the result is wrong in the same way each time
Cause 4: a vague promptThe requirement is unclear, so Claude interprets it differently each round and produces different results

The stop-the-loop protocol — follow this order every time

Once Claude has tried and failed twice, stop immediately and follow this protocol:

Step 1: hard stop — halt everything first
# Type this the moment Claude starts looping
> Stop. Do not try a new approach.
  Collect everything that has already been tried:
  1. Approaches tried that did not work (list them all)
  2. What we know for certain from those experiments
  3. What we still do not know and need more information about
  4. Your hypothesis for where the problem actually is
  Do not propose a new fix until you have answered those four
Step 2: reset the context — clear the bias

An over-long conversation leaves Claude biased by what has already been tried. A reset lets it see the problem fresh:

# Option A: /compact and carry on
/compact
# then brief Claude again on what the problem is
  without telling it what has been tried
 
# Option B: start a completely new session
# quit claude and open it again
claude
 
# then give it fresh context in this bug report format:
> Fresh start: there is a bug [describe] in @[file]
  Error message: [all of it]
  Expected: [describe]
  Actual: [describe]
  Context: [tech stack, environment]
  Do not refer to any previous session; analyse this from scratch
Step 3: isolate — separate the problem out
Instead of fixing the whole system, build a Minimal Reproducible Example (MRE) — the smallest piece of code that still shows the problem:
> Build a Minimal Reproducible Example for this bug:
  I need code that:
  1. Is as small as possible while still showing the problem
  2. Runs without needing a database or an external API
  3. Produces the same error we are seeing
 
  If the MRE makes the problem disappear, that means the problem is
  in the interaction between components, not in the core logic

An example: building a real MRE

# The problem: User.findMany() crashes in production
# but works fine in development
 
# MRE: create a small test file
> Create a file test-db.ts that:
  1. Connects to the database directly, not through the middleware
  2. Runs the same User.findMany() that crashes
  3. Logs the result
  Then run it in the production environment
  to see whether the crash is in the query itself or in the middleware
Step 4: change strategy — take a different approach

If fixing it head-on does not work, change the strategy for the whole problem:

StrategyWhen to use itHow
RewriteThe code is too tangled to fixDelete it and write it again from the spec
WorkaroundThe root cause is hard and you need a fix nowFind a way around the problem, even if it is not elegant
SimplifyThe feature is too complexCut the requirement down and do a simpler version
Change libraryThe library itself has the bugSwitch to a different library
Skip for nowThe bug is not criticalMove past it and leave a TODO comment
External helpClaude genuinely cannot fix itStack Overflow, GitHub Issues, Discord

Techniques for particular kinds of bug

Bug type 1: bugs you cannot reproduce (intermittent bugs)

A bug that only happens sometimes is the hardest kind to fix, and the kind Claude loops on most.

> This bug happens intermittently. Do not try to fix it directly.
  Do this instead:
 
  1. Add comprehensive logging first:
     - a timestamp at every step
     - the state of the important variables
     - a request ID for tracking
 
  2. Build log aggregation so the pattern becomes visible:
     When does it happen? How often? Which kind of user?
 
  3. Once there are enough logs, analyse them for the common factor
 
  Do not change any code until the logs tell us the root cause
Bug type 2: bugs that depend on the environment

Works locally, breaks in staging or production. This is hard for Claude because it cannot see the real environment.

> This bug only happens in production
  Analyse the differences between the environments:
 
  Local:
  - Node version: [x]
  - Database: local PostgreSQL
  - ENV vars: [list]
 
  Production:
  - Node version: [x]
  - Database: AWS RDS
  - ENV vars: [list]
 
  Build a checklist of everything that could differ
  and how to verify each item
 
  Do not assume the code is wrong until every
  environment issue has been ruled out
Bug type 3: race condition bugs

These happen when several parts of the code run at once and share state — hard for Claude to grasp, so spell it out.

> I suspect this is a race condition
  Here is the scenario:
 
  1. User A clicks "Add to Cart" (async)
  2. User A clicks "Add to Cart" again before the first finishes
  3. Both rounds read the old cart before updating
  4. The first round saves quantity = 1
  5. The second round also saves quantity = 1 (not 2)
 
  Please:
  1. Confirm whether this really is a race condition
  2. Propose a suitable solution (optimistic lock,
     pessimistic lock, queue, or something else)
  3. Explain the trade-offs of each solution
  4. Implement the best one for this use case

A CLAUDE.md section that prevents loops in advance

Add this section to CLAUDE.md so Claude handles bugs systematically from the outset:

## 🐛 Bug Fixing Protocol
 
### When you hit a bug, always follow this order:
1. **Understand first**: state how you understand the problem before anything else
2. **Hypothesize**: propose 2-3 hypotheses, not just one
3. **Test hypothesis**: find the fastest way to verify
4. **Fix root cause**: fix the cause, not the symptom
 
### Loop Prevention Rules:
- If two attempts fail → STOP and ask
- Never retry the same approach if the error message is unchanged
- If you do not know the root cause → say so plainly, do not guess
- If you need more information → have the user gather it first
 
### When stuck:
1. Summarise what is known for certain and what is not
2. Propose diagnostic steps the user can run
3. If external information is needed, name the URL or the doc

The “Explain like I’m wrong” technique — making Claude challenge its own assumptions

This one is powerful. It forces Claude to analyse from a new angle instead of confirming its existing bias:

> We have tried to fix this auth bug three times without success
  Now I want you to assume that
  "what we think the problem is may not be the real problem"
 
  Re-analyse from zero:
  1. If the JWT token is not the problem, where else could it be?
  2. What have we not tested at all?
  3. What assumptions might we have wrong?
  4. If you were this bug, where would you be hiding?

Decision tree: when to stop vibing and do something else

Sometimes vibe coding is not the right solution. Learn the signals that it is time to change method:

Claude has tried more than 3 rounds without success→ Reset the context and start again, or break the problem into smaller pieces
The error message changes but it is still broken→ A new bug from the old fix. git revert and use a different strategy
Claude says it is not confident→ Stop vibing. Go and read Stack Overflow or the official docs first
The problem involves a third-party library→ Check that library’s GitHub Issues first; it may be a known bug
The error is infrastructure-level (DNS, SSL, port)→ Leave Claude Code and read the server logs directly
The bug only happens in production→ You need production debugging tools: Sentry, Datadog, CloudWatch
Thirty minutes in and still nothing→ Take a break, ask someone, or post in a community

For people who do not read code: finding a way out intelligently

When something goes wrong that they do not understand, people without a programming background tend to panic and try everything at random. This section is a workflow that lets you solve problems systematically without understanding the code at all.

The underlying principle: “describe it clearly and Claude will handle it”

People who do not read code have one advantage: no technical bias. That means they describe the problem from the user’s point of view, which is often closer to the mark. Use that.

A problem-solving workflow for non-coders

1Describe the behaviour you see, not what you think caused it
Say “what happened”, not “why it happened” — your own interpretation of the cause is usually wrong and sends Claude to the wrong place
❌ Interpreting the cause yourself
“The database has probably broken”
“It is probably a memory problem”
“JavaScript is probably erroring”
(Claude fixes what you said, not the real problem)
✅ Describing behaviour
“I click Save and the page goes blank”
“The image does not show, just an empty box”
“When I type an invalid email, nothing happens”
(Claude analyses from the actual behaviour)
2Copy everything you can see across to Claude
Error messages, the screen, the logs — the more information, the better
# How to copy an error from the browser console
1. Press F12 to open Developer Tools
2. Click the "Console" tab
3. Find the red text → right-click → Copy
4. Paste it into Claude Code
 
# How to copy an error from the terminal
1. Select the whole error message
2. Ctrl+C (Windows) or Cmd+C (Mac)
3. Paste it into Claude Code
 
# Tell Claude where you copied it from
> This is the error from the browser console:
  [paste the error]
 
> This is the error from the terminal when I ran npm start:
  [paste the error]
3Tell Claude what you did beforehand
The order of actions matters. List every step you took before the problem appeared
> Before the error, this is what I did:
  1. Edited the file src/pages/home.js
     (added a red button in the middle)
  2. Saved the file
  3. Refreshed the browser
  4. Saw this error immediately
 
  It was working fine before that
4Have Claude explain before it fixes
Get Claude to explain what the problem is and how it will fix it before it does anything
> Before you fix anything, explain to me:
  1. What this problem is (without technical terms)
  2. How you will fix it
  3. Whether there is any risk, such as breaking something else
  4. Whether there is a Plan B if the fix does not work
  Wait for my approval before you actually change anything
5One step at a time, slowly
No rush. Have Claude do one thing, then report the result before continuing
> Fix it one step at a time and wait for me to report back
  If I do not understand a step, I will ask
  If a step produces a new error, I will copy it for you

A problem-reporting template for non-coders

Copy this template and use it the moment something goes wrong:

------- Template: a bug report for non-coders -------
 
> I have run into a problem and need help
 
  What I want to happen:
  [describe what should work]
 
  What actually happens:
  [describe what happens instead]
 
  The steps I took:
  1. [what I did]
  2. [what I did]
  3. the error appeared
 
  Error message (all of it):
  [paste the error here]
 
  My system:
  - Windows / Mac / Linux
  - Browser: Chrome / Firefox / Safari
 
  Did it work before: yes / it has never worked
 
  Please:
  1. Explain the problem in plain language first
  2. Give me the fix one step at a time
  3. Wait for me to confirm each step
------- end of template -------

When Claude’s answer does not land — how to ask a follow-up

If Claude answers and you still do not understand, there is nothing to be embarrassed about. Ask again. These are the prompts that come up most:

Explain it more simply> Explain that again in a way someone who does not program can follow, without technical terms
Ask for an example> Could you give a more concrete example?
Ask for finer steps> Break this step down further; what exactly do I do?
Confirm your understanding> My understanding is [summarise what you understood]. Is that right?
Ask for an alternative> Is there an easier way that does not involve writing code myself?
Ask what happens if you get it wrong> If I make a mistake at this step, what happens and can it be undone?

No-code alternatives — when vibe coding is too hard

Sometimes there is a route that involves no code at all, and Claude can point you at the right tool:

> I need [describe what you want]
  but I have no programming background
  Is there a tool or platform that can do this without writing code?
  budget: [state it, or "free if possible"]
 
# Tools Claude tends to recommend:
- Websites: Webflow, Framer, Squarespace
- Automation: Zapier, Make (Integromat), n8n
- Database/App: Airtable, Notion, Glide
- Chatbot: Voiceflow, Landbot
- Forms: Typeform, Tally
- Dashboard: Retool, AppSmith

An AI Wiki is your project’s knowledge organised in a form Claude can search and use immediately, instead of you re-explaining the same context every session. It makes a large difference to the quality of the code Claude produces.

Why does an AI Wiki matter?

Picture it: a new developer joins the project knowing nothing. Without documentation, they ask the same questions over and over. Claude is the same. An AI Wiki is the onboarding document that lets Claude read once and get to work without asking.

docs/                          ← the Wiki's main folder
  README.md                    ← an index of every doc
  architecture/
    overview.md                ← system overview + diagram
    data-flow.md               ← how data moves through the system
    decisions.md               ← ADRs (why X was chosen over Y)
  domain/
    business-rules.md          ← the complicated business rules
    glossary.md                ← domain-specific vocabulary
    user-journeys.md           ← every main user flow
  api/
    endpoints.md               ← the full API reference
    error-codes.md             ← every error code + what it means
    auth.md                    ← how to authenticate
  database/
    schema.md                  ← ER diagram + field descriptions
    indexes.md                 ← the indexes that exist and why
    migrations.md              ← the history of important migrations
  runbooks/
    deploy.md                  ← how to deploy, step by step
    rollback.md                ← how to roll back in an emergency
    common-issues.md           ← frequent problems + their fixes
  development/
    setup.md                   ← how to set up a local environment
    testing.md                 ← testing strategy + how to run it
    code-style.md              ← the conventions in detail

How to use the AI Wiki in Claude Code

Method 1: reference it directly in the prompt

# Reference it before starting work
> Before implementing the payment feature
  read the Payment section of @docs/domain/business-rules.md
  and @docs/api/auth.md to understand the auth flow
  then design a solution consistent with the existing pattern
 
# Reference it when something is unclear
> Look at @docs/architecture/decisions.md
  for how we decided to handle caching
  and implement this consistently with that decision

Method 2: point to it in CLAUDE.md so Claude reads it automatically

# add this section to CLAUDE.md
## 📚 Documentation (read before working)
 
### Before implementing any feature:
- read @docs/domain/business-rules.md
- read @docs/architecture/overview.md
 
### Before any database change:
- read @docs/database/schema.md
- read @docs/database/migrations.md
 
### Before any API change:
- read @docs/api/endpoints.md
- read @docs/api/error-codes.md

Writing a good AI Wiki — the principles, with examples

Example 1: a good business-rules.md
# Business Rules
 
## Pricing Rules
 
### Discount Calculation
The order discounts are applied in (this matters; always apply in this order):
1. Coupon discount (off the full price)
2. Member discount (off the post-coupon price)
3. Bundle discount (off the post-member price)
4. The final price must not fall below the cost price
 
Example:
- Full price: 1,000 baht
- Coupon 10%: 1,000 × 0.9 = 900
- Member Gold 5%: 900 × 0.95 = 855
- Bundle 3%: 855 × 0.97 = 829.35
- Cost price: 800 → OK (829 > 800)
 
### Free Shipping Rules
- Free shipping: order total >= 500 baht (after discount)
- Exception: products in the "Heavy" category never ship free
- Exception: upcountry delivery requires an order total >= 800 baht
 
## Order Status Rules
| Status | Can change to | Cannot change to |
|--------|--------------|----------------|
| pending | confirmed, cancelled | shipped, completed |
| confirmed | shipped, cancelled | completed |
| shipped | completed | pending, confirmed, cancelled |
| completed | (nothing) | every status |
| cancelled | (nothing) | every status |
Example 2: a good error-codes.md
# API Error Codes Reference
 
## Format
Every error response has this shape:
{ "error": { "code": "E001", "message": "...", "details": {} } }
 
## Error Codes
 
### Authentication (E1xx)
| Code | HTTP | Meaning | What the user sees | Action |
|------|------|---------|------------|--------|
| E101 | 401 | Token expired | "Please log in again" | Redirect to login |
| E102 | 401 | Invalid token | "Session error" | Clear token + login |
| E103 | 403 | Not permitted | "You do not have access" | Show the 403 page |
 
### Validation (E2xx)
| Code | HTTP | Meaning | What the user sees | Action |
|------|------|---------|------------|--------|
| E201 | 400 | Bad email format | "That email format is not valid" | Highlight field |
| E202 | 400 | Password too short | "Password must be at least 8 characters" | Highlight field |
| E203 | 409 | Duplicate email | "An account already exists for this email" | Show a login link |
 
## Things to watch
- E500 must never expose the internal error message to the user
- Every E5xx must be logged in Sentry along with the request ID
- E101 and E102 must always clear the cookie as well

Having Claude build and maintain the AI Wiki

You do not have to write the whole Wiki yourself. Claude can build it and keep it current:

# Build the Wiki from the codebase
> Analyse the project and build an AI Wiki
  Start from these files:
  @src/server/actions/ @src/lib/ @prisma/schema.prisma
 
  Produce:
  1. docs/api/endpoints.md from all the route handlers
  2. docs/database/schema.md from the Prisma schema
  3. docs/domain/business-rules.md from the validation logic
 
# Update the Wiki after implementing a feature
> The Notification system is finished
  Update the related docs:
  - add the Notification endpoints to @docs/api/endpoints.md
  - add the notification table to @docs/database/schema.md
  - add the notification rules to @docs/domain/business-rules.md
 
# Build a runbook from experience
> We just fixed a dead Redis connection
  Create a runbook at @docs/runbooks/common-issues.md
  describing the symptoms, the root cause and the fix
  so it goes faster next time

RAG (retrieval-augmented generation) for Claude Code

RAG is the technique of letting Claude search a large knowledge base before answering, instead of loading everything into context. It fits large codebases, large document sets, and information that changes often.

When is RAG worth it?

Use RAG whenYou do not need RAG when
The codebase is over 100 filesThe project is small, under 50 files
There is a lot of documentationThere is little documentation and it fits in CLAUDE.md
The information changes often, such as API docsThe information is static and rarely changes
You need to search across several repositoriesYou work in a single repo
Context fills up often because of large filesThere is still plenty of context left
A large team all needing the same knowledgeA solo developer, where memorising it is easy

The easiest approach adds no infrastructure at all. Use the MCP Filesystem server you already have, plus Claude’s built-in search:

# set up MCP Filesystem to cover all the docs
// .mcp.json at the project root (see local/project/user scopes in Chapter 2)
{
  "mcpServers": {
    "docs": {
      "command": "npx",
      "args": [
        "-y", "@modelcontextprotocol/server-filesystem",
        "/Users/john/Projects/myapp/docs",
        "/Users/john/Projects/myapp/src"
      ]
    }
  }
}
 
# after that Claude can search on its own
> Search all of docs for anywhere that mentions
  "rate limiting", then summarise it for me
 
> Find every function related to payment in src/
  and list what each of them does

Mid-level RAG: LlamaIndex plus Claude

LlamaIndex is the most widely used RAG framework. It takes about 30 minutes to set up and supports Claude directly.

The setup steps

1Install the dependencies
Requires Python 3.9+ and pip
pip install llama-index llama-index-llms-anthropic
pip install llama-index-embeddings-huggingface
# use HuggingFace embeddings (free) instead of OpenAI
2Build an index from the documents
The first indexing pass can take a while, depending on how large the docs are
# build_index.py
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.llms.anthropic import Anthropic
from llama_index.core import Settings
import os
 
os.environ["ANTHROPIC_API_KEY"] = "sk-ant-..."
 
# load the documents from the folder
documents = SimpleDirectoryReader(
    input_dir="./docs",
    recursive=True,           # read subfolders too
    required_exts=[".md", ".txt", ".py"]  # only the file types we want
).load_data()
 
# set Claude as the LLM
Settings.llm = Anthropic(model="claude-sonnet-5")
 
# build the vector index
index = VectorStoreIndex.from_documents(documents)
 
# save the index to disk (no need to rebuild every time)
index.storage_context.persist(persist_dir="./index_storage")
print(f"Indexed {len(documents)} documents")
3Query through RAG
Once the index is ready, searching is immediate
# query_rag.py
from llama_index.core import StorageContext, load_index_from_storage
from llama_index.llms.anthropic import Anthropic
from llama_index.core import Settings
 
# load the saved index
storage_context = StorageContext.from_defaults(
    persist_dir="./index_storage"
)
index = load_index_from_storage(storage_context)
 
Settings.llm = Anthropic(model="claude-sonnet-5")
 
# build the query engine
query_engine = index.as_query_engine(
    similarity_top_k=5,  # pull the 5 most relevant documents
    response_mode="tree_summarize"  # summarise across several docs
)
 
# ask a question
response = query_engine.query(
    "how to calculate the discount for a member on the Gold tier"
)
print(response)
print("\nSources:")
for node in response.source_nodes:
    print(f"  - {node.metadata['file_path']}  (score: {node.score:.2f})")
4Connect RAG to Claude Code through MCP
So Claude Code can call the RAG service directly
# rag_server.py — a simple HTTP server
from flask import Flask, request, jsonify
from llama_index.core import StorageContext, load_index_from_storage
from llama_index.llms.anthropic import Anthropic
from llama_index.core import Settings
 
app = Flask(__name__)
 
# load the index at startup
storage_context = StorageContext.from_defaults(persist_dir="./index_storage")
index = load_index_from_storage(storage_context)
Settings.llm = Anthropic(model="claude-sonnet-5")
query_engine = index.as_query_engine(similarity_top_k=5)
 
@app.route("/query", methods=["POST"])
def query():
    question = request.json["question"]
    response = query_engine.query(question)
    return jsonify({
        "answer": str(response),
        "sources": [n.metadata["file_path"] for n in response.source_nodes]
    })
 
if __name__ == "__main__":
    app.run(port=8765)
 
# run the server: python rag_server.py
 
# ask through bash from inside Claude Code:
> Run: curl -X POST http://localhost:8765/query
  -H "Content-Type: application/json"
  -d '{"question": "how payment retry is handled"}'
  then use the answer to implement the feature

Advanced RAG: codebase search with embeddings

For a large codebase, semantic search is far more powerful than grep. It searches by meaning, even when the function names differ.

# code_search.py — search code by meaning
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.core.node_parser import CodeSplitter
from llama_index.llms.anthropic import Anthropic
from llama_index.core import Settings
 
# load source code only
documents = SimpleDirectoryReader(
    input_dir="./src",
    recursive=True,
    required_exts=[".ts", ".tsx", ".js", ".py"]
).load_data()
 
# use CodeSplitter to split the code by function/class
splitter = CodeSplitter(
    language="typescript",
    chunk_lines=50,       # roughly 50 lines of code per chunk
    chunk_lines_overlap=5
)
 
nodes = splitter.get_nodes_from_documents(documents)
Settings.llm = Anthropic(model="claude-sonnet-5")
index = VectorStoreIndex(nodes)
 
# search the code by meaning
retriever = index.as_retriever(similarity_top_k=10)
 
# example: find the code related to authentication
results = retriever.retrieve("check if user is logged in")
for r in results:
    print(f"File: {r.metadata['file_path']}:{r.metadata['start_char_idx']}")
    print(f"Score: {r.score:.3f}")
    print(r.text[:200])
    print("---")

A RAG workflow for real vibe coding

This is the workflow that puts RAG to work in daily development:

# .claude/commands/rag-search.md
# RAG Search Command
 
# search the knowledge base before implementing
# Usage: /project:rag-search [topic]
 
Do the following:
1. Run: curl -s -X POST http://localhost:8765/query
   -H "Content-Type: application/json"
   -d {"question": "information about $ARGUMENTS"}
 
2. Read the result and its sources
 
3. Summarise what the knowledge base knows about this topic
   and which source files are involved
 
4. Propose how the new implementation should be done
   so it stays consistent with the existing pattern
 
# usage:
> /project:rag-search payment retry logic
> /project:rag-search user authentication
> /project:rag-search error handling pattern

Auto-updating the RAG index when the code changes

If the index goes stale, Claude gets old information. Set up an auto-update with a Git hook:

# .git/hooks/post-commit
#!/bin/bash
 
# find the files that just changed
CHANGED=$(git diff --name-only HEAD~1 HEAD)
 
# if any .md or source file changed
if echo "$CHANGED" | grep -qE ".(md|ts|tsx|py)$"; then
  echo "🔄 Updating RAG index..."
  python build_index.py --incremental
  echo "✅ RAG index updated"
fi
 
# make it executable
chmod +x .git/hooks/post-commit
 
# or use watch mode (run it in the background)
# watchmedo shell-command \
#   --patterns="*.md;*.ts;*.py" \
#   --command="python build_index.py --incremental" \
#   --recursive \
#   ./docs ./src

RAG for third-party documentation

Beyond internal docs, RAG also works on the documentation of the libraries you use, which lets Claude answer questions about a specific library version more accurately than its training data can.

# download the docs of the libraries you use
# for example: Next.js docs, Prisma docs
 
# scrape_docs.py
import requests
from bs4 import BeautifulSoup
import os
 
def scrape_docs(base_url, output_dir, max_pages=200):
    """download docs as markdown files"""
    os.makedirs(output_dir, exist_ok=True)
    # ... implementation
 
# docs worth indexing:
# - Prisma docs (they change very often)
# - Next.js App Router docs (version specific)
# - a partner's internal API docs
# - the company knowledge base
 
# then build the index together with the internal docs
python build_index.py --dirs ./docs ./external-docs
 
# in Claude Code
> Look at @docs/external/prisma/ and tell me
  what API changes Prisma 5.x made relative to 4.x
  that affect our code

RAG versus the alternatives: which to pick?

SolutionSetup difficultySuitsUpsideDownside
CLAUDE.md onlyEasySmall projects, under 50 filesNo setup at allSize-limited, read every time
MCP FilesystemEasyMid-size projects, 50-200 filesClaude searches on its ownNo semantic search
LlamaIndex RAGModerateLarge projects, over 200 filesSemantic searchThe index has to be maintained
Embedded vector DBHardEnterprise, large teamsFast, scalesComplex setup
External RAG serviceEasy to moderateTeams who do not want infrastructureManaged, nothing to maintainExtra cost, privacy concerns

💡 Recommended for beginners
Start with CLAUDE.md + MCP Filesystem
When the project grows large enough that context fills up often, add LlamaIndex
The best RAG is the one your team can actually maintain, not the most sophisticated one

In summary: the vibe coding checklist for daily use

Everything in this chapter, collected into one file you can use as a quick reference:

# .claude/commands/daily-checklist.md
# Daily Vibe Coding Checklist
 
## Before the session (5 minutes)
□ git checkout -b feature/[feature name]
□ Read CLAUDE.md: > read @CLAUDE.md and summarise what we are doing
□ RAG search if the project is large: /project:rag-search [topic]
□ Set the scope: tell Claude what is in scope and what is out
 
## During the build (every iteration)
□ Clear prompt: use the CLEAR framework
□ One small piece at a time: do not send the whole requirement at once
□ Review before running: read the main logic, look for red flags
□ Test everything: run the tests before committing
□ Commit often: commit whenever a sub-feature is done
 
## When you hit a bug
□ Under 2 rounds: try fixing it directly
□ At 2 rounds: hard stop → re-analyse
□ Still nothing: reset the context → new session
□ Still nothing: build an MRE → isolate the problem
□ Still nothing: change strategy → do not force it
 
## Before ending the session
□ /project:review — review all the code
□ Run every test; they must pass before you commit
□ /project:handoff — create the handoff document
□ Update the Wiki if any doc has changed
□ git push origin [branch]

Further glossary for this chapter (continued)

Loop bugClaude going round in circles on a bug without succeeding; the loop has to be stopped and reset
Hard stopHalting everything immediately and having Claude summarise what it knows before trying again
MRE (minimal reproducible example)The smallest code that still shows the bug, which isolates the problem
Race conditionA bug that appears when two processes run at once and share state
Intermittent bugA bug that occurs irregularly, hard to fix because it does not reproduce every time
AI WikiA knowledge base in Markdown, designed for Claude to read and search
RAG (retrieval-augmented generation)Having an AI search for information before answering, rather than memorising everything
Vector indexA data structure holding text embeddings so meaning can be searched
EmbeddingTurning text into numbers (a vector) that represent its semantic meaning
Semantic searchSearching by meaning rather than by keyword matching
LlamaIndexA Python framework for building RAG pipelines, easy to use with an LLM
Incremental indexingUpdating the index only for the files that changed, instead of rebuilding it all
ADR (architecture decision record)A document recording an important decision and why that option was chosen