Table of Contents
Introduction
What Are Durable Functions?
The Three Types of Functions in Durable Functions
Real-World Scenario: Automating Insurance Claims Processing
Implementation Walkthrough
Best Practices for Enterprise Use
Conclusion
Introduction
In modern cloud-native architectures, orchestrating long-running, stateful workflows across distributed systems is a non-trivial challenge. Traditional serverless functions are stateless and short-lived—great for event-driven tasks, but ill-suited for multi-step business processes that require coordination, retries, human approvals, or external system callbacks.
Enter Durable Functions—an extension of Azure Functions that brings workflow orchestration to the serverless world. As a senior cloud architect who’s designed mission-critical systems for Fortune 500 insurers and fintechs, I’ve seen firsthand how Durable Functions transform complex operational workflows into resilient, scalable, and observable pipelines.
But to wield this power effectively, you must understand its core building blocks: the three types of functions that form every Durable Functions application.
What Are Durable Functions?
Durable Functions is a pattern built on top of Azure Functions that enables you to write stateful workflows in code—without managing infrastructure or state storage. Under the hood, it uses the Durable Task Framework, which persists execution history to Azure Storage and replays function logic as needed to maintain consistency.
This model eliminates the need for external workflow engines or custom state-tracking databases—critical for enterprises demanding auditability, idempotency, and cost efficiency.
The Three Types of Functions in Durable Functions
Every Durable Functions solution comprises exactly three function types, each with a distinct role:
Client Function
The entry point. Triggered by HTTP, queue messages, or timers, it starts a new orchestration instance by calling DurableOrchestrationClient.start_new()
. It returns an instance ID immediately—enabling asynchronous initiation without blocking.
Orchestrator Function
The brain. This function defines the workflow logic: sequencing, parallelism, error handling, and human-in-the-loop steps. Crucially, it must be deterministic—no random calls, direct I/O, or non-replayable operations. All external interactions are delegated to Activity Functions.
Activity Function
The worker. These are standard stateless functions that perform I/O-bound tasks: calling APIs, querying databases, sending emails, or invoking third-party services. They are invoked exclusively by the Orchestrator and return results back to it.
This separation enforces clean architecture: the Orchestrator manages control flow, while Activity Functions handle side effects.
Real-World Scenario: Automating Insurance Claims Processing
Let’s move beyond theory. Imagine you’re the cloud architect at Veridian Insurance, processing 10,000+ auto claims daily. Each claim requires:
Fraud risk scoring via an external AI service
Damage assessment from a mobile app upload
Adjuster review (human approval)
Payment processing
Customer notification
This workflow spans minutes to days, involves external systems, and requires audit trails.
![PlantUML Diagram]()
Using Durable Functions:
The Client Function is triggered when a customer submits a claim via mobile app (HTTP POST).
The Orchestrator Function coordinates the entire lifecycle:
Calls Activity Functions in parallel for fraud check and image analysis
Waits for adjuster approval (using wait_for_external_event
)
Processes payment only after approval
Retries failed steps with exponential backoff
Activity Functions encapsulate each integration:
call_fraud_api()
analyze_damage_images()
send_approval_request()
process_payment()
If the payment gateway is down, the orchestrator retries only that step—without restarting the entire claim. The customer sees real-time status via the instance ID returned at submission.
Implementation Walkthrough
Here’s a simplified, production-ready implementation in Python (Azure Functions v4+):
import azure.functions as func
import azure.durable_functions as df
# --- Client Function ---
def main(req: func.HttpRequest, starter: str) -> func.HttpResponse:
client = df.DurableOrchestrationClient(starter)
instance_id = await client.start_new("ClaimsOrchestrator", None, req.get_json())
return client.create_check_status_response(req, instance_id)
# --- Orchestrator Function ---
def ClaimsOrchestrator(context: df.DurableOrchestrationContext):
claim_data = context.get_input()
# Parallel fraud & damage checks
fraud_task = context.call_activity("call_fraud_api", claim_data)
damage_task = context.call_activity("analyze_damage_images", claim_data)
fraud_result, damage_result = yield context.task_all([fraud_task, damage_task])
if fraud_result["risk_score"] > 0.8:
return {"status": "rejected", "reason": "high_fraud_risk"}
# Wait for human approval (up to 72 hours)
approval = yield context.wait_for_external_event("ApproverDecision", timeout=259200)
if not approval.get("approved"):
return {"status": "denied"}
# Process payment
payment_result = yield context.call_activity("process_payment", claim_data)
yield context.call_activity("notify_customer", {"claim_id": claim_data["id"], "status": "paid"})
return {"status": "completed", "payment_id": payment_result["id"]}
# --- Activity Functions (examples) ---
def call_fraud_api(context: df.ActivityContext):
claim = context.get_input()
# Call external fraud API (omitted for brevity)
return {"risk_score": 0.25}
def process_payment(context: df.ActivityContext):
claim = context.get_input()
# Integrate with payment processor
return {"id": "pay_98765", "amount": claim["amount"]}
![asd]()
![vcd]()
Error handling, logging, and retry policies are omitted for clarity but are essential in production.
Best Practices for Enterprise Use
Never perform I/O in Orchestrators: Violates determinism and breaks replay semantics.
Use eternal orchestrations for recurring workflows (e.g., nightly batch jobs).
Leverage sub-orchestrations to modularize complex workflows.
Monitor with Application Insights: Track instance lifetimes, failures, and replay counts.
Secure external events: Validate payloads and use instance-specific event names.
Conclusion
Durable Functions solve a critical gap in serverless computing: stateful coordination at scale. By mastering its three function types—Client, Orchestrator, and Activity—you unlock the ability to model real-world business processes with resilience, visibility, and minimal operational overhead.
In industries like insurance, healthcare, or logistics—where workflows span systems, humans, and time—this pattern isn’t just convenient; it’s transformative. As cloud architects, our job is to abstract complexity while ensuring reliability. Durable Functions deliver exactly that.
Start small. Automate one approval chain. Then scale to mission-critical operations. The stateful future of serverless is here—and it’s durable.