Claude Skills Guide

Claude Code Skills Microservices Communication Patterns

Building microservices architectures requires careful consideration of how services communicate, handle failures, and maintain consistency across distributed systems. Claude Code skills provide a powerful way to automate, document, and generate communication patterns between microservices, helping developers implement reliable inter-service communication without starting from scratch. See the advanced hub for related architectural patterns.

This guide covers practical patterns for implementing microservices communication using Claude Code skills, with real code examples you can apply to your architecture.

Understanding Microservices Communication

Microservices communicate through two primary mechanisms: synchronous communication where a client waits for a response, and asynchronous communication where services exchange messages without blocking. Each approach has trade-offs around latency, consistency, and complexity that affect your system design.

Claude Code skills can generate boilerplate code, validate API contracts, implement error handling, and create documentation for all these communication patterns. The skill system acts as a specialized assistant that understands your architecture and produces consistent, production-ready implementations.

Pattern 1: REST API Communication

REST remains the most common pattern for synchronous microservices communication. Claude Code skills can generate client libraries, validate request/response schemas, and implement retry logic.

# rest-client-skill.md
name: REST Microservice Client Generator
description: Generate type-safe REST client code for microservices communication

When generating REST client code for microservices:

  1. Use a base client class with common configuration
  2. Implement exponential backoff for retries
  3. Add timeout handling for long-running requests
  4. Include circuit breaker pattern for resilience
  5. Generate TypeScript types from OpenAPI specs

Here’s how a generated client might look:

// user-service-client.ts
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';

class CircuitBreaker {
  private failures = 0;
  private lastFailure: Date | null = null;
  private state: 'closed' | 'open' | 'half-open' = 'closed';
  
  constructor(
    private threshold: number = 5,
    private timeout: number = 30000
  ) {}
  
  async execute<T>(fn: () => Promise<T>): Promise<T> {
    if (this.state === 'open') {
      if (Date.now() - (this.lastFailure?.getTime() ?? 0) > this.timeout) {
        this.state = 'half-open';
      } else {
        throw new Error('Circuit breaker is open');
      }
    }
    
    try {
      const result = await fn();
      this.failures = 0;
      this.state = 'closed';
      return result;
    } catch (error) {
      this.failures++;
      this.lastFailure = new Date();
      if (this.failures >= this.threshold) {
        this.state = 'open';
      }
      throw error;
    }
  }
}

class UserServiceClient {
  private client: AxiosInstance;
  private breaker: CircuitBreaker;
  
  constructor(baseURL: string) {
    this.client = axios.create({
      baseURL,
      timeout: 5000,
    });
    this.breaker = new CircuitBreaker();
  }
  
  async getUser(userId: string): Promise<User> {
    return this.breaker.execute(() => 
      this.client.get(`/users/${userId}`).then(r => r.data)
    );
  }
  
  async createUser(data: CreateUserRequest): Promise<User> {
    return this.breaker.execute(() =>
      this.client.post('/users', data).then(r => r.data)
    );
  }
}

Pattern 2: Message Queue Communication

Asynchronous communication through message queues decouples services and improves system resilience. Claude Code skills can generate publisher and consumer code, handle dead letter queues, and implement idempotency.

# message-queue-skill.md
name: Message Queue Pattern Generator
description: Generate async communication code using RabbitMQ, Kafka, or SQS

Generate message queue implementations that include:

  1. Publisher with confirmation handling
  2. Consumer with graceful shutdown
  3. Dead letter queue configuration
  4. Message retry with backoff
  5. Idempotency keys for duplicate detection

A practical implementation:

# message_publisher.py
import json
import time
import uuid
import hashlib
from typing import Any, Callable
from dataclasses import dataclass
import pika
from pika.exceptions import AMQPConnectionError

@dataclass
class Message:
    id: str
    payload: dict
    timestamp: float
    idempotency_key: str

