TL;DR: Claude Code isn't just a coding assistant — it's an autonomous AI Agent that can carry a project from an empty directory to deployment-ready. This guide walks the full lifecycle: from claude /init to production, using the Explore → Plan → Execute workflow, CLAUDE.md as project context, vertical-slice architecture, and an 8-step feature loop that keeps human judgment on the decisions that matter. Because that agent runs with real access to your files, shell, and repository, the guide also covers the control boundaries you keep firm along the way.


Table of Contents


Key Takeaways

  • Explore → Plan → Execute: Claude Code's 3-phase workflow keeps you from implementing blindly — understand first, plan second, code third.
  • CLAUDE.md is your project context: Auto-generated with /init, it persists conventions, tech stack, and architecture across sessions — a convention aid, not an enforced control.
  • Vertical Slice Architecture: Build complete features end-to-end (UI → API → DB) instead of horizontal layers, which fits Claude Code's multi-file coordination.
  • 8-Step Feature Loop: Describe → Explore → Plan → Approve → Implement → Review → Test → Commit keeps architectural decisions explicit before code is written.
  • Auto-Accept with scoped access: For approved, low-risk tasks you can let Claude run hands-free — but hands-free means it acts with your file and shell privileges, so scope what it can touch.

Why Build Entire Projects with Claude Code

The gap between "AI helps me write functions" and "AI builds my entire project" is the gap between a copilot and an agent. Claude Code sits on the agent side: it operates as a terminal agent that reads, writes, executes, and iterates — without requiring a GUI or IDE integration.

Unlike editor-embedded tools, Claude Code directly operates on your file system, runs shell commands, manages Git branches, and maintains context through CLAUDE.md. That makes it well suited for end-to-end project creation, where dozens of files need coordinated changes across frontend, backend, and infrastructure layers.

The honest framing of the payoff is qualitative: a structured loop lets you delegate the mechanical work — scaffolding, boilerplate, first-pass tests — while keeping the decisions that carry risk (requirements, architecture, security) explicitly yours. It does not remove review; it moves your effort from typing toward directing and verifying. If you want numbers for your own team, measure them on your own work — there is a method for that at the end of this guide.

We'll build a task-management app as the running example: a React + TypeScript frontend, a Python FastAPI backend, and a PostgreSQL database, with tests on both sides.


Phase 1: Project Initialization & CLAUDE.md

Every Claude Code project begins with proper initialization. The /init command analyzes your repository (or empty directory) and generates a CLAUDE.md file that serves as persistent memory across future sessions.

Installing Claude Code

bash
# Native installer (recommended)
curl -fsSL https://claude.ai/install.sh | bash

# Or via npm
npm install -g @anthropic-ai/claude-code

# Verify installation
claude --version

The /init Command

Navigate to your project directory (or create a new one) and run:

bash
mkdir my-fullstack-app && cd my-fullstack-app
claude

# Inside the Claude Code session:
> /init

Claude Code scans your project structure (if files exist) or asks about your intended stack, then generates a CLAUDE.md file:

markdown
# CLAUDE.md - Project Configuration

## Project Overview
Full-stack task management app with React frontend and Python FastAPI backend.

## Tech Stack
- Frontend: React 18 + TypeScript + Vite + TailwindCSS
- Backend: Python 3.12 + FastAPI + SQLAlchemy + PostgreSQL
- Testing: Vitest (frontend), Pytest (backend)
- Deployment: Docker + docker-compose

## Common Commands
- `npm run dev` - Start frontend dev server
- `uvicorn main:app --reload` - Start backend
- `npm run test` - Frontend tests
- `pytest` - Backend tests
- `docker-compose up` - Full stack

## Architecture Decisions
- REST API with OpenAPI spec
- JWT authentication
- Repository pattern for data access
- Feature-based folder structure

## Coding Conventions
- Use absolute imports with @ prefix
- All API responses follow {data, error, meta} envelope
- Database models use snake_case, API DTOs use camelCase
- Every endpoint needs input validation with Pydantic

