Skip to main content
Tawan
Tawan
00:00
Melbourne

Write Code Faster and Effectively with Claude Code (Without the Hallucinations)

Jan 15, 2025

šŸŽÆ

TL;DR: Learn to use Claude Code as a reasoning partner (not just a generator) through proper prompting, MCP servers for context, and verification strategies to eliminate hallucinations. Ship production code 10x faster.

Table of Contents

  1. Introduction: Coding in the AI Era
  2. Meet Claude Code: The AI Developer's Edge
  3. Setting Up Claude Code
  4. Writing Prompts That Code
  5. From Snippets to Systems
  6. Debugging and Testing
  7. Preventing Hallucinations
  8. Managing Context with MCP
  9. The Future of Human + AI Coding

1. Introduction: Coding in the AI Era

We've moved from manual coding to conversational development — expressing intent in natural language, letting AI handle implementation.

The productivity paradox: Generating 100 lines in minutes → spending hours debugging hallucinated functions, non-existent APIs, or "looks right" logic that fails silently.

šŸ’”

Claude Code is a reasoning engine, not just a generator. It understands context, suggests architectural patterns, and explains decisions before writing code. Your job: guide it properly.


2. Meet Claude Code: The AI Developer's Edge

The key difference: Agentic workflow

Unlike direct AI code generation, Claude Code works as an autonomous agent:

Real example - Prompt transformation:

šŸ”„

Your simple query → Claude Code's comprehensive prompt

Watch how a 5-word request becomes a production-ready instruction.

You type:

Add user authentication

Claude Code analyzes your codebase and generates:

Implement user authentication system for this Next.js 15 application.

CONTEXT DISCOVERED:
- Framework: Next.js 15.3.4 with App Router
- Database: No existing DB detected - need to set up
- Current auth: None found
- File structure: /app directory with server components
- Styling: Tailwind CSS 4.x
- TypeScript: Enabled with strict null checks

IMPLEMENTATION PLAN:

Step 1: Database Setup
- Install and configure Drizzle ORM with PostgreSQL
- Create users table with schema:
  - id (uuid, primary key)
  - email (unique, indexed)
  - password_hash (text)
  - created_at, updated_at (timestamps)
- Set up database migrations in drizzle/

Step 2: Authentication Logic
- Create app/lib/auth/ directory
- Implement password hashing with bcrypt (12 rounds)
- Build session management with HTTP-only cookies
- Add rate limiting: 5 attempts per 15 minutes per IP
- Implement middleware for protected routes

Step 3: Server Actions
- Create app/lib/auth/actions.ts:
  - signUp(email, password)
  - signIn(email, password)
  - signOut()
  - getCurrentUser()
- Add proper error handling following Next.js 15 patterns
- Validate inputs with Zod schema validation

Step 4: UI Components
- Create app/components/auth/:
  - LoginForm.tsx (client component)
  - SignUpForm.tsx (client component)
- Add /login and /signup routes
- Implement form validation and loading states
- Add error messages and success feedback

Step 5: Protected Routes
- Create middleware.ts for route protection
- Add auth check to protected pages
- Implement redirect logic (unauthorized → /login)

SECURITY REQUIREMENTS:
- CSRF protection via same-site cookies
- SQL injection prevention (Drizzle parameterized queries)
- XSS protection (React auto-escaping)
- Bcrypt for password hashing (cost factor: 12)
- Rate limiting on auth endpoints
- No passwords in logs or error messages

CONSTRAINTS:
- Maximum 400 lines per file
- Use Server Actions (not API routes)
- Follow existing error handling pattern in app/lib/errors.ts (if exists)
- TypeScript strict mode compliance
- All components must be accessible (ARIA labels)

VERIFICATION STEPS:
1. Create test user and verify signup works
2. Test login with correct/incorrect credentials
3. Verify session persistence across page refreshes
4. Test rate limiting (6th attempt should fail)
5. Ensure protected routes redirect when not authenticated
6. Check middleware runs on correct routes

Please implement this step by step, showing the code for each file.
Start with database schema and migration.

What happened:

  1. šŸ” Analyzed your codebase (Next.js 15, no DB, Tailwind)
  2. šŸŽÆ Planned 5-step implementation
  3. šŸ”’ Added security requirements you didn't mention
  4. šŸ“ Applied your constraints (400 lines/file from CLAUDE.md)
  5. āœ… Included verification checklist

The difference: From "add auth" → production-ready specification with security, testing, and your project's patterns.


Traditional AI vs Claude Code (Agentic):

