Claude Code Plugins

Community-maintained marketplace

Feedback

durable-objects-pattern-checker

@hirefrank/hirefrank-marketplace
1
0

Automatically validates Cloudflare Durable Objects usage patterns, ensuring correct state management, hibernation, and strong consistency practices

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name durable-objects-pattern-checker
description Automatically validates Cloudflare Durable Objects usage patterns, ensuring correct state management, hibernation, and strong consistency practices
triggers Durable Object imports, DO stub usage, state management patterns, DO ID generation

Durable Objects Pattern Checker SKILL

Activation Patterns

This SKILL automatically activates when:

  • Durable Object imports or exports are detected
  • DO stub creation and usage patterns
  • State management in Durable Objects
  • ID generation patterns (idFromName, newUniqueId)
  • Hibernation and lifecycle patterns
  • WebSocket or real-time features with DOs

Expertise Provided

Durable Objects Best Practices

  • State Management: Ensures proper state persistence and consistency
  • ID Generation: Validates correct ID patterns for different use cases
  • Hibernation: Checks for proper hibernation implementation
  • Lifecycle Management: Validates constructor, fetch, and alarm handling
  • Strong Consistency: Ensures DOs are used when strong consistency is needed
  • Performance: Identifies DO performance anti-patterns

Specific Checks Performed

❌ Durable Objects Anti-Patterns

// These patterns trigger immediate alerts:
// Using DOs for stateless operations
export default {
  async fetch(request: Request, env: Env) {
    const id = env.COUNTER.newUniqueId();  // New DO every request!
    const stub = env.COUNTER.get(id);
    return stub.fetch(request);  // Overkill for simple counter
  }
}

// Missing hibernation for long-lived DOs
export class ChatRoom {
  constructor(state, env) {
    this.state = state;
    // Missing this.state.storage.setAlarm() for hibernation
  }
}

✅ Durable Objects Best Practices

// These patterns are validated as correct:
// Reuse DO instances for stateful coordination
export default {
  async fetch(request: Request, env: Env) {
    const ip = request.headers.get('CF-Connecting-IP');
    const id = env.RATE_LIMITER.idFromName(ip);  // Reuse same DO
    const stub = env.RATE_LIMITER.get(id);
    return stub.fetch(request);
  }
}

// Proper hibernation implementation
export class ChatRoom {
  constructor(state, env) {
    this.state = state;
    this.env = env;
    
    // Set alarm for hibernation after inactivity
    this.state.storage.setAlarm(Date.now() + 30000); // 30 seconds
  }
  
  alarm() {
    // DO will hibernate after alarm
  }
}

Integration Points

Complementary to Existing Components

  • cloudflare-architecture-strategist agent: Handles complex DO architecture, SKILL provides immediate pattern validation
  • edge-performance-oracle agent: Handles DO performance analysis, SKILL ensures correct usage patterns
  • workers-binding-validator SKILL: Ensures DO bindings are correct, SKILL validates usage patterns

Escalation Triggers

  • Complex DO architecture questions → cloudflare-architecture-strategist agent
  • DO performance troubleshooting → edge-performance-oracle agent
  • DO migration strategies → cloudflare-architecture-strategist agent

Validation Rules

P1 - Critical (Will Cause Issues)

  • New Unique ID Per Request: Creating new DO for every request
  • Missing Hibernation: Long-lived DOs without hibernation
  • State Leaks: State not properly persisted to storage
  • Blocking Operations: Synchronous operations in DO fetch

P2 - High (Performance/Correctness Issues)

  • Wrong ID Pattern: Using newUniqueId when idFromName is appropriate
  • Stateless DOs: Using DOs for operations that don't need state
  • Missing Error Handling: DO operations without proper error handling
  • Alarm Misuse: Incorrect alarm patterns for hibernation

P3 - Medium (Best Practices)

  • State Size: Large state objects that impact performance
  • Concurrency: Missing concurrency control for shared state
  • Cleanup: Missing cleanup in DO lifecycle

Remediation Examples

Fixing New Unique ID Per Request

// ❌ Critical: New DO for every request (expensive and wrong)
export default {
  async fetch(request: Request, env: Env) {
    const userId = getUserId(request);
    
    // Creates new DO instance for every request!
    const id = env.USER_SESSION.newUniqueId();
    const stub = env.USER_SESSION.get(id);
    
    return stub.fetch(request);
  }
}

// ✅ Correct: Reuse DO for same entity
export default {
  async fetch(request: Request, env: Env) {
    const userId = getUserId(request);
    
    // Reuse same DO for this user
    const id = env.USER_SESSION.idFromName(userId);
    const stub = env.USER_SESSION.get(id);
    
    return stub.fetch(request);
  }
}

Fixing Missing Hibernation

// ❌ High: DO never hibernates (wastes resources)
export class ChatRoom {
  constructor(state, env) {
    this.state = state;
    this.env = env;
    this.messages = [];
  }
  
  async fetch(request) {
    // Handle chat messages...
    // But never hibernates - stays in memory forever!
  }
}

// ✅ Correct: Implement hibernation
export class ChatRoom {
  constructor(state, env) {
    this.state = state;
    this.env = env;
    
    // Load persisted state
    this.loadState();
    
    // Set alarm for hibernation after inactivity
    this.resetHibernationTimer();
  }
  
  async loadState() {
    const messages = await this.state.storage.get('messages');
    this.messages = messages || [];
  }
  
  resetHibernationTimer() {
    // Reset alarm for 30 seconds from now
    this.state.storage.setAlarm(Date.now() + 30000);
  }
  
