Article Architecture

The AI Queue & Background Job Migration Plan

As applications scale, synchronous HTTP handlers eventually hit a wall. Video processing, PDF generation, LLM API calls, and bulk database exports trigger gateway 504 timeouts and freeze user interfaces. Moving heavy work into background queues feels like an easy win, but asking an AI assistant to extract a function into a background worker without a migration plan usually creates zombie jobs, double-executions, and unhandled retry loops.

Last reviewed: Aug 2 2026

A dark technical diorama on a wet reflective floor with floating cyan data blocks queued up before entering a heavy black worker pipe glowing warm amber.
Moving heavy synchronous tasks into background queues protects your web handlers, but idempotency and state boundaries are what keep workers from running wild.

TL;DR

Do not ask AI to "just move this endpoint to a background worker." Synchronous HTTP request-response handlers have clean failure boundaries; asynchronous queues introduce distributed state. A safe background job migration requires five steps: isolating the payload, enforcing idempotency keys, configuring explicit retry backoffs with dead-letter queues, designing state visibility (polling vs WebSockets), and verifying queue backpressure before cutting over.

Which plan do you need?

This page covers the first move: work that runs inside an HTTP request today and needs to run in a queue instead. If you already have a queue and are changing its payloads, workers, or topology, the problem is different — messages enqueued by the old code are still waiting when the new code deploys. That case is covered in The AI Background Job Migration Plan.


1. The Synchronous Timeout Trap

When an endpoint performs heavy computation or calls external APIs directly during an HTTP request, it relies on a simple assumption: every hop between the browser and your code stays connected until the server finishes. In production, something upstream gives up first. Nginx closes an idle upstream read after its default proxy_read_timeout of 60 seconds. Cloudflare returns a 524 when the origin sends no response within its default proxy read timeout of 125 seconds, adjustable only on Enterprise plans. Managed platform routers and load balancers add their own ceilings, often tighter than both.

Do not tune your architecture to a specific number. The number is whichever limit in the chain is smallest, it is set by infrastructure you may not control, and it changes when someone puts a new proxy in front of the app. Work that has no bounded upper duration does not belong in a request-response cycle at any timeout value.

When you prompt an AI coding assistant to fix a request timeout, its default impulse is often to increase server timeout limits or wrap the execution in a quick setTimeout or fire-and-forget background promise. Fire-and-forget logic inside a web process is dangerous: if the server restarts or deploys a new release, in-flight jobs vanish without a trace.

Moving to a dedicated message queue (such as Redis/BullMQ, SQS, or Celery) decouples the web tier from heavy execution. However, extracting synchronous code into an asynchronous worker alters how errors, retries, and return values behave.


2. Phase 1: Payload Isolation & Job Dispatch

The first migration phase requires refactoring the API endpoint so it only validates inputs, creates a tracking record in the database, enqueues a job payload, and immediately returns a 202 Accepted response to the client.

A common mistake when instructing AI to write the dispatch code is passing entire database objects or large file blobs directly in the queue message. Queue payloads should remain lightweight references.

DO: Enqueue IDs and Minimal Metadata
// Good queue payload
{
  "jobId": "job_984f1a20",
  "documentId": "doc_102",
  "requestedBy": "usr_402",
  "attempt": 1
}

If you pass full records in the queue payload, the worker may process stale data if the record was updated between enqueue time and execution time. Pass entity IDs so the background worker fetches the latest state directly from the database upon execution.


3. Phase 2: Mandatory Idempotency & State Checks

Distributed queues guarantee at-least-once delivery. Network blips or worker restarts mean the same job payload will eventually run more than once. If worker execution is not idempotent, users get double-billed, duplicate emails are sent, or corrupted records are written to the database.

Before letting an AI assistant write worker handler functions, require it to include an idempotency check and an explicit state machine transition:

If a worker receives a job whose status is already Completed or currently Processing under an active lock, it should log a duplicate warning and return cleanly without re-executing the heavy side effects.


4. Phase 3: Retry Policies & Dead-Letter Queues (DLQ)

AI-generated workers frequently lack granular error categorization. If an external API returns a 401 Unauthorized error due to invalid credentials, retrying the job 10 times with exponential backoff will only flood logs and burn rate limits.

Distinguish between transient errors (network timeout, rate limit 429, temporary DB connection loss) and fatal errors (invalid schema, malformed payload, 401/403 auth failure). Transient errors should trigger retries with jittered exponential backoff; fatal errors must fail fast and move directly to a Dead-Letter Queue (DLQ).

Worker Error Boundary Pattern
try {
  await processDocument(job.data.documentId);
} catch (error) {
  if (isTransientError(error)) {
    // Retry with backoff
    throw error;
  } else {
    // Non-retryable error: record failure and move to DLQ
    await markJobFatal(job.data.jobId, error.message);
    return;
  }
}

5. Phase 4: Frontend State Visibility & Polling

When an API endpoint changes from returning final data to returning 202 Accepted with a jobId, the frontend UI must adapt. Asking AI to rewrite the frontend without guidelines often leads to aggressive 500ms polling loops that overwhelm web servers.

Provide explicit rules for the frontend client implementation:


6. Phase 5: Verification & Cutover Runbook

Before deploying your queue migration to production, test the async architecture under real stress:

  1. The Kill Test: Force-kill a worker process halfway through execution. Verify that another worker picks up the job and that idempotency guards prevent duplicate side effects.
  2. The Backpressure Test: Enqueue 1,000 jobs simultaneously. Verify that worker concurrency limits protect downstream databases from connection pool exhaustion.
  3. The Poison Pill Test: Submit an intentionally malformed payload. Verify that it routes to the DLQ after configured retries without halting the queue processor.

With idempotency keys in place, transient retry boundaries defined, and user feedback mechanisms active, migrating synchronous endpoints to background queues becomes a controlled, repeatable architectural upgrade.


Copy-Paste Prompt: Queue Extraction Analysis

Prompt

"Plan the move of this synchronous endpoint into a background queue before changing any files. Endpoint and current handler: [path or code]. Queue and worker runtime available: [Redis/BullMQ, SQS, Celery, or unknown]. What the caller does with the response today: [details]. Side effects the handler performs: [emails, payments, writes, external calls]. First list every side effect that is not safe to run twice, and every piece of state the handler reads from the request rather than the database. Then output: (1) the validation that stays in the request and the work that moves, (2) the minimal job payload as IDs only, (3) the tracking record and its state transitions, (4) the idempotency key and the exact duplicate check the worker runs first, (5) per-error classification into transient retry or fatal DLQ, with the backoff, (6) the client contract for the 202 response and the status endpoint, (7) the polling or event schedule and its timeout, and (8) the kill, backpressure, and poison-pill tests I should run before cutover. Do not write the worker yet. Mark anything you had to assume about my infrastructure as an assumption."

Resolve the assumptions before asking for an implementation. Then migrate one endpoint at a time and keep the synchronous path available behind a flag until the queued path has survived the three tests above under real load.


Related reading

Continue with The AI Background Job Migration Plan for changing a queue you already run, The AI API Contract Change Plan for the 202 Accepted response change, The AI Performance Fix Plan for deciding whether the work needs a queue at all, and AI Observability for monitoring queue depth and worker failures.


Back to Home