Back to Blog

深入剖析 Claude Code:代码演练与最佳实践(第四部分)

2026-03-31
Claude Code TypeScript 最佳实践 设计模式 代码质量

深入剖析 Claude Code:代码演练与最佳实践

系列文章第四部分:实战经验与最佳实践


通过前三篇文章,我们了解了 Claude Code 的架构、设计模式和核心功能实现。今天,让我们聚焦于代码质量,从实际代码中学习最佳实践。

设计模式实战

工具模式:统一接口的力量

Claude Code 中所有工具都实现统一接口,这带来了极大的可组合性。

// 工具接口定义
interface Tool {
  name: string
  description: string
  parameters?: ToolParameter[]
  call(params: any, context: ToolUseContext): AsyncGenerator
}

// 实际实现示例
class CustomTool implements Tool {
  name = 'custom_operation'
  description = 'Performs custom operation'

  async* call(
    params: { input: string },
    context: ToolUseContext
  ): AsyncGenerator {
    // 发送进度
    yield { type: 'progress', data: 'Starting...' }

    // 检查取消信号
    if (context.abortController.signal.aborted) {
      return
    }

    // 执行操作
    const result = await this.process(params.input)

    // 返回结果
    yield { type: 'result', data: result }
  }

  private async process(input: string): Promise {
    // 实现细节
  }
}

// 工具注册
const toolRegistry = new Map()
toolRegistry.set('custom_operation', new CustomTool())

// 统一调用方式
async function executeTool(
  toolName: string,
  params: any,
  context: ToolUseContext
): AsyncGenerator {
  const tool = toolRegistry.get(toolName)
  if (!tool) {
    throw new Error(`Tool not found: ${toolName}`)
  }
  yield* tool.call(params, context)
}

策略模式:不同工具的不同执行策略

// 定义策略接口
interface ExecutionStrategy {
  execute(params: any, context: ToolUseContext): Promise
}

// Bash 执行策略
class BashExecutionStrategy implements ExecutionStrategy {
  async execute(params: { command: string }, context: ToolUseContext): Promise {
    const { command } = params
    const { signal } = context.abortController

    const process = spawn(command, { shell: true })

    // 监听取消信号
    signal.addEventListener('abort', () => {
      process.kill()
    })

    return new Promise((resolve, reject) => {
      process.on('close', (code) => {
        resolve({ exitCode: code })
      })
      process.on('error', reject)
    })
  }
}

// 文件操作策略
class FileOperationStrategy implements ExecutionStrategy {
  async execute(params: { path: string; content: string }, context: ToolUseContext): Promise {
    await fs.writeFile(params.path, params.content)
    return { success: true }
  }
}

// 策略工厂
class StrategyFactory {
  private strategies = new Map()

  register(toolType: string, strategy: ExecutionStrategy): void {
    this.strategies.set(toolType, strategy)
  }

  get(toolType: string): ExecutionStrategy {
    const strategy = this.strategies.get(toolType)
    if (!strategy) {
      throw new Error(`No strategy for: ${toolType}`)
    }
    return strategy
  }
}

// 使用
const factory = new StrategyFactory()
factory.register('bash', new BashExecutionStrategy())
factory.register('file', new FileOperationStrategy())

const strategy = factory.get('bash')
await strategy.execute({ command: 'npm test' }, context)

观察者模式:AsyncGenerator 本质

AsyncGenerator 是观察者模式的自然实现:

// 事件发射器
class EventEmitter {
  private listeners = new Map>()

  on(event: string, listener: Listener): void {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, new Set())
    }
    this.listeners.get(event)!.add(listener)
  }

  emit(event: string, data: any): void {
    const listeners = this.listeners.get(event)
    if (listeners) {
      listeners.forEach(listener => listener(data))
    }
  }
}

