Claude Skills Guide

Claude Code for Process Manager Pattern Workflow

The Process Manager pattern (also known as Saga Coordinator or Workflow Orchestrator) is a powerful architectural pattern for coordinating complex multi-step business processes across distributed services. When implemented correctly, it enables reliable execution of long-running workflows while handling failures gracefully through compensation mechanisms. This guide shows you how to use Claude Code to implement robust Process Manager workflows efficiently.

Understanding the Process Manager Pattern

The Process Manager pattern serves as the central coordinator for complex business transactions that span multiple services. Unlike simple request-response patterns, Process Managers maintain workflow state, orchestrate sequential or parallel steps, and handle failures through compensating actions.

Consider an e-commerce order fulfillment process: when a customer places an order, the system must reserve inventory, process payment, initiate shipping, and send notifications. If any step fails, the Process Manager must roll back previously completed steps to maintain data consistency—a concept known as a saga.

The key characteristics of an effective Process Manager include persistent state tracking, retry logic with exponential backoff, timeout handling, and compensation workflows for rollback scenarios.

Setting Up Your Project for Process Manager Development

Before implementing a Process Manager, establish a project structure that separates workflow logic from business operations. Claude Code can help scaffold this architecture quickly.

Create a dedicated module for your workflow orchestration:

// src/workflows/order-fulfillment.ts
interface WorkflowStep {
  name: string;
  execute: () => Promise<StepResult>;
  compensate?: () => Promise<void>;
}

interface StepResult {
  success: boolean;
  data?: any;
  error?: Error;
}

interface WorkflowContext {
  orderId: string;
  customerId: string;
  items: CartItem[];
  status: 'pending' | 'in_progress' | 'completed' | 'failed' | 'compensating';
  completedSteps: string[];
  stepResults: Map<string, any>;
}

This structure separates the workflow definition from the actual business logic, making your code more testable and maintainable. Claude Code can generate the boilerplate for different workflow types and help you implement the orchestration logic.

Implementing the Core Process Manager

The Process Manager acts as the state machine that coordinates your workflow steps. Here’s how to implement one:

// src/process-manager/base-process-manager.ts
class ProcessManager<T extends WorkflowContext> {
  private steps: WorkflowStep[] = [];
  private context: T;
  private retryPolicy: RetryPolicy;

  constructor(context: T, retryPolicy?: RetryPolicy) {
    this.context = context;
    this.retryPolicy = retryPolicy || defaultRetryPolicy;
  }

  addStep(step: WorkflowStep): this {
    this.steps.push(step);
    return this;
  }

  async execute(): Promise<WorkflowResult> {
    this.context.status = 'in_progress';
    
    for (const step of this.steps) {
      try {
        const result = await this.executeWithRetry(step);
        
        if (!result.success) {
          await this.handleFailure(step, result.error);
          return { success: false, error: result.error };
        }

        this.context.completedSteps.push(step.name);
        this.context.stepResults.set(step.name, result.data);
        
      } catch (error) {
        await this.handleFailure(step, error);
        return { success: false, error };
      }
    }

    this.context.status = 'completed';
    return { success: true, context: this.context };
  }

  private async executeWithRetry(step: WorkflowStep): Promise<StepResult> {
    let lastError: Error;
    
    for (let attempt = 1; attempt <= this.retryPolicy.maxAttempts; attempt++) {
      try {
        return await step.execute();
      } catch (error) {
        lastError = error;
        if (!this.retryPolicy.shouldRetry(error) || attempt === this.retryPolicy.maxAttempts) {
          throw error;
        }
        await this.retryPolicy.delay(attempt);
      }
    }
    
    throw lastError!;
  }

  private async handleFailure(step: WorkflowStep, error?: Error): Promise<void> {
    this.context.status = 'compensating';
    
    // Execute compensation in reverse order
    const completedSteps = [...this.context.completedSteps].reverse();
    
    for (const stepName of completedSteps) {
      const completedStep = this.steps.find(s => s.name === stepName);
      if (completedStep?.compensate) {
        try {
          await completedStep.compensate();
        } catch (compensateError) {
          // Log but continue compensation
          console.error(`Compensation failed for ${stepName}:`, compensateError);
        }
      }
    }
    
    this.context.status = 'failed';
  }
}

