TL;DR: Claude Code 不只是编程助手——它是一个能把项目从空目录带到部署就绪的自主 AI Agent。本文走完完整生命周期:从 claude /init 到生产,使用 Explore → Plan → Execute 工作流、把 CLAUDE.md 作为项目上下文、Vertical Slice 架构,以及一个让人类判断留在关键决策上的 8 步 Feature 循环。因为这个 Agent 是带着对你文件、Shell 和仓库的真实访问权限在运行的,本文也一并讲清你需要守住的控制边界。


目录


核心要点

  • Explore → Plan → Execute:Claude Code 的三阶段工作流,让你不再盲目动手——先理解,再规划,最后写代码。
  • CLAUDE.md 是你的项目上下文:用 /init 自动生成,跨会话保存约定、技术栈和架构——它是约定辅助,不是被强制执行的控制。
  • Vertical Slice 架构:端到端构建完整功能(UI → API → DB),而不是横向按层开发,这更契合 Claude Code 的多文件协同能力。
  • 8 步 Feature 循环:描述 → 探索 → 规划 → 批准 → 实现 → 审查 → 测试 → 提交,让架构决策在写代码之前就明确。
  • 带作用域访问的 Auto-Accept:对已批准的低风险任务可以让 Claude 免确认执行——但免确认意味着它是以你的文件和 Shell 权限在动作,所以要限定它能碰的范围。

为什么用 Claude Code 构建完整项目

"AI 帮我写函数"和"AI 构建我整个项目"之间的鸿沟,就是 Copilot 和 Agent 之间的鸿沟。Claude Code 站在 Agent 一侧:它作为终端 Agent 运行,读取、写入、执行、迭代——不需要 GUI 或 IDE 集成。

与嵌入编辑器的工具不同,Claude Code 直接操作你的文件系统、运行 Shell 命令、管理 Git 分支,并通过 CLAUDE.md 维护上下文。这让它很适合端到端的项目创建——需要在前端、后端和基础设施层之间对几十个文件做协同修改的场景。

对收益要诚实:它是定性的。结构化的循环让你把机械性工作外包出去——脚手架、样板代码、首轮测试——同时把有风险的决策(需求、架构、安全)明确地留在自己手里。它不会免除审查;它把你的精力从敲键盘转移到指挥和验证上。如果你想要针对自己团队的数字,就在自己的工作上度量——本文末尾给了方法。

我们用一个任务管理应用作为贯穿全文的例子:React + TypeScript 前端、Python FastAPI 后端、PostgreSQL 数据库,两端都带测试。


阶段一:项目初始化与 CLAUDE.md

每个 Claude Code 项目都从正确的初始化开始。/init 命令会分析你的仓库(或空目录),生成一个 CLAUDE.md 文件,作为跨会话的持久记忆。

安装 Claude Code

bash
# 原生安装器(推荐)
curl -fsSL https://claude.ai/install.sh | bash

# 或通过 npm
npm install -g @anthropic-ai/claude-code

# 验证安装
claude --version

/init 命令

进入项目目录(或新建一个),运行:

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

# 在 Claude Code 会话内:
> /init

Claude Code 扫描项目结构(如已有文件)或询问你打算用的技术栈,然后生成 CLAUDE.md

markdown
# CLAUDE.md - 项目配置

## 项目概述
全栈任务管理应用,React 前端 + Python FastAPI 后端。

## 技术栈
- 前端:React 18 + TypeScript + Vite + TailwindCSS
- 后端:Python 3.12 + FastAPI + SQLAlchemy + PostgreSQL
- 测试:Vitest(前端),Pytest(后端)
- 部署:Docker + docker-compose

## 常用命令
- `npm run dev` - 启动前端开发服务器
- `uvicorn main:app --reload` - 启动后端
- `npm run test` - 前端测试
- `pytest` - 后端测试
- `docker-compose up` - 全栈启动

## 架构决策
- 带 OpenAPI 规范的 REST API
- JWT 认证
- 数据访问采用 Repository 模式
- 基于 Feature 的目录结构

