Claude-skill-registry durable-objects-pattern-checker
Automatically validates Cloudflare Durable Objects usage patterns, ensuring correct state management, hibernation, and strong consistency practices
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/durable-objects-pattern-checker" ~/.claude/skills/majiayu000-claude-skill-registry-durable-objects-pattern-checker && rm -rf "$T"
manifest:
skills/data/durable-objects-pattern-checker/SKILL.mdsource content
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 →
agentcloudflare-architecture-strategist - DO performance troubleshooting →
agentedge-performance-oracle - DO migration strategies →
agentcloudflare-architecture-strategist
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
whennewUniqueId
is appropriateidFromName - 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.