This implementation provides the foundation for reliable workflow execution with automatic rollback on failure. The retry policy allows you to define custom retry logic for transient failures, while the compensation mechanism ensures your system remains consistent even when errors occur.

Defining Workflow Steps with Compensation

Each workflow step should include both the forward action and the compensation action. Here’s a practical example:

// src/workflows/order-steps.ts
const reserveInventoryStep: WorkflowStep = {
  name: 'reserve_inventory',
  execute: async () => {
    const reservation = await inventoryService.reserveItems(
      context.items,
      context.orderId
    );
    return { success: true, data: reservation };
  },
  compensate: async () => {
    await inventoryService.releaseReservation(context.orderId);
  }
};

const processPaymentStep: WorkflowStep = {
  name: 'process_payment',
  execute: async () => {
    const payment = await paymentService.chargeCustomer(
      context.customerId,
      context.totalAmount
    );
    return { success: true, data: payment };
  },
  compensate: async () => {
    await paymentService.refundCustomer(
      context.customerId,
      context.orderId
    );
  }
};

const initiateShippingStep: WorkflowStep = {
  name: 'initiate_shipping',
  execute: async () => {
    const shipment = await shippingService.createShipment({
      orderId: context.orderId,
      address: context.shippingAddress,
      items: context.items
    });
    return { success: true, data: shipment };
  },
  compensate: async () => {
    await shippingService.cancelShipment(context.orderId);
  }
};

// Compose the workflow
const orderFulfillmentWorkflow = new ProcessManager(context)
  .addStep(reserveInventoryStep)
  .addStep(processPaymentStep)
  .addStep(initiateShippingStep);

This step-by-step approach ensures each operation can be rolled back independently, maintaining system consistency throughout the workflow.

Handling Long-Running Workflows with Persistence

Production Process Managers must persist their state to survive application restarts. Implement state persistence:

// src/process-manager/persisted-process-manager.ts
class PersistedProcessManager extends ProcessManager<WorkflowContext> {
  private storage: WorkflowStorage;

  constructor(context: WorkflowContext, storage: WorkflowStorage) {
    super(context);
    this.storage = storage;
  }

  async saveState(): Promise<void> {
    await this.storage.save({
      workflowId: this.context.orderId,
      status: this.context.status,
      completedSteps: this.context.completedSteps,
      stepResults: Object.fromEntries(this.context.stepResults),
      updatedAt: new Date().toISOString()
    });
  }

  static async resume(workflowId: string, storage: WorkflowStorage): Promise<WorkflowContext> {
    const persisted = await storage.load(workflowId);
    if (!persisted) {
      throw new Error(`Workflow ${workflowId} not found`);
    }
    return {
      orderId: workflowId,
      status: persisted.status,
      completedSteps: persisted.completedSteps,
      stepResults: new Map(Object.entries(persisted.stepResults))
    };
  }
}

Persisting workflow state enables recovery after failures and supports distributed architectures where workflows may be processed by different instances.

Best Practices for Process Manager Implementations

When implementing Process Managers with Claude Code, follow these best practices:

Define clear timeouts for each step to prevent workflows from hanging indefinitely. Use a timeout configuration that accounts for expected processing time plus a reasonable buffer.

Implement idempotency in your step operations to ensure re-execution doesn’t cause duplicate effects. Store operation identifiers and check for duplicates before processing.

Log extensively throughout the workflow execution. Include the workflow ID, current step, input parameters, and results at each stage. This logging proves invaluable for debugging production issues.

Use correlation IDs to track related operations across services. Pass the workflow ID as a correlation ID in all service calls to enable distributed tracing.

Test compensation paths thoroughly by deliberately failing workflows at various points and verifying rollback behavior. This testing reveals gaps in your compensation logic before they cause production issues.

Conclusion

The Process Manager pattern provides a robust foundation for building reliable distributed workflows. By implementing proper state management, retry logic, and compensation mechanisms, you can coordinate complex multi-service transactions with confidence. Claude Code accelerates the development of these patterns by generating boilerplate, suggesting improvements, and helping you implement best practices from the start.

Start with simple workflows and progressively add complexity as your understanding of failure modes improves. The investment in proper Process Manager implementation pays dividends in system reliability and maintainability.

Built by theluckystrike — More at zovo.one