DEV Community

Cover image for I Completely Moved from AWS Lambda to Azure Functions Because of This One Feature
Pratik Pathak
Pratik Pathak

Posted on • Originally published at pratikpathak.com

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

You know that moment when you’re deep into building something and you realize you’ve been doing it the hard way all along? That was me, staring at my AWS console at 2 AM, managing yet another collection of Lambda functions stitched together with Step Functions, SQS queues, and DynamoDB tables just to handle what should have been a straightforward workflow.

I’m talking to myself here, really. Because if past-me had known what I know now about Azure Durable Functions, I would have saved myself months of architectural headaches.

The AWS Lambda Reality Check

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 used 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:

  • Store the pending state somewhere
  • Set up an API Gateway endpoint to receive the approval
  • Poll or use callbacks to resume the workflow
  • Handle timeouts (what if they never respond?)
  • 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.

Here’s what a human interaction workflow looks like in Durable Functions:

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

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

        if (!approved)
        {
            return false;
        }
    }

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

    return true;
}
Enter fullscreen mode Exit fullscreen mode

That’s it. The framework handles the state. It handles the timeout. It handles 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 between calls automatically.

2. Fan-Out/Fan-In

Need to process 100 items in parallel and then aggregate the results? Durable Functions does this natively. No managing queues, no tracking completion states manually.

var tasks = new List<Task<int>>();
foreach (var item in items)
{
    tasks.Add(context.CallActivityAsync<int>("ProcessItem", item));
}

var results = await Task.WhenAll(tasks);
var total = results.Sum();
Enter fullscreen mode Exit fullscreen mode

3. Async HTTP APIs

Long-running operations with status polling built right in. The framework gives you status endpoints automatically.

4. Monitor Pattern

Polling workflows that check conditions periodically until something happens. Perfect for waiting for 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:

Week 1-2: Learning Curve

I spent time understanding the orchestrator function model. The key insight is that orchestrator functions replay from the beginning each time they resume, but the framework ensures deterministic execution. Once that clicked, everything made sense.

Week 3-4: Pilot Migration

I picked my simplest workflow and rewrote it in Durable Functions. Deployed it alongside the AWS version, compared results.

Week 5-6: Complex Workflows

Migrated the order processing system. This was the real test. The Durable Functions version was not only shorter but also easier to debug. I could see the orchestration history, replay failed instances, and understand exactly what happened at each step.

Week 7-8: Full Cutover

Decommissioned the AWS infrastructure. Goodbye Step Functions. Goodbye state management DynamoDB tables. Goodbye complexity.

The Real Results

Let me be honest about what actually improved:

60% Less Code

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

I went from 5 AWS services to 2 Azure services (Functions and Storage). The mental model is simpler. Debugging is faster.

Better Observability

The Durable Functions framework gives you a built-in history of every orchestration. I can see exactly when each step executed, what the inputs and outputs were, and where failures occurred.

Cost Reduction

This surprised me. By eliminating Step Functions (which charges per state transition) and reducing DynamoDB usage, my monthly bill dropped by about 40%. Your mileage may vary, but for stateful workflows, the pricing model worked out better.

Faster Development

New features that would have taken a week now take a day. The abstractions are at the right level—not too high that you lose control, not too low that you’re building infrastructure.

Is It All Perfect?

No. Let me be real about the tradeoffs.

The replay model takes getting used to. You have to be careful about non-deterministic code in orchestrators (like calling DateTime.Now directly). There’s a learning curve.

If you’re already invested in the AWS ecosystem with expertise in Step Functions, the migration cost might not be worth it for simple use cases.

And if you’re building genuinely simple, stateless functions, regular Lambda or Azure Functions (without Durable) is perfectly fine.

But if you’re building anything that involves complex workflows, human interaction, long-running processes, or coordination between multiple steps—Durable Functions is a game-changer.

Final Thoughts

I’m talking to myself here, and to anyone who’s been in that 2 AM situation, wrestling with complexity that shouldn’t exist.

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

If you’re hitting the same walls I was hitting with Lambda, give it a look. The worst case is you learn something new. The best case is you find yourself wondering why you didn’t switch sooner.

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

Top comments (0)