// 转换为 AsyncGenerator
async function* eventToGenerator(
  emitter: EventEmitter,
  event: string
): AsyncGenerator {
  let resolver: (value: any) => void

  emitter.on(event, (data) => {
    if (resolver) {
      resolver(data)
    }
  })

  while (true) {
    const data = await new Promise(resolve => {
      resolver = resolve
    })
    yield data
  }
}

// 使用
const emitter = new EventEmitter()

// 在另一个线程
setTimeout(() => {
  emitter.emit('data', { message: 'Hello' })
}, 1000)

// 消费
for await (const data of eventToGenerator(emitter, 'data')) {
  console.log(data)
  break  // 只接收一次
}

命令模式:CLI 结构

import { Command } from '@commander-js/extra-typings'

// 定义命令
const program = new Command()

program
  .name('claude')
  .description('AI coding assistant')
  .version('0.2.8')

// 添加子命令
program
  .command('cost')
  .description('Show cost information')
  .action(showCost)

program
  .command('doctor')
  .description('Run diagnostics')
  .action(runDoctor)

// 命令实现
async function showCost() {
  const tracker = new CostTracker()
  const summary = tracker.getSessionSummary()

  console.log(`
Session Cost:
  Tokens: ${summary.tokens}
  Cost: ${summary.formatted}
  `)
}

// 解析并执行
program.parse()

错误处理策略

分层错误处理

// 自定义错误类型
class ToolExecutionError extends Error {
  constructor(
    public toolName: string,
    public originalError: Error,
    message?: string
  ) {
    super(message || `Tool ${toolName} failed: ${originalError.message}`)
    this.name = 'ToolExecutionError'
  }
}

class ValidationError extends Error {
  constructor(
    public field: string,
    public value: any,
    message: string
  ) {
    super(message)
    this.name = 'ValidationError'
  }
}

// 错误处理中间件
async function withErrorHandling(
  operation: () => Promise,
  context: string
): Promise {
  try {
    return await operation()
  } catch (error) {
    if (error instanceof ValidationError) {
      // 验证错误:返回友好消息
      throw new Error(`Validation failed for ${error.field}: ${error.message}`)
    } else if (error instanceof ToolExecutionError) {
      // 工具执行错误:记录并包装
      logError(error, context)
      throw new Error(
        `Failed to execute ${error.toolName}: ${error.originalError.message}`
      )
    } else {
      // 未知错误:记录并重新抛出
      logError(error, context)
      throw error
    }
  }
}

// 使用示例
async function executeToolSafe(
  tool: Tool,
  params: any,
  context: ToolUseContext
): Promise {
  return withErrorHandling(async () => {
    // 验证参数
    validateParams(params, tool.parameters)

    // 执行工具
    const results = []
    for await (const result of tool.call(params, context)) {
      results.push(result)
    }

    return results[results.length - 1]  // 返回最后一个结果
  }, `tool:${tool.name}`)
}

优雅降级

// 可选功能:失败时降级
async function getOptionalFeature(): Promise {
  try {
    return await loadFeature()
  } catch (error) {
    logError(error, 'optional-feature')
    return null  // 降级:不使用该功能
  }
}

// 使用
const feature = await getOptionalFeature()
if (feature) {
  await feature.enhanceBehavior()
} else {
  // 使用基本行为
  await basicBehavior()
}

用户友好的错误消息

// 错误消息格式化
function formatErrorMessage(error: Error, context: string): string {
  // 根据错误类型生成不同消息
  if (error.message.includes('ENOENT')) {
    return `File not found: ${context}`
  } else if (error.message.includes('EACCES')) {
    return `Permission denied: ${context}`
  } else if (error.message.includes('ETIMEDOUT')) {
    return `Operation timed out. Please try again.`
  }

  // 默认消息
  return `An error occurred: ${error.message}`
}

// 使用
try {
  await readFile(path)
} catch (error) {
  const userMessage = formatErrorMessage(error, path)
  console.error(userMessage)
  // 不会显示原始错误堆栈给用户
}

Sentry 集成

// src/services/sentry.ts
import * as Sentry from '@sentry/node'