  async fetch(request) {
    // Reset timer on activity
    this.resetHibernationTimer();
    
    // Handle chat messages...
    return new Response('Message processed');
  }
  
  async alarm() {
    // Persist state before hibernation
    await this.state.storage.put('messages', this.messages);
    // DO will hibernate after this method returns
  }
}

Fixing Wrong ID Pattern

// ❌ High: Using newUniqueId for named resources
export default {
  async fetch(request: Request, env: Env) {
    const roomId = new URL(request.url).searchParams.get('room');
    
    // Wrong: Creates new DO for same room name
    const id = env.CHAT_ROOM.newUniqueId();
    const stub = env.CHAT_ROOM.get(id);
    
    return stub.fetch(request);
  }
}

// ✅ Correct: Use idFromName for named resources
export default {
  async fetch(request: Request, env: Env) {
    const roomId = new URL(request.url).searchParams.get('room');
    
    // Correct: Same DO for same room name
    const id = env.CHAT_ROOM.idFromName(roomId);
    const stub = env.CHAT_ROOM.get(id);
    
    return stub.fetch(request);
  }
}

Fixing State Persistence

// ❌ Critical: State not persisted (lost on hibernation)
export class Counter {
  constructor(state, env) {
    this.state = state;
    this.count = 0;  // Not persisted!
  }
  
  async fetch(request) {
    if (request.url.endsWith('/increment')) {
      this.count++;  // Lost when DO hibernates!
      return new Response(`Count: ${this.count}`);
    }
  }
}

// ✅ Correct: Persist state to storage
export class Counter {
  constructor(state, env) {
    this.state = state;
  }
  
  async fetch(request) {
    if (request.url.endsWith('/increment')) {
      // Persist to storage
      const currentCount = (await this.state.storage.get('count')) || 0;
      const newCount = currentCount + 1;
      await this.state.storage.put('count', newCount);
      
      return new Response(`Count: ${newCount}`);
    }
    
    if (request.url.endsWith('/get')) {
      const count = await this.state.storage.get('count') || 0;
      return new Response(`Count: ${count}`);
    }
  }
}

Fixing Stateless DO Usage

// ❌ High: Using DO for stateless operation (overkill)
export default {
  async fetch(request: Request, env: Env) {
    // Using DO for simple API call - unnecessary!
    const id = env.API_PROXY.newUniqueId();
    const stub = env.API_PROXY.get(id);
    return stub.fetch(request);
  }
}

// ✅ Correct: Handle stateless operations in Worker
export default {
  async fetch(request: Request, env: Env) {
    // Simple API call - handle directly in Worker
    const response = await fetch('https://api.example.com/data');
    return response;
  }
}

// ✅ Correct: Use DO for actual stateful coordination
export default {
  async fetch(request: Request, env: Env) {
    const ip = request.headers.get('CF-Connecting-IP');
    
    // Rate limiting needs state - perfect for DO
    const id = env.RATE_LIMITER.idFromName(ip);
    const stub = env.RATE_LIMITER.get(id);
    
    return stub.fetch(request);
  }
}

Durable Objects Use Cases

Use Durable Objects When:

  • Strong Consistency required (rate limiting, counters)
  • Stateful Coordination (chat rooms, game sessions)
  • Real-time Features (WebSockets, collaboration)
  • Distributed Locks (coordination between requests)
  • Long-running Operations (background processing)

Don't Use Durable Objects When:

  • Stateless Operations (simple API calls)
  • Read-heavy Caching (use KV instead)
  • Large File Storage (use R2 instead)
  • Simple Key-Value (use KV instead)

MCP Server Integration

When Cloudflare MCP server is available:

  • Query DO performance metrics and best practices
  • Get latest hibernation patterns and techniques
  • Check DO usage limits and quotas
  • Analyze DO performance in production

Benefits

Immediate Impact

  • Prevents Resource Waste: Catches DO anti-patterns that waste resources
  • Ensures Correctness: Validates state persistence and consistency
  • Improves Performance: Identifies performance issues in DO usage

Long-term Value

  • Consistent DO Patterns: Ensures all DO usage follows best practices
  • Better Resource Management: Proper hibernation and lifecycle management
  • Reduced Costs: Efficient DO usage reduces resource consumption

Usage Examples

During DO Creation

// Developer types: const id = env.MY_DO.newUniqueId();
// SKILL immediately activates: "⚠️ HIGH: Using newUniqueId for every request. Consider idFromName for named resources or if this should be stateless."

During State Management

// Developer types: this.count = 0; in constructor
// SKILL immediately activates: "❌ CRITICAL: State not persisted to storage. Use this.state.storage.put() to persist data."

During Hibernation

// Developer types: DO without alarm() method
// SKILL immediately activates: "⚠️ HIGH: Durable Object missing hibernation. Add alarm() method and setAlarm() for resource efficiency."

Performance Targets

DO Creation

  • Excellent: Reuse existing DOs (idFromName)
  • Good: Minimal new DO creation
  • Acceptable: Appropriate DO usage patterns
  • Needs Improvement: Creating new DOs per request

State Persistence

  • Excellent: All state persisted to storage
  • Good: Critical state persisted
  • Acceptable: Basic state management
  • Needs Improvement: State not persisted

Hibernation

  • Excellent: Proper hibernation implementation
  • Good: Basic hibernation setup
  • Acceptable: Some hibernation consideration
  • Needs Improvement: No hibernation (resource waste)

This SKILL ensures Durable Objects are used correctly by providing immediate, autonomous validation of DO patterns, preventing common mistakes and ensuring efficient state management.