Step Handler Best Practices
A comprehensive guide to writing robust, idempotent, and maintainable step handlers in Tasker workflows
Table of Contents
Overview
Step handlers are the core business logic units in Tasker workflows. While the system only requires a process method, following established patterns ensures:
Idempotency: Operations can be safely retried without side effects
Retry Safety: Clear separation between retryable and permanent failures
Maintainability: Consistent structure across your codebase
Testability: Predictable interfaces for comprehensive testing
Core Philosophy
The business logic call must be safe to retry, or intentionally not retryable, in a way that ensures idempotency.
Too many distributed systems enter invalid states due to poorly handled secondary actions. By following these patterns, we prevent such issues.
The Four-Phase Pattern
Every step handler should follow this proven four-phase pattern:
Phase 1: Extract and Validate Inputs
Extract all required data from task context and previous step results
Validate inputs early with
PermanentErrorfor missing/invalid dataNormalize data formats (e.g., symbolize keys)
Fail fast with clear error messages
Phase 2: Execute Business Logic
Perform the core operation (computation, API call, data transformation)
Handle service-specific errors appropriately
Return raw results for phase 3 validation
Phase 3: Validate Business Logic Results
Ensure the operation completed successfully
Check for business-level failures (e.g., payment declined)
Classify errors correctly (
PermanentErrorvsRetryableError)
Phase 4: Process Results (Optional)
Override
process_resultswhen neededFormat and store step results safely
Handle result processing errors as
PermanentError(don't retry business logic)
Implementation Patterns
Basic Step Handler Structure
Input Extraction and Validation Pattern
API Configuration Pattern
For Tasker::StepHandler::Api handlers, configure the base URL and connection settings in the YAML configuration, not in the handler code:
The framework automatically passes this configuration to the handler:
Business Logic Execution Pattern
Result Validation Pattern
Error Handling & Retry Safety
Error Classification Principles
Use PermanentError for:
Missing or invalid input data
Authentication/authorization failures
Business rule violations (insufficient funds, invalid state)
Structural errors (missing required fields in responses)
Use RetryableError for:
Network timeouts and connection errors
Service unavailable (5xx status codes)
Rate limiting (429 status codes)
Temporary resource constraints
Error Classification Examples
Idempotency Principles
Key Concepts
Idempotent Operations: Can be performed multiple times with the same result
Side Effect Isolation: Separate side effects from core business logic
State Verification: Check current state before making changes
Compensation Patterns: Handle partial failures gracefully
Idempotency Implementation Patterns
Base Class Selection
Choose the appropriate base class based on your step handler's purpose:
Tasker::StepHandler::Base
Tasker::StepHandler::BaseUse for: Computational tasks, data transformations, internal operations
Examples: Data validation, calculations, internal service calls
Pattern: Direct business logic execution
Tasker::StepHandler::Api
Tasker::StepHandler::ApiUse for: HTTP API calls, external service integration
Examples: Payment processing, user creation, external notifications
Pattern: HTTP client with automatic retry and circuit breaker support
Configuration: URL and connection settings via YAML
handler_config
YAML Configuration:
Testing Patterns
Comprehensive Test Structure
Examples from the Blog Series
Post 01: E-commerce Reliability Pattern
File: spec/blog/fixtures/post_01_ecommerce_reliability/step_handlers/process_payment_handler.rb
Phase 1: Validates payment method, token, and cart total
Phase 2: Calls MockPaymentService with retry logic
Phase 3: Checks payment status and handles declines vs temporary failures
Phase 4: Formats payment results with transaction details
Key Learning: Payment declines are permanent errors, but rate limiting is retryable.
Post 02: Data Pipeline Resilience Pattern
File: spec/blog/fixtures/post_02_data_pipeline_resilience/step_handlers/extract_orders_handler.rb
Phase 1: Validates date range parameters
Phase 2: Extracts data from MockDataWarehouseService
Phase 3: Verifies extraction completed successfully
Phase 4: Stores extraction metadata and metrics
Key Learning: Data extraction timeouts are retryable, but query errors are permanent.
Post 03: Microservices Coordination Pattern
File: spec/blog/fixtures/post_03_microservices_coordination/step_handlers/create_user_account_handler.rb
Phase 1: Validates user data requirements
Phase 2: HTTP API call to user service with circuit breaker
Phase 3: Handles HTTP status codes and idempotency checks
Phase 4: Processes different response scenarios (created vs already exists)
Key Learning: User conflicts require idempotency checks to determine if it's an error or success.
Post 04: Team Scaling Pattern
File: spec/blog/fixtures/post_04_team_scaling/step_handlers/customer_success/execute_refund_workflow_handler.rb
Phase 1: Validates approval workflow and maps team-specific data
Phase 2: Cross-namespace HTTP API call to payments team
Phase 3: Handles task creation status from remote team
Phase 4: Formats delegation results with correlation tracking
Key Learning: Cross-team coordination requires careful data mapping and correlation tracking.
Common Anti-Patterns to Avoid
❌ Don't Do This
✅ Do This Instead
Summary
Following these patterns ensures:
Robust Error Handling: Clear classification of permanent vs retryable errors
Idempotent Operations: Safe retry behavior without side effects
Maintainable Code: Consistent structure across all step handlers
Comprehensive Testing: Predictable interfaces for thorough test coverage
Production Readiness: Battle-tested patterns from real-world usage
Remember: The goal is not rigid adherence to rules, but building reliable, maintainable distributed systems that handle failures gracefully.
This guide is maintained as a living document based on patterns proven in the Tasker blog examples and production usage.
Last updated