export function initSentry(): void {
  Sentry.init({
    dsn: process.env.SENTRY_DSN,
    environment: process.env.NODE_ENV,
    tracesSampleRate: 1.0,
  })
}

// 捕获错误
export function captureError(error: Error, context?: Record): void {
  Sentry.withScope(scope => {
    if (context) {
      Object.entries(context).forEach(([key, value]) => {
        scope.setExtra(key, value)
      })
    }
    Sentry.captureException(error)
  })
}

// 使用
try {
  await riskyOperation()
} catch (error) {
  captureError(error, {
    operation: 'riskyOperation',
    userId: getCurrentUser()?.id
  })
}

类型安全最佳实践

严格的类型检查

// tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true
  }
}

精心设计的接口

// 工具参数使用联合类型
type ToolParams =
  | { tool: 'bash'; params: BashParams }
  | { tool: 'read_file'; params: ReadFileParams }
  | { tool: 'edit_file'; params: EditFileParams }

// 类型守卫
function isBashParams(params: ToolParams): params is { tool: 'bash'; params: BashParams } {
  return params.tool === 'bash'
}

// 使用
function executeTool(params: ToolParams): Promise {
  if (isBashParams(params)) {
    return executeBash(params.params)
  } else if (params.tool === 'read_file') {
    return readFile(params.params)
  }
  // TypeScript 会确保处理所有情况
  exhaustiveCheck(params)
}

// 编译时检查完整性
function exhaustiveCheck(value: never): never {
  throw new Error(`Unexpected value: ${value}`)
}

泛型的正确使用

// 工具结果类型
interface ToolResult {
  type: 'progress' | 'data' | 'result' | 'error'
  data?: T
  error?: string
}

// 工具接口
interface Tool {
  name: string
  description: string
  call(
    params: TParams,
    context: ToolUseContext
  ): AsyncGenerator>
}

// 具体工具实现
class BashTool implements Tool<{ command: string }, { exitCode: number }> {
  name = 'bash'
  description = 'Execute bash commands'

  async* call(
    params: { command: string },
    context: ToolUseContext
  ): AsyncGenerator> {
    // TypeScript 知道 params.command 存在
    // 返回类型被正确推断
    yield { type: 'result', data: { exitCode: 0 } }
  }
}

类型守卫和断言

// 消息类型
type Message = UserMessage | AssistantMessage | SystemMessage

interface UserMessage {
  type: 'user'
  content: string
}

interface AssistantMessage {
  type: 'assistant'
  content: string
  toolUse?: ToolUseBlock[]
}

interface SystemMessage {
  type: 'system'
  content: string
}

// 类型守卫
function isAssistantMessage(message: Message): message is AssistantMessage {
  return message.type === 'assistant'
}

// 使用
function hasToolUse(message: Message): boolean {
  if (isAssistantMessage(message)) {
    return message.toolUse !== undefined
  }
  return false
}

// 类型断言函数
function assertIsAssistantMessage(message: Message): asserts message is AssistantMessage {
  if (message.type !== 'assistant') {
    throw new Error('Expected assistant message')
  }
}

// 使用
try {
  assertIsAssistantMessage(message)
  // 现在 message 被认为是 AssistantMessage
  console.log(message.toolUse)
} catch (error) {
  console.error('Not an assistant message')
}

性能优化技巧

流式响应实现

// 不好的做法:等待完整响应
async function badExample(): Promise {
  const response = await fetch(url)
  const text = await response.text()
  return text
}

// 好的做法:流式处理
async function* goodExample(): AsyncGenerator {
  const response = await fetch(url)
  const reader = response.body!.getReader()

  while (true) {
    const { done, value } = await reader.read()
    if (done) break

    const chunk = new TextDecoder().decode(value)
    yield chunk
  }
}

// 使用
for await (const chunk of goodExample()) {
  process.stdout.write(chunk)
}

记忆化模式

import { memoize } from 'lodash-es'

// 简单记忆化
const memoizedExpensive = memoize(async (input: string) => {
  return await expensiveOperation(input)
})