ApproachTraditional AI CodingClaude Code (Agentic)
User inputDirect prompt to AIAnalyzed and enriched by agent
PlanningUser must planAgent creates implementation plan
ContextUser provides contextAgent reads codebase automatically
ExecutionSingle generationMulti-step orchestrated workflow
VerificationUser validatesAgent checks patterns and consistency

You work with an orchestrator, not just a generator.


3. Setting Up Claude Code for a Lean Workflow

Minimal stack (no Cursor, no heavy IDEs):

# 1. Install
npm install -g @anthropics/claude-code

# 2. Initialize
cd your-project && claude-code init

# 3. Create CLAUDE.md (project context)
cat > CLAUDE.md << 'EOF'
# Architecture
- Next.js 15 App Router
- TypeScript strict mode
- Max 400 lines per file
EOF

# 4. Start coding
claude-code "Implement user auth with JWT"
⚔

Pro tip: Create a CLAUDE.md file in your repo. This gives Claude instant context about your architecture, coding standards, and constraints.


4. Writing Prompts That Code

The KEY difference between section 2 and this section:

  • Section 2: Shows how Claude Code interprets vague inputs ("Add user authentication")
  • Section 4: Shows how to write great prompts that get focused, useful outputs

The anatomy: Intent + Constraints + Context

The Problem with Section 2's Example

In section 2, we showed: "Add user authentication" → Claude generates 80 lines of comprehensive planning.

The issue: That's too much for many scenarios! You might just want a simple login form, not a full auth system with database setup.

How to Control the Output

šŸ’”

The secret: Be specific about what you want, and Claude Code will generate exactly that — not everything it could do.

Example 1: Focused Feature Request

Your Prompt:

Add a simple login form to /login route:

CONTEXT:
- Next.js 15 App Router
- Auth handled by existing auth.ts (don't modify)
- Just need UI component

REQUIREMENTS:
- Email + password fields
- "Remember me" checkbox
- Call existing signIn() function
- Show loading state during submission

CONSTRAINTS:
- Client component
- Use existing Tailwind classes
- Max 100 lines

Claude Code generates:

// Focused, 60-line implementation

"use client";

import { useState } from "react";
import { signIn } from "@/app/lib/auth/actions";

export function LoginForm() {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState("");

  async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault();
    setIsLoading(true);
    setError("");

    const formData = new FormData(e.currentTarget);

    try {
      await signIn(formData);
    } catch (err) {
      setError("Invalid credentials");
    } finally {
      setIsLoading(false);
    }
  }

  return (
    <form onSubmit={handleSubmit} className="space-y-4 max-w-md mx-auto">
      <div>
        <label htmlFor="email" className="block text-sm font-medium">
          Email
        </label>
        <input
          id="email"
          name="email"
          type="email"
          required
          className="mt-1 block w-full rounded-md border px-3 py-2"
        />
      </div>

      <div>
        <label htmlFor="password" className="block text-sm font-medium">
          Password
        </label>
        <input
          id="password"
          name="password"
          type="password"
          required
          className="mt-1 block w-full rounded-md border px-3 py-2"
        />
      </div>

      <div className="flex items-center">
        <input
          id="remember"
          name="remember"
          type="checkbox"
          className="h-4 w-4 rounded border-gray-300"
        />
        <label htmlFor="remember" className="ml-2 block text-sm">
          Remember me
        </label>
      </div>

      {error && (
        <p className="text-sm text-red-600">{error}</p>
      )}

      <button
        type="submit"
        disabled={isLoading}
        className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:opacity-50"
      >
        {isLoading ? "Signing in..." : "Sign in"}
      </button>
    </form>
  );
}

Notice: 60 lines, exactly what was asked — not the 80-line spec from section 2!

Example 2: Architectural Guidance

Your Prompt:

Planning a real-time notification system.

CURRENT STACK:
- Next.js 15 + PostgreSQL
- 10k DAU, expect 100k in 6 months

DON'T CODE YET. Just explain:
1. Architecture options (polling vs WebSocket vs SSE)
2. Scaling considerations
3. Which approach you'd recommend and why

Claude Code responds:

Architecture Analysis for Real-Time Notifications:

OPTION 1: Server-Sent Events (SSE)
Pros:
- Native browser support, simple implementation
- Auto-reconnect built-in
- Works over HTTP (no WebSocket infrastructure)

Cons:
- One-way communication only
- Connection limit per domain (6 in most browsers)

OPTION 2: WebSockets
Pros:
- Bi-directional, lowest latency
- Best for chat-like features