## 编码约定
- 使用 @ 前缀的绝对导入
- 所有 API 响应遵循 {data, error, meta} 信封
- 数据库模型用 snake_case,API DTO 用 camelCase
- 每个端点都需要用 Pydantic 做输入校验

为什么 CLAUDE.md 重要

CLAUDE.md 在每次会话启动时被读取。实际意义是:

  1. 你无需每次重复项目上下文。
  2. Claude Code 往往会一致地遵循你的约定。
  3. 架构说明跨越数天、数周依然保留。
  4. 队友的会话继承同一套标准。

但有一点要说清:CLAUDE.md 塑造 Agent 的产出,但不强制任何东西。写一句"客户端组件里绝不直接访问数据库"是有用的提醒,不是护栏——护栏在你的架构和代码审查里。

flowchart TD A["开发者启动:claude"] --> B["Claude 读取 CLAUDE.md"] B --> C["加载项目上下文"] C --> D{"描述新任务"} D --> E["Claude 理解技术栈"] D --> F["Claude 遵循约定"] D --> G["Claude 知道常用命令"] E --> H["准确的实现"] F --> H G --> H

阶段二:用 Plan Mode 做需求分析

在写下第一行代码之前,用 Claude Code 的 Plan Mode 分析需求、建立共识。Plan Mode(按 Shift+Tab 激活)让 Claude 只思考、只提方案、不执行。

Explore → Plan → Execute 循环

这是 Claude Code 处理复杂任务的运作模型:

flowchart LR A["1. EXPLORE"] --> B["2. PLAN"] B --> C["3. EXECUTE"] C --> D{"审查通过?"} D -->|是| E["提交"] D -->|否| B A -.- A1["读文件,理解结构"] B -.- B1["提出改动,获得批准"] C -.- C1["多文件编辑,运行命令"]

在实践中使用 Plan Mode

bash
# 在 Claude Code 会话内,按 Shift+Tab 进入 Plan Mode
> [Plan Mode] 构建一个任务管理应用,包含:
  - 用户认证(注册/登录/登出)
  - 任务的 CRUD 操作
  - 任务分类和优先级
  - 带通知的截止日期
  - 带 OpenAPI 文档的 REST API

Claude Code 会返回一份结构化方案:

code
## 分析

我将构建一个全栈任务管理器,架构如下:

### 前端(React + TypeScript)
- 页面:Login、Signup、Dashboard、TaskDetail
- 组件:TaskList、TaskCard、CategoryFilter、PriorityBadge
- 状态:服务端状态用 React Query,UI 状态用 Zustand