// 带缓存的记忆化
class Cache {
  private cache = new Map()

  get(key: string): any | null {
    const entry = this.cache.get(key)
    if (!entry) return null

    if (Date.now() > entry.expiry) {
      this.cache.delete(key)
      return null
    }

    return entry.value
  }

  set(key: string, value: any, ttl: number): void {
    this.cache.set(key, {
      value,
      expiry: Date.now() + ttl
    })
  }
}

const cache = new Cache()

async function getCachedOrCompute(
  key: string,
  compute: () => Promise,
  ttl: number = 60000
): Promise {
  const cached = cache.get(key)
  if (cached !== null) {
    return cached
  }

  const value = await compute()
  cache.set(key, value, ttl)
  return value
}

并行处理

// 串行处理(慢)
async function sequential(items: Item[]): Promise {
  const results = []
  for (const item of items) {
    const result = await processItem(item)
    results.push(result)
  }
  return results
}

// 并行处理(快)
async function parallel(items: Item[]): Promise {
  return Promise.all(items.map(item => processItem(item)))
}

// 限制并发数
async function parallelWithLimit(
  items: Item[],
  limit: number = 10
): Promise {
  const results: Result[] = []

  for (let i = 0; i < items.length; i += limit) {
    const batch = items.slice(i, i + limit)
    const batchResults = await Promise.all(
      batch.map(item => processItem(item))
    )
    results.push(...batchResults)
  }

  return results
}

资源清理

// 使用 AbortController 清理
async function withCleanup(
  operation: (signal: AbortSignal) => Promise
): Promise {
  const controller = new AbortController()

  try {
    return await operation(controller.signal)
  } finally {
    controller.abort()
  }
}

// 使用
await withCleanup(async (signal) => {
  const response = await fetch(url, { signal })
  return await response.json()
})

// 清理事件监听器
class ManagedEventEmitter extends EventEmitter {
  private listeners: Array<{ event: string; listener: Function }> = []

  on(event: string, listener: Function): this {
    super.on(event, listener)
    this.listeners.push({ event, listener })
    return this
  }

  removeAll(): void {
    this.listeners.forEach(({ event, listener }) => {
      this.off(event, listener)
    })
    this.listeners = []
  }
}

测试策略

单元测试

import { describe, it, expect, vi } from 'vitest'

describe('BashTool', () => {
  it('should execute command successfully', async () => {
    const tool = new BashTool()
    const mockContext = {
      abortController: new AbortController(),
      options: {}
    }

    // Mock spawn
    vi.mock('child_process', () => ({
      spawn: vi.fn(() => ({
        stdout: { on: vi.fn(), emit: 'data' },
        on: vi.fn((event, callback) => {
          if (event === 'close') callback(0)
        })
      }))
    }))

    const results = []
    for await (const result of tool.call(
      { command: 'echo hello' },
      mockContext
    )) {
      results.push(result)
    }

    expect(results).toHaveLength(2)  // progress + result
    expect(results[1].data.exitCode).toBe(0)
  })

  it('should handle command timeout', async () => {
    const tool = new BashTool()
    const abortController = new AbortController()

    // 立即取消
    abortController.abort()

    const results = []
    for await (const result of tool.call(
      { command: 'sleep 10' },
      { abortController }
    )) {
      results.push(result)
    }

    // 应该提前退出
    expect(results.length).toBeLessThan(2)
  })
})

Mock AI 响应

// Mock Claude API
class MockClaudeAPI {
  async *create(
    params: CreateMessageParams
  ): AsyncGenerator {
    // 返回预设响应
    yield {
      type: 'assistant',
      content: 'This is a mock response'
    }
  }
}

// 测试中使用
describe('QueryEngine', () => {
  it('should process AI response', async () => {
    const mockAPI = new MockClaudeAPI()
    const engine = new QueryEngine(mockAPI)

    const response = await engine.query('test message')

    expect(response.content).toBe('This is a mock response')
  })
})