Cons:
- Requires separate WebSocket server/load balancer
- More complex deployment (sticky sessions)
- Harder to scale horizontally

OPTION 3: Polling
Pros:
- Simplest implementation
- Works everywhere, no connection limits

Cons:
- Higher latency (1-5 second delay)
- More server load (constant requests)

RECOMMENDATION FOR YOUR USE CASE:

Start with SSE for 10k → 100k users:

1. CURRENT (10k DAU):
   - SSE handles this easily
   - Each user = 1 persistent connection
   - On Vercel: Use Route Handlers with streaming

2. SCALING TO 100K:
   - Move to dedicated server (Railway, Fly.io)
   - Use Redis Pub/Sub for multi-instance support
   - Estimated cost: ~$50/month

3. MIGRATION PATH IF NEEDED:
   - If you hit 500k+ DAU, consider WebSockets
   - Abstract notification client behind interface
   - Swap SSE → WebSocket without changing app code

NEXT STEPS:
1. Prototype SSE endpoint: GET /api/notifications/stream
2. Test with 100 concurrent connections locally
3. Measure server resource usage

Want me to implement the SSE prototype?

Notice: No code! Just architectural guidance because that's what was requested.

The Pattern: Prompt = Output Control

šŸŽÆ

Key insight: Section 2's comprehensive output happens when Claude Code has to guess your intent. Section 4's focused outputs happen when you tell Claude exactly what you want.

Quick Prompt Templates

For small tasks:

Add [FEATURE] to [FILE/ROUTE]:
- Use existing [PATTERN/FUNCTION]
- Max [N] lines
- [SPECIFIC REQUIREMENT]

For planning:

Planning [FEATURE]. DON'T CODE.
Explain:
1. [QUESTION 1]
2. [QUESTION 2]
Recommend approach.

For refactoring:

Refactor [FILE] to [GOAL]:
- Keep same API
- Improve [METRIC]
- Max [N] lines per function
Show before/after.
🧠

Pro tip: Add "Explain approach first, then implement" to force reasoning mode — reduces hallucinations by 80%.


5. From Snippets to Systems

Design-first workflow (3-step process):

Step 1: Planning

Build commenting system. Before coding:
1. Suggest DB schema
2. Identify race conditions
3. Spam prevention approach
4. Nested comments strategy

Step 2: Refinement

Add to schema:
- Soft deletes (deleted_at)
- Edit support (edited_at)
- Upvote/downvote counts
Explain migration.

Step 3: Implementation

Implement as modules:
- app/lib/comments/db.ts (DB layer)
- app/lib/comments/actions.ts (Server actions)
- app/components/comments/thread.tsx (UI)
- app/lib/comments/types.ts (types)

Max 400 lines/file. Use existing error patterns.

6. Debugging and Testing with Claude

Debug template:

[Paste error/function]

CONTEXT: Next.js 15, PostgreSQL, intermittent under load
EXPECTED: X
ACTUAL: Y

Explain:
1. Root cause
2. Why intermittent
3. Fix + prevention

Test generation:

Write Vitest tests for [function]:
- Happy path + errors
- Mock DB calls
- Edge cases I missed
šŸ”

Pre-flight testing: Ask Claude to predict which tests will fail before running them. Catches logic errors early.


7. Preventing Hallucinations

Why They Happen

🚩 Red Flags

  • Importing non-installed packages
  • Using non-existent API methods
  • "Too good to be true" magic
  • Missing error handling
  • Hard-coded values

āœ… Verification Workflow

Constraint-based prompting:

MUST USE: bcrypt, Drizzle ORM, Server Actions
MUST NOT: Install packages, use deprecated patterns
ASK FIRST: If need something not listed

Self-review prompt:

Review your code:
1. All imports installed?
2. Methods exist in lib versions?
3. What assumptions made?
4. Potential bugs?

Incremental verification:

3 steps (verify each before next):
1. DB schema → verify against DB
2. Server actions → test with Postman
3. UI → test in browser

8. Managing Context and Memory

The Problem

The Solution: MCP Servers

MCP = Model Context Protocol — gives Claude real-time access to:

MCP Example 1: PostgreSQL

{
  "postgres": {
    "command": "npx",
    "args": ["-y", "@modelcontextprotocol/server-postgres",
             "postgresql://readonly@localhost/mydb"]
  }
}

Prompt:

Build analytics dashboard. Query DB for:
1. Users table schema
2. Related tables (sessions, events)
3. Suggest queries for DAU, conversion funnel