Why CLAUDE.md Matters

CLAUDE.md is read at the start of every session. In practice that means:

  1. You don't repeat project context each time.
  2. Claude Code tends to follow your conventions consistently.
  3. Architecture notes persist across days and weeks.
  4. A teammate's session inherits the same standards.

Keep one expectation clear: CLAUDE.md shapes what the agent produces; it does not enforce anything. A "never access the database directly in client components" line is a helpful nudge, not a guardrail — the guardrail lives in your architecture and code review.

flowchart TD A["Developer starts: claude"] --> B["Claude reads CLAUDE.md"] B --> C["Project context loaded"] C --> D{"New task described"} D --> E["Claude understands tech stack"] D --> F["Claude follows conventions"] D --> G["Claude knows common commands"] E --> H["Accurate implementation"] F --> H G --> H

Phase 2: Requirements Analysis with Plan Mode

Before writing a single line of code, use Claude Code's Plan Mode to analyze requirements and establish a shared understanding. Plan Mode (activated with Shift+Tab) instructs Claude to think and propose without executing.

The Explore → Plan → Execute Cycle

This is Claude Code's operating model for complex tasks:

flowchart LR A["1. EXPLORE"] --> B["2. PLAN"] B --> C["3. EXECUTE"] C --> D{"Review OK?"} D -->|Yes| E["Commit"] D -->|No| B A -.- A1["Read files, understand structure"] B -.- B1["Propose changes, get approval"] C -.- C1["Multi-file edits, run commands"]

Using Plan Mode in Practice

bash
# Inside Claude Code session, press Shift+Tab to enter Plan Mode
> [Plan Mode] Build a task management app with:
  - User authentication (signup/login/logout)
  - CRUD operations for tasks
  - Task categories and priority levels
  - Due dates with notifications
  - REST API with OpenAPI docs

Claude Code responds with a structured plan:

code
## Analysis

I'll build a full-stack task manager with the following architecture:

### Frontend (React + TypeScript)
- Pages: Login, Signup, Dashboard, TaskDetail
- Components: TaskList, TaskCard, CategoryFilter, PriorityBadge
- State: React Query for server state, Zustand for UI state

