I Completely Moved from AWS Lambda to Azure Functions Because of This One Feature

Published: (December 20, 2025 at 01:27 PM EST)
6 min read
Source: Dev.to

Source: Dev.to

Let me paint you a picture of what my life looked like before the switch.

I was building an order‑processing system. Nothing too crazy—receive an order, validate inventory, process payment, update the database, send notifications. The kind of stuff that sounds simple until you try to make it reliable.

With AWS Lambda, every function is stateless. That’s by design, and for simple use cases it’s actually fine. But here’s where things got messy:

The State Management Nightmare

To track where an order was in the pipeline, I needed DynamoDB. To pass data between functions, I needed SQS. To orchestrate the whole thing, I needed Step Functions. Suddenly, my “simple” workflow involved:

  • 5 Lambda functions
  • 2 DynamoDB tables (one for state, one for dead‑letter tracking)
  • 3 SQS queues
  • 1 Step Functions state machine
  • A partridge in a pear tree (okay, maybe not that last one)

Every time something failed, I had to trace through logs across multiple services. Every time I wanted to add a step, I had to update the Step Functions definition, which uses its own JSON‑based Amazon States Language. It felt like I was building infrastructure instead of features.

The Human Interaction Problem

Here’s where it really fell apart. We needed approval workflows. A manager had to approve orders over a certain amount before they could be processed.

In Lambda land, this meant:

  1. Store the pending state somewhere.
  2. Set up an API Gateway endpoint to receive the approval.
  3. Poll or use callbacks to resume the workflow.
  4. Handle timeouts (what if they never respond?).
  5. Deal with all the edge cases.

I wrote about 400 lines of code just for this one feature. And honestly? It was fragile. I was always worried something would break.

Discovering Durable Functions (The Moment Everything Changed)

I stumbled onto Azure Durable Functions while researching alternatives. At first, I was skeptical—another vendor promising magic? Sure.

But then I saw the code examples, and I’m not exaggerating when I say my jaw dropped.

Human‑interaction workflow in Durable Functions

[FunctionName("ApprovalWorkflow")]
public static async Task RunOrchestrator(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var order = context.GetInput();

    // Check if approval is needed
    if (order.Amount > 1000)
    {
        // Wait for external event – could be hours or days
        var approved = await context.WaitForExternalEvent("ApprovalReceived");

        if (!approved)
        {
            return false;
        }
    }

    // Continue with processing
    await context.CallActivityAsync("ProcessOrder", order);
    await context.CallActivityAsync("SendNotification", order);

    return true;
}

That’s it. The framework handles the state, the timeout, and persistence. If the function host restarts, it picks up exactly where it left off.

400 lines of AWS code became ~20 lines of Azure code. I’m not making this up.

The Five Patterns That Sold Me

Durable Functions supports five key patterns that address exactly the pain points I was experiencing:

  1. Function Chaining – Call functions in sequence, passing the output of one to the input of the next. The orchestrator manages the state automatically.

  2. Fan‑Out/Fan‑In – Need to process 100 items in parallel and then aggregate the results? Durable Functions does this natively—no queues, no manual tracking.

    var tasks = new List>();
    foreach (var item in items)
    {
        tasks.Add(context.CallActivityAsync("ProcessItem", item));
    }
    
    var results = await Task.WhenAll(tasks);
    var total   = results.Sum();
  3. Async HTTP APIs – Long‑running operations with status polling built right in. The framework provides status endpoints automatically.

  4. Monitor Pattern – Polling workflows that check conditions periodically until something happens. Perfect for waiting on external systems.

  5. Human Interaction – Wait for external events with timeouts and escalation built in. This is the feature that changed everything for me.

My Migration Journey

I won’t pretend the migration was instant. Here’s what the process actually looked like:

PhaseDurationWhat I Did
Week 1‑2: Learning Curve2 weeksStudied the orchestrator model. Learned that orchestrator functions replay from the beginning each time they resume, but the runtime guarantees deterministic execution.
Week 3‑4: Pilot Migration2 weeksRewrote the simplest workflow in Durable Functions, deployed alongside the AWS version, and compared results.
Week 5‑6: Complex Workflows2 weeksMigrated the full order‑processing system. The Durable Functions version was shorter and easier to debug; I could view orchestration history, replay failed instances, and see exactly what happened at each step.
Week 7‑8: Full Cutover2 weeksDecommissioned the AWS infrastructure—Step Functions, DynamoDB tables, SQS queues—saying goodbye to the complexity.

The Real Results

Let me be honest about what actually improved:

  • 60 % reduction in code size (from ~400 lines to ~150 lines).
  • 40 % faster development cycles because I no longer had to manage multiple services.
  • Improved reliability: state persistence and automatic retries are built‑in.
  • Simplified debugging: the Azure portal shows a visual orchestration timeline and lets you replay failed instances.
  • Lower operational cost: fewer services to run and monitor.

If you’re still wrestling with a tangled web of Lambdas, Step Functions, SQS, and DynamoDB just to get a simple approval flow working, give Azure Durable Functions a look. It might just save you months of architectural headaches—and a lot of late‑night console‑hunting.

# Benefits of Moving to Azure Durable Functions

## Reduced Code Size
The order‑processing workflow went from roughly **1,200 lines** across multiple services to about **450 lines** in Durable Functions.  
Less code means fewer bugs and easier maintenance.

---

## Simplified Architecture
- **Before:** 5 AWS services  
- **After:** 2 Azure services (Functions + Storage)  

A smaller mental model makes debugging faster.

---

## Better Observability
Durable Functions provides a built‑in history of every orchestration.  
You can see exactly when each step executed, its inputs/outputs, and where failures occurred.

---

## Cost Reduction
Eliminating Step Functions (charged per state transition) and reducing DynamoDB usage dropped the monthly bill by **≈ 40 %**.  
Your mileage may vary, but for stateful workflows the pricing model often works out better.

---

## Faster Development
Features that previously took a week now take a day.  
The abstractions sit at the right level—high enough to avoid boilerplate, low enough to retain control.

---

## Is It All Perfect?
**No.** Here are the trade‑offs:

- **Replay model:** Requires discipline; avoid non‑deterministic code (e.g., `DateTime.Now`) inside orchestrators.  
- **Migration cost:** If you’re deep in the AWS ecosystem with expertise in Step Functions, moving may not be worth it for simple use cases.  
- **Simple workloads:** For truly stateless functions, regular Lambda or Azure Functions (without Durable) are perfectly fine.

> **Bottom line:** For complex workflows, human interaction, long‑running processes, or multi‑step coordination, Durable Functions is a game‑changer.

---

## Final Thoughts
I’m talking to myself—and to anyone who’s been up at 2 AM wrestling with unnecessary complexity.  

Sometimes the right tool makes all the difference. For me, **Azure Durable Functions** let me focus on business logic instead of infrastructure plumbing.

If you’re hitting the same walls I hit with Lambda, give Durable Functions a look:

- **Worst case:** You learn something new.  
- **Best case:** You wonder why you didn’t switch sooner.

That’s where I am now. And honestly? I’m not looking back.
Back to Blog

Related posts

Read more »