Then implement based on REAL schema.

Result: āœ… Uses actual columns, not hallucinated ones

MCP Example 2: GitHub

{
  "github": {
    "command": "npx",
    "args": ["-y", "@modelcontextprotocol/server-github"],
    "env": {"GITHUB_TOKEN": "${GITHUB_TOKEN}"}
  }
}

Prompt:

Adding blog series feature.
Fetch from GitHub:
1. Current blog implementation
2. MDX frontmatter parsing
3. Feed generation
Then extend pattern.

Result: āœ… Follows YOUR codebase patterns

MCP Example 3: Context7

Context7: https://context7.com

{
  "context7": {
    "command": "npx",
    "args": ["-y", "@context7/mcp-server"]
  }
}

Prompt:

Upgrading Next.js 14 → 15.
Using Context7:
1. Breaking changes in App Router
2. New features (React 19)
3. Migration plan from CURRENT docs

Why Context7:

  • āœ… Always latest docs (not outdated training data)
  • āœ… Curated quality (React, Next.js, Stripe, etc.)
  • āœ… No hallucinated APIs

MCP Example 4: Memory Bank (Knowledge Graph)

{
  "memory": {
    "command": "npx",
    "args": ["-y", "@modelcontextprotocol/server-memory"]
  }
}

Store project knowledge:

Save to memory bank:

Auth System:
- JWT + refresh tokens
- bcrypt (12 rounds)
- Rate limit: 5/15min
- Owner: @backend-team

Caching Decision:
- Redis chosen over in-memory
- Why: Multi-instance deployment
- Date: 2024-11-15

Query later:

I'm new. Explain:
1. How auth works
2. Why Redis for caching
3. Who owns payments

Why this matters: Claude retrieves YOUR decisions, not generic advice.

šŸ“

Store "why" not "what": Code shows implementation, memory bank stores architectural decisions, tradeoffs, and ownership.

MCP Best Practices

Security: Always use read-only DB users

"postgresql://readonly_user@localhost/mydb"

Efficiency: Request specific context, not everything

āŒ "Read all 500 files"
āœ… "Show auth implementation + users schema"

The MCP difference:

  • Without: "Here's generic auth advice"
  • With: "Your users table has email + password_hash columns..."

9. The Future of Human + AI Coding

Hybrid Engineering

Skills matrix:

Traditional (Still Critical)New (Increasingly Important)
Architecture thinkingPrompt engineering
Code reviewAI output verification
Security awarenessContext management (MCP)
Performance tuningHuman-AI orchestration

The Magic of Collaboration

Example workflow:

  1. You: "Checkout is slow" (business context)
  2. Claude: Analyzes → "N+1 queries + no caching" (technical analysis)
  3. You: "Fix N+1 first" (strategic decision)
  4. Claude: Implements optimized queries (execution)
  5. You: Review → ship (quality gate)

Result: 30 minutes instead of 3 hours

Staying Relevant

Your competitive advantage isn't speed — it's:

  • āœ… Verification skill
  • āœ… Prompt quality
  • āœ… Architectural vision
  • āœ… Context management

Developers who thrive:

  • Orchestrate AI as force multiplier
  • Verify rigorously
  • Focus on problems AI can't solve

Want More? Get the Complete Guide with Bonus Content

You've learned the fundamentals of Claude Code, but there's more! The complete PDF guide includes an exclusive bonus section:

šŸŽ Bonus: Advanced Workflows & PM System

Master advanced techniques and run projects like a PM with AI as your dev team:

Part 1: Advanced Workflow Hacks

  • Reusable prompt templates for common tasks
  • Automation scripts for commit messages, PRs, and CI failures
  • Refactoring workflows with SOLID principles
  • Advanced prompting with "think" and "ultrathink" modes
  • Context files with @ mentions for consistent code generation

Part 2: Agentic Project Management

  • Complete command system setup (.claude/commands/)
  • Feature, bug fix, and refactor templates
  • Real-world PM workflow examples (E-commerce checkout optimization)
  • Daily PM → AI agent workflows
  • Productivity tips and team knowledge building

Why Download the PDF?

  • āœ… Exclusive bonus content not available on the website
  • āœ… Offline access - read anywhere, anytime
  • āœ… Complete guide with all sections in one place
  • āœ… Print-friendly format for team sharing
  • āœ… Advanced techniques for "think"/"ultrathink" modes and context files

Have questions or success stories using Claude Code? Reach out at hello@tawan.org or share on Twitter.