When you first build an AI application, the interaction looks like this:
User Request → Build Prompt → Call Model → Return Response
This works perfectly because each request is independent. If it fails, you just try again. Nothing carries over between calls.
Now imagine running that same logic across 10,000 records simultaneously:
10,000 rows → 10,000 model calls → some succeed, some fail,
some hit rate limits, some timeout, worker crashes at row 8,432...
Suddenly you face questions your prototype never had to answer:
Key Insight: You have stopped debugging AI behavior and started debugging distributed execution.
Before moving on, ask yourself: Why does the single-request model break down at scale? (Answer: because it assumes independence, but large executions have shared state, partial progress, and interdependencies.)
Consider this scenario:
Background job processes rows 1 → 8,431 ✓
Worker crashes during row 8,432 ✗
System restarts...
Your instinct says: "Row 8,432 failed. Retry row 8,432."
But look deeper:
| Question | Why It Matters |
|---|---|
| Did the model generate a response? | Maybe it succeeded but crashed before saving |
| Did the output reach the database? | Partial writes create inconsistent state |
| Was the status updated? | System may not know what "done" means |
| Is retry safe? | Could duplicate already-completed work |
The failed row is not the real problem. It is simply where uncertainty becomes visible.
The application has no explicit representation of the execution itself. It only has:
This is like trying to reconstruct a conversation from scattered sticky notes after someone threw them in the air.
Before you can fix failures, you must answer: What should the system actually recover?
❌ Too Broad: Retry the entire 10,000-row job
→ Repeats thousands of successful calls
→ Wastes model quota
→ Risks overwriting correct outputs
❌ Too Granular: Retry every tiny instruction independently
→ Massive orchestration overhead
→ Hard to manage and reason about
Model your execution as a hierarchy that mirrors how users think about the work:
Column Run (user's overall request)
└── Batch / Row (meaningful execution unit)
└── Activities (atomic operations)
├── Resolve inputs
├── Build prompt
├── Call model
├── Validate response
├── Write output
└── Update progress
Why this works:
Core Principle: The right retry boundary is the smallest meaningful unit of recoverable work.
Map this to something familiar: If you were processing 1,000 customer invoices, what would be your hierarchy? (Hint: Batch run → Individual invoice → Line item operations)
Once you have smaller recoverable units, a new problem emerges:
If a worker restarts, where does the next worker find out what already completed?
Worker A processes rows 1-8,431
Worker A crashes
Worker B starts
Worker B reads logs + database + timestamps
Worker B tries to reconstruct what happened
Worker B makes assumptions...
Worker B gets it wrong in edge cases
This works when failures are rare. It becomes increasingly fragile as executions grow longer.
Instead of asking "How does a worker remember where it left off?", ask:
"What if the execution remembered its own history, independent of any worker?"
Traditional Model: Durable Model:
┌─────────────┐ ┌─────────────────────────┐
│ Worker │ │ Execution History │
│ (holds │ │ (persisted externally) │
│ state) │ │ │
│ crashes ✗ │ │ Worker A reads it ✓ │
└─────────────┘ │ Worker A crashes │
│ Worker B reads it ✓ │
│ Execution continues ✓ │
└─────────────────────────┘
The worker becomes replaceable. The execution does not.
Column Execution = Parent Workflow (durable, persisted)
└── Row/Batch = Child Workflow (own retry behavior)
└── Model Call = Activity (retry policy defined here)
If worker disappears:
→ Another worker picks up from persisted execution history
→ No reconstruction needed
→ No guessing about what completed
| Metric | Before Durable Workflows | After Durable Workflows |
|---|---|---|
| Failure rate (10 users, 200 rows) | ~90% | 0% |
| Recovery method | Manual reconstruction | Automatic from history |
| P95 completion time | Baseline | ~60% improvement |
Now that execution is durable and boundaries are defined, retries become precise:
Transient model timeout on Row 47:
→ Retry ONLY the activity that timed out
→ Row 47 resumes from last checkpoint
→ Rows 1-46 untouched ✓
→ Model quota not spent twice ✓
Validation failure on Row 23:
→ Isolated to Row 23
→ Other rows continue unaffected ✓
Process Batch:
├── Row 1 → Success → Checkpoint ✓
├── Row 2 → Success → Checkpoint ✓
├── Row 3 → Failure → Retry (up to 10x, exponential backoff)
│ ├── Attempt 2 → Success → Checkpoint ✓
└── Row 4 → Success → Checkpoint ✓
If worker crashes after Row 2's checkpoint:
→ Resume from Row 3 (not Row 1)
→ Row 3 retries remaining attempts
→ Rows 1-2 never re-executed
Every activity should define:
| Component | Purpose | Example |
|---|---|---|
| Max attempts | Prevent infinite loops | 10 retries |
| Backoff strategy | Avoid thundering herd | Exponential |
| Non-retryable errors | Skip hopeless retries | Invalid API key |
| Idempotency | Safe to repeat | Write only if not exists |
Rule Before Implementing Retries: Decide what your system is allowed to repeat before deciding how to repeat it.
A single status indicator tells users and engineers nothing useful:
❌ "Running..."
→ Is it progressing? Stalled? Retrying?
→ Nobody knows.
Your observability model should match your execution hierarchy:
Worksheet Level: "3 of 5 columns complete"
└── Column Level: "8,200 of 10,000 rows complete"
└── Row Level: "Row 47: Retrying (attempt 2/10)"
└── Cell Level: "Value generated ✓ / Failed ✗ / Pending..."
Why this matters:
Principle: Progress visibility should mirror the same execution boundaries that define recovery. When they stay aligned, failures become understandable instead of mysterious.
Most teams start AI architecture discussions here:
❌ Old Questions:
"Which model should we use?"
"How should we write the prompt?"
"What temperature setting works best?"
Production AI demands these questions instead:
✓ New Questions:
"What is the smallest meaningful unit of recoverable work?"
"What state must survive a worker restart?"
"Which operations are safe to retry?"
"How will users know what completed vs. what needs attention?"
Imagine execution reaches row 8,432 before something unexpected happens.
| Your First Instinct | What It Means |
|---|---|
| "Rerun everything from the start" | You are still designing around model execution |
| "What state survived the failure?" | You have started designing for production |
PROTOTYPE AI PRODUCTION AI
───────────────── ──────────────────────────────
Single request Long-running workflow
Independent calls Coordinated execution
Stateless Durable state
Retry = start over Retry = resume from boundary
"Did model respond?" "What state survived?"
One status Layered observability
Worker = execution Execution > any single worker
1. EXECUTION HIERARCHY
Define meaningful units at every level
2. DURABLE STATE
Execution history lives outside any single process
3. ALIGNED RETRIES
Retry boundaries match execution boundaries
4. LAYERED OBSERVABILITY
Progress visibility matches execution structure
The model generates intelligence. Your execution model determines whether that intelligence can be trusted.
Choosing a better model improves the quality of your outputs. Designing durable workflows determines whether those outputs survive to reach your users reliably at scale.