集成测试

describe('Tool Integration', () => {
  it('should execute multiple tools in sequence', async () => {
    const context = {
      abortController: new AbortController(),
      readFileTimestamps: {}
    }

    // 1. 读取文件
    const readTool = new FileReadTool()
    const readResults = []
    for await (const result of readTool.call(
      { file_path: 'test.txt' },
      context
    )) {
      readResults.push(result)
    }

    // 2. 编辑文件
    const editTool = new FileEditTool()
    const editResults = []
    for await (const result of editTool.call(
      {
        file_path: 'test.txt',
        old_string: 'old',
        new_string: 'new'
      },
      context
    )) {
      editResults.push(result)
    }

    // 验证
    expect(editResults[editResults.length - 1].data.success).toBe(true)
  })
})

代码组织

模块化设计

// 文件结构
src/
├── tools/           # 工具实现
│   ├── BashTool/
│   ├── FileReadTool/
│   └── index.ts     # 工具注册
├── services/        # 服务层
│   ├── claude.ts
│   └── mcp.ts
├── utils/           # 工具函数
│   ├── config.ts
│   └── log.ts
└── types.ts         # 类型定义

// 每个模块导出清晰的接口
// src/tools/index.ts
export { BashTool } from './BashTool/BashTool.js'
export { FileReadTool } from './FileReadTool/FileReadTool.js'
export type { Tool } from './Tool.js'
export function getTools(): Tool[] { /* ... */ }

关注点分离

// 数据访问层
class ConfigRepository {
  async load(path: string): Promise {
    const content = await fs.readFile(path, 'utf-8')
    return JSON.parse(content)
  }

  async save(path: string, config: Config): Promise {
    await fs.writeFile(path, JSON.stringify(config, null, 2))
  }
}

// 业务逻辑层
class ConfigService {
  constructor(private repo: ConfigRepository) {}

  async getProjectConfig(): Promise {
    const config = await this.repo.load('.claude/config.json')
    return this.validate(config)
  }

  private validate(config: any): ProjectConfig {
    // 验证逻辑
  }
}

// 表现层
async function showConfig() {
  const repo = new ConfigRepository()
  const service = new ConfigService(repo)
  const config = await service.getProjectConfig()
  console.log(config)
}

可测试性考虑

// 依赖注入使代码可测试
class ToolExecutor {
  constructor(
    private toolRegistry: ToolRegistry,
    private permissionChecker: PermissionChecker,
    private logger: Logger
  ) {}

  async execute(toolName: string, params: any): Promise {
    // 可以轻松 mock 这些依赖
    if (!await this.permissionChecker.canUse(toolName)) {
      throw new Error('Permission denied')
    }

    this.logger.info(`Executing ${toolName}`)
    // ...
  }
}

// 测试中
it('should check permissions', async () => {
  const mockPermissionChecker = {
    canUse: vi.fn().mockResolvedValue(false)
  }

  const executor = new ToolExecutor(
    toolRegistry,
    mockPermissionChecker,
    logger
  )

  await expect(executor.execute('bash', {})).rejects.toThrow('Permission denied')
})

代码质量检查清单

在提交代码前,确保:

  • 所有函数都有类型签名
  • 错误被正确处理和记录
  • 异步操作支持取消
  • 资源被正确清理
  • 性能关键路径被优化
  • 代码被适当测试
  • 变量命名清晰
  • 复杂逻辑有注释
  • 没有重复代码

下一步

在最后一篇文章中,我们将总结:

  • 关键经验总结:从项目中提取的核心经验
  • 实践应用:如何将这些经验应用到自己的项目
  • 未来方向:AI Agent 的发展趋势
  • 学习资源:进一步学习的建议

系列目录

  1. 项目概览与核心设计
  2. 架构模式与设计哲学
  3. 关键功能与实现细节
  4. 代码演练与最佳实践(本文)
  5. 经验总结与实践应用(即将推出)
Enjoyed this article? Share it with others!