class ResilientPublisher:
    def __init__(
        self,
        host: str = 'localhost',
        exchange: str = 'microservices',
        max_retries: int = 3
    ):
        self.connection = None
        self.channel = None
        self.host = host
        self.exchange = exchange
        self.max_retries = max_retries
        self.processed_keys: set[str] = set()
        
    def connect(self):
        """Establish connection with retry logic."""
        for attempt in range(self.max_retries):
            try:
                self.connection = pika.BlockingConnection(
                    pika.ConnectionParameters(host=self.host)
                )
                self.channel = self.connection.channel()
                self.channel.exchange_declare(
                    exchange=self.exchange,
                    exchange_type='topic',
                    durable=True
                )
                return
            except AMQPConnectionError:
                wait_time = 2 ** attempt
                print(f"Connection failed, retrying in {wait_time}s...")
                time.sleep(wait_time)
        raise ConnectionError("Failed to connect after max retries")
    
    def publish(
        self,
        routing_key: str,
        payload: dict,
        idempotency_key: str = None
    ) -> bool:
        """Publish message with idempotency support."""
        if idempotency_key and idempotency_key in self.processed_keys:
            print(f"Duplicate message detected: {idempotency_key}")
            return False
            
        message = Message(
            id=uuid.uuid4().hex,
            payload=payload,
            timestamp=time.time(),
            idempotency_key=idempotency_key or hashlib.md5(
                json.dumps(payload, sort_keys=True).encode()
            ).hexdigest()
        )
        
        self.channel.basic_publish(
            exchange=self.exchange,
            routing_key=routing_key,
            body=json.dumps(message.__dict__),
            properties=pika.BasicProperties(
                delivery_mode=2,  # Persistent
                content_type='application/json',
                message_id=message.id
            )
        )
        
        if idempotency_key:
            self.processed_keys.add(idempotency_key)
        return True
    
    def close(self):
        if self.connection:
            self.connection.close()

Pattern 3: Event-Driven Communication

Event-driven architectures enable loose coupling and scalability. Services emit events that other services consume independently, allowing you to add new consumers without modifying producers.

# event-skill.md
name: Event Schema Generator
description: Generate event schemas and handlers for event-driven microservices

Create event-driven communication that includes:

  1. Event schema definitions (JSON Schema)
  2. Event publisher with versioning
  3. Event subscriber with filtering
  4. Schema evolution handling
  5. Event sourcing support

Pattern 4: Saga Pattern for Distributed Transactions

When microservices need to coordinate multi-step operations across services, the saga pattern provides a way to maintain consistency without distributed transactions. For broader agent coordination patterns, see Claude Code Multi-Agent Subagent Communication Guide.

// saga-orchestrator.ts
interface SagaStep<T> {
  name: string;
  compensate: () => Promise<void>;
  execute: () => Promise<T>;
}

interface SagaState {
  completedSteps: string[];
  currentStep: string | null;
  data: Record<string, any>;
  status: 'pending' | 'running' | 'completed' | 'failed' | 'compensating';
}

class SagaOrchestrator {
  private steps: SagaStep<any>[] = [];
  private state: SagaState = {
    completedSteps: [],
    currentStep: null,
    data: {},
    status: 'pending'
  };
  
  addStep<T>(step: SagaStep<T>): this {
    this.steps.push(step);
    return this;
  }
  
  async execute(initialData: Record<string, any>): Promise<void> {
    this.state.data = initialData;
    this.state.status = 'running';
    
    try {
      for (const step of this.steps) {
        this.state.currentStep = step.name;
        console.log(`Executing step: ${step.name}`);
        
        const result = await step.execute();
        this.state.data[step.name] = result;
        this.state.completedSteps.push(step.name);
      }
      
      this.state.status = 'completed';
      console.log('Saga completed successfully');
    } catch (error) {
      await this.compensate();
      throw error;
    }
  }
  
  private async compensate(): Promise<void> {
    this.state.status = 'compensating';
    console.log('Starting compensation...');
    
    // Reverse completed steps in LIFO order
    for (const stepName of this.state.completedSteps.reverse()) {
      const step = this.steps.find(s => s.name === stepName);
      if (step) {
        try {
          console.log(`Compensating step: ${stepName}`);
          await step.compensate();
        } catch (compError) {
          console.error(`Compensation failed for ${stepName}:`, compError);
          // Log for manual intervention
        }
      }
    }
    
    this.state.status = 'failed';
  }
}

// Usage example for order processing saga
async function processOrder(orderData: OrderData) {
  const saga = new SagaOrchestrator();
  
  saga
    .addStep({
      name: 'reserve_inventory',
      execute: () => reserveInventory(orderData.items),
      compensate: () => releaseInventory(orderData.items)
    })
    .addStep({
      name: 'process_payment',
      execute: () => processPayment(orderData.payment),
      compensate: () => refundPayment(orderData.payment)
    })
    .addStep({
      name: 'create_shipment',
      execute: () => createShipment(orderData.shipping),
      compensate: () => cancelShipment(orderData.shipping)
    });
  
  await saga.execute({ order: orderData });
}

Choosing the Right Pattern

Select your communication pattern based on these factors:

Claude Code skills accelerate implementing all these patterns by generating boilerplate, validating implementations, and maintaining consistency across your microservices ecosystem.

Built by theluckystrike — More at zovo.one