### 后端(FastAPI + PostgreSQL)
- 模型:User、Task、Category
- 端点:/auth/*、/tasks/*、/categories/*
- 中间件:JWT 认证、CORS、限流

### 数据库 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)

### 实现顺序(Vertical Slices)
1. 项目脚手架 + Docker 配置
2. 认证切片:User 模型 → 注册/登录端点 → 登录 UI
3. 任务切片:Task 模型 → CRUD 端点 → Dashboard UI
4. 分类切片:Category 模型 → 端点 → 筛选 UI
5. 打磨:通知、错误处理、测试

是否按此方案执行?

为什么"先规划"重要

先规划不是为了省下某个固定百分比的工作量——它关乎错误在哪里暴露。没有方案,Claude Code 可能选到与你意图冲突的约定或模式,而你要到代码写完之后才发现这种不匹配,那时纠正代价高昂。有了方案,架构上的分歧只是写代码之前的一行更正。这就是杠杆:在成本最低的时刻抓住错误的方向。

关于结构化 AI 开发方法论的更深入背景,见我们的 Spec Coding 完全指南,它把这种"先规划"的思路正式化了。


阶段三:架构设计

批准高层方案后,下一阶段是详细的架构设计。Claude Code 在这里很强,因为它能一次性提出具体的文件结构、API 契约和数据模型。

生成项目结构

bash
> 按已批准的方案创建完整的项目结构。
  前后端都采用基于 Feature 的组织方式。

Claude Code 生成:

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 契约设计

Claude Code 能在实现之前生成 OpenAPI 规范,确保前端和后端(或不同的 Claude Code 会话)共享同一份契约:

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

人类审查检查点

这是工作流中的关键节点:架构是人类判断不可替代的地方。Claude Code 提议,你来审查:

  • 安全性(认证流程、数据暴露、谁能操作谁的记录)
  • 可扩展性影响(数据库设计、缓存策略)
  • 团队约定(命名、目录结构)
  • 业务逻辑准确性

一旦批准,Claude Code 就在一套确定下来的架构"轨道"上执行。


阶段四:逐步实现

架构批准后,实现遵循 Vertical Slice 模式:端到端构建完整功能,而不是先建所有模型、再建所有端点、最后建所有 UI。

Vertical Slice 工作流

flowchart TD A["功能:用户认证"] --> B["数据库:users 表 + 迁移"] B --> C["后端:认证端点"] C --> D["前端:登录/注册页"] D --> E["集成:端到端测试"] E --> F["功能完成且可测试"] G["功能:任务 CRUD"] --> H["数据库:tasks 表 + 迁移"] H --> I["后端:任务端点"] I --> J["前端:Dashboard + 表单"] J --> K["集成:端到端测试"] K --> L["功能完成且可测试"]

实现认证切片

bash
> 实现认证的 Vertical Slice:
  1. User 模型和迁移
  2. 带 JWT 的注册和登录端点
  3. 登录和注册的 React 页面
  遵循我们设计的 Schema。密码用 bcrypt。

Claude Code 生成跨多文件的协同改动:

后端 - User 模型:

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())

后端 - 认证端点:

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"}

前端 - 登录页(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">登录</h1>
        
        {loginMutation.isError && (
          <div className="bg-red-50 text-red-600 p-3 rounded">
            邮箱或密码错误
          </div>
        )}
        
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          placeholder="邮箱"
          className="w-full p-3 border rounded"
          required
        />
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          placeholder="密码"
          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 ? '登录中...' : '登录'}
        </button>
      </form>
    </div>
  );
}

注意:这个切片验证了用户身份,但它本身并没有授权用户操作特定记录。当你构建任务切片时,每个读取或修改任务的端点都必须检查该任务属于调用者——认证证明谁在调用,授权决定他们能碰什么。这个检查在你的处理函数里,不在登录流程里。

用 Auto-Accept 模式加速实现

对于高置信度任务(架构批准之后),可以开启 Auto-Accept,让 Claude Code 连续执行多文件改动而不在每步暂停确认:

bash
# 在 Claude Code 设置中或通过标志
> /config auto-accept true

# 现在 Claude 会创建文件、安装包、运行命令
# 每一步都不再征求批准

Auto-Accept 是一次真实的授权,不只是便利:免确认意味着 Agent 正以你的权限创建文件、安装包、运行 Shell 命令,且没有逐步检查点。在实现已批准方案的低风险改动时开启它;在任何探索性操作、任何触及迁移或生产配置的操作、以及任何你自己都不会盲跑的操作前,关闭它。由于这类控制在不同版本间会变化,请在 Claude Code 文档中确认当前的标志名与行为。

用 /agents 处理专门子任务

Claude Code 的 /agents 命令可派生专门的子 Agent 做并行工作:

bash
> /agents create frontend-agent "按我们的设计系统实现 TaskList 和 TaskCard 
  组件。使用 features/tasks/types.ts 里的任务 API 类型。"

> /agents create backend-agent "按 Repository 模式实现任务 CRUD 端点。
  包含分页和筛选。"

这在单个会话内实现了并行开发——一个 Agent 处理 UI,另一个构建 API 层。但要记住:你在 Prompt 里写的角色会塑造行为,却不会限制能力;如果你想让审查或测试 Agent 保持只读,那必须来自它的工具权限,而不只是它的描述。


阶段五:测试与调试

Claude Code 基于它创建的实现生成测试套件。因为它掌握代码的完整上下文,能覆盖边界情况和错误路径。

生成测试套件

bash
> 为认证功能生成完整测试:
  - 密码哈希和 JWT 创建的单元测试
  - 注册和登录端点的集成测试
  - LoginPage 的前端组件测试

后端测试(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

前端测试(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('渲染带邮箱和密码字段的登录表单', () => {
    render(<LoginPage />, { wrapper });
    expect(screen.getByPlaceholderText('邮箱')).toBeInTheDocument();
    expect(screen.getByPlaceholderText('密码')).toBeInTheDocument();
    expect(screen.getByRole('button', { name: /登录/i })).toBeInTheDocument();
  });

  it('提交期间显示加载状态', async () => {
    render(<LoginPage />, { wrapper });
    fireEvent.change(screen.getByPlaceholderText('邮箱'), {
      target: { value: '[email protected]' }
    });
    fireEvent.change(screen.getByPlaceholderText('密码'), {
      target: { value: 'password123' }
    });
    fireEvent.click(screen.getByRole('button'));
    
    await waitFor(() => {
      expect(screen.getByText('登录中...')).toBeInTheDocument();
    });
  });

  it('登录失败时显示错误信息', async () => {
    // Mock 失败的 API 响应
    vi.spyOn(global, 'fetch').mockRejectedValueOnce(new Error('401'));
    
    render(<LoginPage />, { wrapper });
    fireEvent.click(screen.getByRole('button'));
    
    await waitFor(() => {
      expect(screen.getByText(/邮箱或密码错误/i)).toBeInTheDocument();
    });
  });
});

生成的套件是很好的起点,但通过的套件只能证明测试实际断言了的东西。Agent 可能写出通过却在检查错误行为的测试——例如断言了 200 却没断言响应体,或者根本没测未授权路径。在信任绿色运行之前,去读断言,尤其是安全相关路径的断言。

调试工作流

当测试失败或出现 Bug 时,Claude Code 的调试工作流借助它对整个代码库的理解:

bash
> 密码错误时登录端点返回 500 而不是 401。
  错误信息:"AttributeError: 'NoneType' object has no attribute 'password_hash'"

Claude Code 会:

  1. 读取相关的端点和模型文件
  2. 识别出查询在检查密码之前可能返回 None
  3. 修复条件逻辑
  4. 再次运行测试以确认修复

这个"调试-修复-验证"的迭代循环,正是 Claude Code 终端原生设计展现价值的地方——它直接运行测试套件并持续迭代直到变绿。


阶段六:代码审查与重构

功能实现且测试通过后,把 Claude Code 当作首轮审查者。它对完整上下文的感知能捕捉到文件级 linter 漏掉的问题。

运行代码审查

bash
> 审查整个任务功能,关注:
  - 安全漏洞
  - 性能问题
  - 与我们约定的代码一致性
  - 缺失的错误处理

Claude Code 产出结构化的审查:

code
## 代码审查:任务功能

### 安全问题(高优先级)
1. **SQL 注入风险**(任务搜索):
   - 文件:backend/app/features/tasks/service.py:45
   - 问题:查询过滤中使用了 f-string
   - 修复:使用 SQLAlchemy 的参数化查询

2. **缺失授权检查**:
   - 文件:backend/app/features/tasks/router.py:67
   - 问题:DELETE /tasks/{id} 没有校验任务归属
   - 修复:删除前增加 user_id 检查

### 性能问题(中优先级)
3. **N+1 查询**(任务列表):
   - 文件:backend/app/features/tasks/router.py:23
   - 问题:每个任务单独加载分类
   - 修复:在查询中用 joinedload()

### 约定违规(低优先级)
4. TaskCard 组件用了内联样式而非 Tailwind 类
5. API 错误响应没遵循 {data, error, meta} 信封

是否修复这些问题?

那条"缺失授权检查"值得停下来看,因为它正是阶段四那个边界在审查里现形:端点验证了调用者身份,却从未检查任务是否属于他。Agent 审查在暴露这类问题上确实有用——但要把它当作提出候选项的首轮,而不是放行。对安全相关的改动,最终仍由人类决定。一次没发现问题的自动审查,并不证明没有问题可找。关于开发者角色如何围绕这类监督发生转变,见从程序员到 Agent 牧羊人:AI 时代开发者角色的演进

有把握地重构

审查后,让 Claude Code 重构:

bash
> 修复审查中发现的所有安全问题。
  保持现有测试覆盖——如果接口变化就更新测试。

Claude Code 编辑多个文件,同时更新实现和测试以保持一致。在你接受之前,重新读一遍 diff——尤其是那个授权修复。


阶段七:部署准备

最后一个阶段把可运行的项目变成部署就绪的包。Claude Code 处理 Docker 配置、环境设置和 CI 流水线创建。

Docker 配置

bash
> 创建生产 Docker 配置:
  - 多阶段构建以最小化镜像体积
  - 用于本地开发的 Docker Compose
  - 环境变量配置
  - 健康检查端点

生成的 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:

这里的 :- 兜底值(localdevdev-secret-change-in-prod)方便你本地起一套栈,而它们的用途仅此而已。它们按设计就是不安全的,绝不能进入部署环境:在预发或生产里,JWT_SECRETDB_PASSWORD 来自真实的密钥管理,没有默认值。这恰恰是那种要靠读配置来核实、而不是靠相信 Agent "知道它只是给 dev 用的"来放心的地方。

CI 流水线生成

bash
> 创建一个 GitHub Actions CI 流水线:
  - 用 PostgreSQL 服务容器运行后端测试
  - 运行前端测试
  - 构建 Docker 镜像
  - 仅在 main 分支部署

Claude Code 会生成一份针对你技术栈配置好的完整 .github/workflows/ci.yml。如果这个 workflow 用到了某个 Agent action 或持有部署凭据,就把它的 permissions 收窄到所需的最小范围——一个能部署的 CI 任务是高价值攻击目标。


当 Agent 拥有真实访问权限时如何保持掌控

用 Agent 构建整个项目,意味着这个 Agent 在编辑文件、运行你的 Shell,而且开着 Auto-Accept 时还是逐步无检查点地做。无论工具多强,有几条边界不能移动:

  • 给能完成工作的最小访问权限。 把 Auto-Accept 和任何子 Agent 的工具权限限定到任务本身。审查或测试 Agent 应当在权限上只读,而不只是在 Prompt 里。
  • 认证是身份,不是授权。 生成的认证代码证明谁在调用;它不证明调用者可以操作某条特定记录。对象级与租户级检查存在于你的处理函数里,并在每个请求上强制执行——阶段六的审查正是用来抓它缺失的时刻。
  • CLAUDE.md 是约定辅助,不是控制。 它改变 Agent 倾向于做什么;它不强制任何东西。真正的护栏是你的架构、权限和审查。
  • 测试和绿色 CI 只证明它们覆盖的东西。 信任之前先读断言,尤其是未授权和错误路径。
  • 不安全的默认值只给本地开发用。 兜底的密钥和密码必须在任何部署之前换成真实的密钥管理。
  • 把不可信输入当作不可信。 如果 Agent 基于 issue 文本、PR 评论,或通过 MCP 抓取的内容行动,那些内容是数据,不是有授权的指令。

这些几乎不拖慢工作流——它主要关乎你把控制留在哪里。把机械性工作外包出去,把决策留在自己手里。


完整工作流可视化

下面是把各阶段串起来的完整 8 步 Feature 工作流:

flowchart TD A["1. 描述:陈述功能需求"] --> B["2. 探索:Claude 读代码库"] B --> C["3. 规划:Claude 提出方案"] C --> D{"4. 批准:人类审核方案"} D -->|驳回| C D -->|批准| E["5. 实现:Claude 写代码"] E --> F["6. 审查:人类 + Claude 检查产出"] F -->|发现问题| E F -->|没问题| G["7. 测试:Claude 生成并运行测试"] G -->|测试失败| E G -->|测试通过| H["8. 提交:Claude 暂存并提交"]

会话管理最佳实践

场景 推荐做法
从零开始的新功能 开新会话,先用 Plan Mode
带堆栈的 Bug 修复 粘贴错误,让 Claude 探索
重构现有代码 先用 Plan Mode 圈定改动范围,再执行
给现有代码补测试 指向相关文件;仅当改动低风险时才用 Auto-Accept
多天的项目 依赖 CLAUDE.md 保持上下文

如何对比,以及如何自己度量收益

Claude Code 与其他 Agent 工具在完整项目上的对比

这些工具是不同的形态,而不是排在一个榜单上。把它当作一张地图,并在各家逐一核实当前细节,因为功能变化很快:

能力 Claude Code Cursor Agent GitHub Copilot
终端原生执行 原生 绑定 IDE 绑定 IDE
多文件协同编辑 有范围 较窄
运行 Shell 命令 直接 经终端面板 有限
持久项目记忆 CLAUDE.md 规则文件 有限
先规划后执行模式 Shift+Tab 手动提示 有限
子 Agent /agents 有限 有限
免确认执行 Auto-Accept 自动运行模式 有限
CI/CD 集成 GitHub Action 有限 GitHub 原生
最佳契合 完整项目、终端 + CI/CD 交互式 IDE 编码 行内补全、GitHub 工作流

关于 2026 年 AI 编程工具的更广对比,见我们的 2026 AI 编程工具对比

在你自己的工作上度量收益

关于 Agent 编程,你会看到大量被引用的效率百分比。多数是在别人的代码库和任务上测出来的,因此并不能预测你的结果。如果收益对你的决策重要,就在你实际做的工作上度量:

  • 挑几个有代表性的功能或 Bug 修复。
  • 一部分用上面的结构化循环做,一部分按你现在的方式做。
  • 追踪几个你在意的具体数字——功能可用所需时间、审查往返次数、逃逸缺陷数。
  • 你自己的任务上对比,而不是在某个基准上。

对于团队决策,这远比任何博客文章(包括本文)里的数字更可靠。

理解 Agent 范式

从"AI 作为自动补全"到"AI 作为自主 Agent"的转变,改变了软件被构建的方式。Claude Code 把它体现为一个真正的 AI Agent:它不只是预测下一行,而是对整个系统进行推理。几个底层概念让这成为可能:

  • LLM 推理支撑多步规划。
  • 上下文窗口 决定它一次能装下多少项目。
  • Prompt Engineering 是 CLAUDE.md 之所以有效的原因——它是一套持久、为项目调校的指令。

常见问题 FAQ

Claude Code 真的能从零构建整个项目吗?

它能脚手架化、实现、测试并准备部署一个全栈项目——多文件编辑、依赖管理、数据库 Schema、测试生成,全部在终端里完成。留给你的是有风险的决策:需求、架构、安全审查,以及决定接受什么。清晰的需求,加上用 Plan Mode 敲定架构,能让生成的代码连贯。

CLAUDE.md 是什么,为什么需要它?

CLAUDE.md 是 Claude Code 的项目记忆文件,每次会话启动时读取。它编码技术栈决策、约定、常用命令和架构说明,让你无需重复上下文。用 /init 自动生成再定制。它塑造产出但不强制任何东西——把它当作约定辅助,不是护栏。

Plan Mode 和常规执行有什么区别?

Plan Mode(Shift+Tab)让 Claude 只分析、只提方案、不执行。它遵循 Explore → Plan → Execute:理解代码库,列出步骤供你批准,只有确认后才执行。它让架构决策在代码存在之前就明确下来——而那正是走错方向纠正成本最低的地方。

用 Claude Code 构建功能的好工作流是什么样的?

一个 8 步循环:描述 → 探索 → 规划 → 批准 → 实现 → 审查 → 测试 → 提交。重点不是某个神奇的提速数字——而是架构不匹配会在方案阶段暴露,而不是等代码写完之后,并且你审查产出而不是盲目接受。

Claude Code 如何处理测试和调试?

它基于你的实现生成单元、集成和端到端测试。调试时,描述错误或粘贴堆栈——它读取相关文件,提出根因,并迭代直到测试通过。因为通过的套件只能证明它断言了的东西,所以要读断言,尤其是未授权和错误路径。


相关资源

术语

  • AI Agent —— 能感知、推理、行动的自主系统
  • LLM —— 驱动代码生成的模型
  • 上下文窗口 —— 模型一次能装下多少代码
  • Prompt Engineering —— CLAUDE.md 作为持久指令集为何有效
  • MCP —— 用于工具集成的 Model Context Protocol