### Backend (FastAPI + PostgreSQL)
- Models: User, Task, Category
- Endpoints: /auth/*, /tasks/*, /categories/*
- Middleware: JWT auth, CORS, rate limiting

### Database Schema
- users (id, email, password_hash, created_at)
- categories (id, name, user_id, color)
- tasks (id, title, description, priority, due_date, category_id, user_id, status)

### Implementation Order (Vertical Slices)
1. Project scaffolding + Docker setup
2. Auth slice: User model → signup/login endpoints → Login UI
3. Tasks slice: Task model → CRUD endpoints → Dashboard UI
4. Categories slice: Category model → endpoints → filter UI
5. Polish: notifications, error handling, tests

Shall I proceed with this plan?

Why Plan-First Matters

Planning first is not about a fixed percentage of saved work — it's about where mistakes surface. Without a plan, Claude Code might pick conventions or patterns that conflict with your intent, and you only discover the mismatch after code exists, when it's expensive to unwind. With a plan, an architectural disagreement is a one-line correction before anything is written. That's the leverage: catching the wrong direction at the cheapest possible moment.

For deeper context on structured AI development methodologies, see our guide on Spec Coding, which formalizes this plan-first approach.


Phase 3: Architecture Design

After approving the high-level plan, the next phase is detailed architecture design. Claude Code is strong here because it can propose concrete file structures, API contracts, and data models at once.

Generating the Project Structure

bash
> Create the complete project structure following the approved plan. 
  Use feature-based organization for both frontend and backend.

Claude Code generates:

code
my-fullstack-app/
├── frontend/
│   ├── src/
│   │   ├── features/
│   │   │   ├── auth/
│   │   │   │   ├── components/
│   │   │   │   ├── hooks/
│   │   │   │   ├── api.ts
│   │   │   │   └── types.ts
│   │   │   ├── tasks/
│   │   │   │   ├── components/
│   │   │   │   ├── hooks/
│   │   │   │   ├── api.ts
│   │   │   │   └── types.ts
│   │   │   └── categories/
│   │   ├── shared/
│   │   │   ├── components/
│   │   │   ├── hooks/
│   │   │   └── utils/
│   │   ├── App.tsx
│   │   └── main.tsx
│   ├── package.json
│   └── vite.config.ts
├── backend/
│   ├── app/
│   │   ├── features/
│   │   │   ├── auth/
│   │   │   │   ├── router.py
│   │   │   │   ├── service.py
│   │   │   │   ├── models.py
│   │   │   │   └── schemas.py
│   │   │   ├── tasks/
│   │   │   └── categories/
│   │   ├── core/
│   │   │   ├── config.py
│   │   │   ├── database.py
│   │   │   └── security.py
│   │   └── main.py
│   ├── tests/
│   ├── requirements.txt
│   └── alembic/
├── docker-compose.yml
├── CLAUDE.md
└── README.md

API Contract Design

Claude Code can generate OpenAPI specs before implementation, ensuring frontend and backend (or separate Claude Code sessions) share a contract:

python
# backend/app/features/tasks/schemas.py
from pydantic import BaseModel, Field
from datetime import datetime
from enum import Enum
from typing import Optional

class Priority(str, Enum):
    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"
    URGENT = "urgent"

class TaskStatus(str, Enum):
    TODO = "todo"
    IN_PROGRESS = "in_progress"
    DONE = "done"

class TaskCreate(BaseModel):
    title: str = Field(..., min_length=1, max_length=200)
    description: Optional[str] = Field(None, max_length=2000)
    priority: Priority = Priority.MEDIUM
    due_date: Optional[datetime] = None
    category_id: Optional[int] = None

class TaskResponse(BaseModel):
    id: int
    title: str
    description: Optional[str]
    priority: Priority
    status: TaskStatus
    due_date: Optional[datetime]
    category_id: Optional[int]
    created_at: datetime
    updated_at: datetime

    class Config:
        from_attributes = True

The Human Review Checkpoint

This is a critical point in the workflow: architecture is where human judgment is irreplaceable. Claude Code proposes; you review for:

  • Security concerns (auth flow, data exposure, who can act on whose records)
  • Scalability implications (database design, caching strategy)
  • Team conventions (naming, folder structure)
  • Business logic accuracy

Once approved, Claude Code executes against a settled set of architectural "rails."


Phase 4: Step-by-Step Implementation

With architecture approved, implementation follows the Vertical Slice pattern: build complete features end-to-end rather than all models first, then all endpoints, then all UI.

Vertical Slice Workflow

flowchart TD A["Feature: User Authentication"] --> B["Database: users table + migration"] B --> C["Backend: auth endpoints"] C --> D["Frontend: login/signup pages"] D --> E["Integration: end-to-end test"] E --> F["Feature complete and testable"] G["Feature: Task CRUD"] --> H["Database: tasks table + migration"] H --> I["Backend: task endpoints"] I --> J["Frontend: dashboard + forms"] J --> K["Integration: end-to-end test"] K --> L["Feature complete and testable"]

Implementing the Auth Slice

bash
> Implement the authentication vertical slice: 
  1. User model and migration
  2. Signup and login endpoints with JWT
  3. Login and signup React pages
  Follow the schemas we designed. Use bcrypt for passwords.

Claude Code generates coordinated changes across multiple files:

Backend - User Model:

python
# backend/app/features/auth/models.py
from sqlalchemy import Column, Integer, String, DateTime
from sqlalchemy.sql import func
from app.core.database import Base

class User(Base):
    __tablename__ = "users"
    
    id = Column(Integer, primary_key=True, index=True)
    email = Column(String(255), unique=True, index=True, nullable=False)
    password_hash = Column(String(255), nullable=False)
    created_at = Column(DateTime(timezone=True), server_default=func.now())

Backend - Auth Endpoints:

python
# backend/app/features/auth/router.py
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.core.security import create_access_token, verify_password, hash_password
from .models import User
from .schemas import UserCreate, UserLogin, TokenResponse

router = APIRouter(prefix="/auth", tags=["auth"])

@router.post("/signup", response_model=TokenResponse, status_code=201)
def signup(user_data: UserCreate, db: Session = Depends(get_db)):
    existing = db.query(User).filter(User.email == user_data.email).first()
    if existing:
        raise HTTPException(status_code=409, detail="Email already registered")
    
    user = User(
        email=user_data.email,
        password_hash=hash_password(user_data.password)
    )
    db.add(user)
    db.commit()
    db.refresh(user)
    
    token = create_access_token({"sub": str(user.id)})
    return {"access_token": token, "token_type": "bearer"}

@router.post("/login", response_model=TokenResponse)
def login(credentials: UserLogin, db: Session = Depends(get_db)):
    user = db.query(User).filter(User.email == credentials.email).first()
    if not user or not verify_password(credentials.password, user.password_hash):
        raise HTTPException(status_code=401, detail="Invalid credentials")
    
    token = create_access_token({"sub": str(user.id)})
    return {"access_token": token, "token_type": "bearer"}

Frontend - Login Page (TypeScript):

typescript
// frontend/src/features/auth/components/LoginPage.tsx
import { useState } from 'react';
import { useMutation } from '@tanstack/react-query';
import { useNavigate } from 'react-router-dom';
import { authApi } from '../api';
import { useAuthStore } from '../../../shared/stores/authStore';

export function LoginPage() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const navigate = useNavigate();
  const setToken = useAuthStore((s) => s.setToken);

  const loginMutation = useMutation({
    mutationFn: authApi.login,
    onSuccess: (data) => {
      setToken(data.access_token);
      navigate('/dashboard');
    },
  });

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    loginMutation.mutate({ email, password });
  };

  return (
    <div className="min-h-screen flex items-center justify-center">
      <form onSubmit={handleSubmit} className="w-full max-w-md space-y-4 p-8">
        <h1 className="text-2xl font-bold text-center">Log In</h1>
        
        {loginMutation.isError && (
          <div className="bg-red-50 text-red-600 p-3 rounded">
            Invalid email or password
          </div>
        )}
        
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          placeholder="Email"
          className="w-full p-3 border rounded"
          required
        />
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          placeholder="Password"
          className="w-full p-3 border rounded"
          required
        />
        <button
          type="submit"
          disabled={loginMutation.isPending}
          className="w-full p-3 bg-blue-600 text-white rounded hover:bg-blue-700"
        >
          {loginMutation.isPending ? 'Logging in...' : 'Log In'}
        </button>
      </form>
    </div>
  );
}

Note that this slice authenticates users but does not, on its own, authorize them for specific records. When you build the tasks slice, every endpoint that reads or mutates a task must check that the task belongs to the caller — authentication proves who is calling, authorization decides what they may touch. That check lives in your handlers, not in the login flow.

Auto-Accept Mode for Rapid Implementation

For high-confidence tasks (after architecture is approved), you can enable Auto-Accept so Claude Code executes multiple file changes without pausing for confirmation at each step:

bash
# In Claude Code settings or via flag
> /config auto-accept true

# Now Claude will create files, install packages, and run commands
# without asking for approval at each step

Auto-Accept is a real grant of authority, not just a convenience: hands-free means the agent is creating files, installing packages, and running shell commands with your privileges and no per-step checkpoint. Enable it for implementing an approved plan on low-stakes changes; disable it for anything exploratory, anything that touches migrations or production configuration, and anything you would not run blind yourself. Confirm the current flag name and behavior in Claude Code's docs, since these controls change between releases.

Using /agents for Specialized Sub-Tasks

Claude Code's /agents command spawns specialized sub-agents for parallel work:

bash
> /agents create frontend-agent "Implement the TaskList and TaskCard 
  components following our design system. Use the task API types from 
  features/tasks/types.ts."

> /agents create backend-agent "Implement the tasks CRUD endpoints 
  following the repository pattern. Include pagination and filtering."

This parallelizes development within a single session — one agent handles UI while another builds the API layer. Keep in mind that the role you write in the prompt shapes behavior but does not confine capability; if you want a reviewer or test agent to stay read-only, that has to come from its tool permissions, not just its description.


Phase 5: Testing & Debugging

Claude Code generates test suites based on the implementation it created. Because it has full context of the code, it can cover edge cases and error paths.

Generating Test Suites

bash
> Generate comprehensive tests for the auth feature:
  - Unit tests for password hashing and JWT creation
  - Integration tests for signup and login endpoints
  - Frontend component tests for LoginPage

Backend Tests (Python - Pytest):

python
# backend/tests/features/auth/test_auth_endpoints.py
import pytest
from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)

class TestSignup:
    def test_successful_signup(self, db_session):
        response = client.post("/auth/signup", json={
            "email": "[email protected]",
            "password": "SecurePass123!"
        })
        assert response.status_code == 201
        data = response.json()
        assert "access_token" in data
        assert data["token_type"] == "bearer"
    
    def test_duplicate_email_returns_409(self, db_session, existing_user):
        response = client.post("/auth/signup", json={
            "email": existing_user.email,
            "password": "AnotherPass456!"
        })
        assert response.status_code == 409
        assert "already registered" in response.json()["detail"]
    
    def test_weak_password_rejected(self, db_session):
        response = client.post("/auth/signup", json={
            "email": "[email protected]",
            "password": "123"
        })
        assert response.status_code == 422

class TestLogin:
    def test_successful_login(self, db_session, existing_user):
        response = client.post("/auth/login", json={
            "email": "[email protected]",
            "password": "ExistingPass123!"
        })
        assert response.status_code == 200
        assert "access_token" in response.json()

    def test_wrong_password_returns_401(self, db_session, existing_user):
        response = client.post("/auth/login", json={
            "email": "[email protected]",
            "password": "WrongPassword!"
        })
        assert response.status_code == 401
    
    def test_nonexistent_email_returns_401(self, db_session):
        response = client.post("/auth/login", json={
            "email": "[email protected]",
            "password": "SomePass123!"
        })
        assert response.status_code == 401

Frontend Tests (TypeScript - Vitest):

typescript
// frontend/src/features/auth/components/__tests__/LoginPage.test.tsx
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { MemoryRouter } from 'react-router-dom';
import { LoginPage } from '../LoginPage';
import { vi } from 'vitest';

const queryClient = new QueryClient({
  defaultOptions: { queries: { retry: false } }
});

const wrapper = ({ children }: { children: React.ReactNode }) => (
  <QueryClientProvider client={queryClient}>
    <MemoryRouter>{children}</MemoryRouter>
  </QueryClientProvider>
);

describe('LoginPage', () => {
  it('renders login form with email and password fields', () => {
    render(<LoginPage />, { wrapper });
    expect(screen.getByPlaceholderText('Email')).toBeInTheDocument();
    expect(screen.getByPlaceholderText('Password')).toBeInTheDocument();
    expect(screen.getByRole('button', { name: /log in/i })).toBeInTheDocument();
  });

  it('shows loading state during submission', async () => {
    render(<LoginPage />, { wrapper });
    fireEvent.change(screen.getByPlaceholderText('Email'), {
      target: { value: '[email protected]' }
    });
    fireEvent.change(screen.getByPlaceholderText('Password'), {
      target: { value: 'password123' }
    });
    fireEvent.click(screen.getByRole('button'));
    
    await waitFor(() => {
      expect(screen.getByText('Logging in...')).toBeInTheDocument();
    });
  });

  it('displays error message on failed login', async () => {
    // Mock failed API response
    vi.spyOn(global, 'fetch').mockRejectedValueOnce(new Error('401'));
    
    render(<LoginPage />, { wrapper });
    fireEvent.click(screen.getByRole('button'));
    
    await waitFor(() => {
      expect(screen.getByText(/invalid email or password/i)).toBeInTheDocument();
    });
  });
});

A generated suite is a strong starting point, but a passing suite proves only what the tests actually assert. An agent can write tests that pass while checking the wrong behavior — for example, asserting a 200 without asserting the response body, or never testing the unauthorized path. Read the assertions, especially for security-relevant paths, before you trust the green run.

The Debugging Workflow

When tests fail or bugs appear, Claude Code's debugging workflow leverages its full-codebase understanding:

bash
> The login endpoint returns 500 instead of 401 when the password is wrong.
  Here's the error: "AttributeError: 'NoneType' object has no attribute 'password_hash'"

Claude Code will:

  1. Read the relevant endpoint and model files
  2. Identify that the query might return None before checking password
  3. Fix the conditional logic
  4. Run the tests again to confirm the fix

This iterative debug-fix-verify loop is where Claude Code's terminal-native design shows its value — it runs the test suite directly and continues until green.


Phase 6: Code Review & Refactoring

After features are implemented and tests pass, use Claude Code as a first-pass reviewer. Its full-context awareness catches issues that file-level linters miss.

Running a Code Review

bash
> Review the entire tasks feature for:
  - Security vulnerabilities
  - Performance issues
  - Code consistency with our conventions
  - Missing error handling

Claude Code produces a structured review:

code
## Code Review: Tasks Feature

### Security Issues (High Priority)
1. **SQL Injection Risk** in task search:
   - File: backend/app/features/tasks/service.py:45
   - Issue: f-string used in query filter
   - Fix: Use SQLAlchemy's parameterized queries

2. **Missing authorization check**:
   - File: backend/app/features/tasks/router.py:67
   - Issue: DELETE /tasks/{id} doesn't verify task ownership
   - Fix: Add user_id check before deletion

### Performance Issues (Medium Priority)
3. **N+1 query** in task listing:
   - File: backend/app/features/tasks/router.py:23
   - Issue: Category loaded separately for each task
   - Fix: Use joinedload() in the query

### Convention Violations (Low Priority)
4. TaskCard component uses inline styles instead of Tailwind classes
5. API error responses don't follow {data, error, meta} envelope

Shall I fix these issues?

The "missing authorization check" finding is worth pausing on, because it is the boundary from Phase 4 showing up in review: the endpoint authenticated the caller but never checked that the task belonged to them. An agent review is genuinely useful for surfacing this class of issue — but treat it as a first pass that raises candidates, not a sign-off. For security-relevant changes, a human still decides. An automated review that finds nothing is not proof there is nothing to find. For how developer roles shift around this kind of oversight, see the evolution of developer roles in the AI era.

Refactoring with Confidence

After review, let Claude Code refactor:

bash
> Fix all security issues identified in the review. 
  Maintain existing test coverage—update tests if interfaces change.

Claude Code edits multiple files, updating both implementation and tests to keep them consistent. Re-read the diff — especially the authorization fix — before you accept it.


Phase 7: Deployment Preparation

The final phase turns your working project into a deployment-ready package. Claude Code handles Docker configuration, environment setup, and CI pipeline creation.

Docker Configuration

bash
> Create production Docker setup with:
  - Multi-stage builds for minimal image size
  - Docker Compose for local development
  - Environment variable configuration
  - Health check endpoints

Generated docker-compose.yml:

yaml
# docker-compose.yml
version: "3.9"

services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: ${DB_USER:-taskapp}
      POSTGRES_PASSWORD: ${DB_PASSWORD:-localdev}
      POSTGRES_DB: ${DB_NAME:-taskapp}
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U taskapp"]
      interval: 5s
      timeout: 3s
      retries: 5

  backend:
    build:
      context: ./backend
      target: development
    ports:
      - "8000:8000"
    environment:
      DATABASE_URL: postgresql://${DB_USER:-taskapp}:${DB_PASSWORD:-localdev}@db:5432/${DB_NAME:-taskapp}
      JWT_SECRET: ${JWT_SECRET:-dev-secret-change-in-prod}
    depends_on:
      db:
        condition: service_healthy
    volumes:
      - ./backend:/app

  frontend:
    build:
      context: ./frontend
      target: development
    ports:
      - "5173:5173"
    environment:
      VITE_API_URL: http://localhost:8000
    volumes:
      - ./frontend:/app
      - /app/node_modules

volumes:
  pgdata:

The :- fallbacks here (localdev, dev-secret-change-in-prod) are convenient for spinning up a local stack, and that is all they are for. They are insecure by design and must never reach a deployed environment: in staging or production, JWT_SECRET and DB_PASSWORD come from real secrets management with no default. This is exactly the kind of thing to verify by reading the config, not by trusting that the agent "knows" it is only for dev.

CI Pipeline Generation

bash
> Create a GitHub Actions CI pipeline that:
  - Runs backend tests with a PostgreSQL service container
  - Runs frontend tests  
  - Builds Docker images
  - Only deploys on main branch

Claude Code generates a complete .github/workflows/ci.yml configured for your stack. If that workflow uses an agent action or holds deployment credentials, scope its permissions to the minimum it needs — a CI job that can deploy is a high-value target.


Keeping Control When the Agent Has Real Access

Building a whole project with an agent means that agent is editing files, running your shell, and — with Auto-Accept — doing so without a checkpoint at each step. A few boundaries do not move, however capable the tooling becomes:

  • Grant the least access that does the job. Scope Auto-Accept and any sub-agent's tool permissions to the task. A reviewer or test agent should be read-only in its permissions, not just its prompt.
  • Authentication is identity, not authorization. Generated auth code proves who is calling; it does not prove the caller may act on a specific record. Object- and tenant-level checks live in your handlers and get enforced on every request — the Phase 6 review exists precisely to catch when they are missing.
  • CLAUDE.md is a convention aid, not a control. It changes what the agent tends to do; it enforces nothing. Real guardrails are your architecture, permissions, and review.
  • Tests and green CI prove only what they cover. Read the assertions before trusting them, especially on unauthorized and error paths.
  • Insecure defaults are for local dev only. Fallback secrets and passwords must be replaced by real secrets management before anything is deployed.
  • Treat untrusted input as untrusted. If the agent acts on issue text, PR comments, or content fetched via MCP, that content is data, not authorized instructions.

None of this slows the workflow much — it is mostly a matter of where you keep control. Delegate the mechanical work; keep the decisions.


The Complete Workflow Visualized

Here's the full 8-step feature workflow that ties the phases together:

flowchart TD A["1. DESCRIBE: State the feature requirement"] --> B["2. EXPLORE: Claude reads codebase"] B --> C["3. PLAN: Claude proposes approach"] C --> D{"4. APPROVE: Human reviews plan"} D -->|Rejected| C D -->|Approved| E["5. IMPLEMENT: Claude writes code"] E --> F["6. REVIEW: Human + Claude inspect output"] F -->|Issues found| E F -->|Looks good| G["7. TEST: Claude generates and runs tests"] G -->|Tests fail| E G -->|Tests pass| H["8. COMMIT: Claude stages and commits"]

Session Management Best Practices

Scenario Recommended Approach
New feature from scratch Start fresh session, Plan Mode first
Bug fix with stack trace Paste error, let Claude explore
Refactoring existing code Plan Mode to scope changes, then execute
Adding tests to existing code Point Claude to the files; Auto-Accept only if the change is low-risk
Multi-day project Rely on CLAUDE.md for context persistence

How This Compares, and How to Measure It Yourself

Claude Code vs Other Agent Tools for Full Projects

The tools take different shapes rather than sitting on a single ranking. Treat this as a map, and verify current specifics at each vendor, since features change quickly:

Capability Claude Code Cursor Agent GitHub Copilot
Terminal-native execution Native IDE-bound IDE-bound
Multi-file coordinated edits Broad Scoped Narrower
Run shell commands Direct Via terminal panel Limited
Persistent project memory CLAUDE.md Rules files Limited
Plan-before-execute mode Shift+Tab Manual prompting Limited
Sub-agents /agents Limited Limited
Hands-free execution Auto-Accept Auto-run mode Limited
CI/CD integration GitHub Action Limited GitHub-native
Best fit Full projects, terminal + CI/CD Interactive IDE coding Inline completion, GitHub workflows

For a broader comparison of AI coding tools in 2026, see our AI Coding Tools Comparison.

Measure the Payoff on Your Own Work

You'll see plenty of efficiency percentages quoted for agentic coding. Most are measured on someone else's codebase and tasks, so they don't predict your results. If the payoff matters to your decision, measure it on work you actually do:

  • Pick a handful of representative features or bug fixes.
  • Do some with the structured loop above and some the way you work today.
  • Track a few concrete numbers you care about — time to a working feature, number of review round-trips, escaped defects.
  • Compare on your tasks, not on a benchmark.

That's a far stronger basis for a team decision than any figure from a blog post, this one included.

Understanding the Agent Paradigm

The shift from "AI as autocomplete" to "AI as autonomous agent" changes how software gets built. Claude Code embodies this as a true AI Agent: it doesn't just predict the next line, it reasons about the whole system. A few underlying concepts make that possible:

  • LLM reasoning handles multi-step planning.
  • The Context Window determines how much of the project it can hold at once.
  • Prompt Engineering is why CLAUDE.md works — it's a persistent, project-tuned set of instructions.

FAQ

Can Claude Code really build an entire project from scratch?

It can scaffold, implement, test, and prepare deployment for a full-stack project — multi-file edits, dependency management, database schemas, and test generation, all from your terminal. What stays with you are the decisions that carry risk: requirements, architecture, security review, and what to accept. Clear requirements plus Plan Mode for architecture keep the generated code coherent.

What is CLAUDE.md and why do I need it?

CLAUDE.md is Claude Code's project memory file, read at the start of every session. It encodes tech-stack decisions, conventions, common commands, and architecture notes so you don't repeat context. Use /init to auto-generate one and customize it. It shapes output but enforces nothing — treat it as a convention aid, not a guardrail.

How does Plan Mode differ from regular execution?

Plan Mode (Shift+Tab) tells Claude to analyze and propose without executing. It follows Explore → Plan → Execute: understand the codebase, outline steps for your approval, and only execute after you confirm. It keeps architectural decisions explicit before code exists, which is where a wrong turn is cheapest to correct.

What's a good workflow for building features with Claude Code?

An 8-step loop: Describe → Explore → Plan → Approve → Implement → Review → Test → Commit. The point isn't a magic speedup — it's that architectural mismatches surface at the plan stage instead of after the code exists, and you review the output rather than accepting it blindly.

How does Claude Code handle testing and debugging?

It generates unit, integration, and e2e tests from your implementation. For debugging, describe the error or paste a stack trace — it reads relevant files, proposes a root cause, and iterates until tests pass. Because a passing suite only proves what it asserts, read the assertions, particularly for unauthorized and error paths.


Concepts

  • AI Agent — autonomous systems that perceive, reason, and act
  • LLM — the models powering code generation
  • Context Window — how much code the model can hold at once
  • Prompt Engineering — why CLAUDE.md works as a persistent instruction set
  • MCP — Model Context Protocol for tool integration