Technical Article

Background Jobs Are Contracts, Not Just Async Functions

Moving work to a background queue is easy. Making that queue reliable over time as codebases, schemas, and deployments evolve is the hard part.

A background job crosses architectural boundaries: one part of the system produces it, and another consumes it—often across different processes, deployment cycles, or codebase versions. That operational reality makes every background job an explicit contract.

Anatomy of a Robust Job Contract

A resilient contract defines at least five critical dimensions:

  • A stable job name, ensuring producers and workers agree on the exact task being executed.
  • A payload schema, guaranteeing malformed data fails immediately at the boundary rather than deep within worker logic.
  • A runtime validator or parser, treating incoming message data as untrusted external input.
  • An explicit worker routing target, eliminating ambiguous handlers across consumer pools.
  • A shared producer API, preventing callers from duplicating queue submission details across the codebase.

The Hidden Cost of Implicit Coupling

Without an explicit contract, asynchronous code becomes tightly coupled in subtle, dangerous ways.

Consider what occurs during rolling deployments:

  1. A producer introduces a modified payload structure.
  2. An older worker instance still expects the previous shape.
  3. Automated retries continuously replay the invalid payload.

In this scenario, the queue is operating exactly as designed, yet the system as a whole is failing.

Libraries like BullMQ handle delivery mechanics, and validation libraries like Zod can enforce schemas. But genuine operational reliability comes from treating the message payload as an API contract between decoupled components.

The Contract-First Mental Model

The essential mental model for reliable asynchronous systems is:

Producer → Contract → Queue → Worker

Rather than:

Function → "Run this later"

When background jobs are treated as first-class contracts, versioning, backwards compatibility, and schema evolution become deliberate engineering decisions rather than unexpected production incidents.