Event-driven backends are how commerce systems absorb spikes without turning every Admin request into a hostage situation. I have built and operated Lambdas for order intake, dispatch, fulfillments, inventory sync, reporting, watermarking pipelines, and scheduled work via EventBridge. Used well, events isolate failure. Used casually, they create mysteries.

Commands vs events vs jobs

Teams blur vocabulary:

  • Command: do this (may be rejected)
  • Event: this happened (past tense)
  • Job: perform work, possibly derived from either

If you treat everything as a fire-and-forget event, you lose the ability to say no. If you treat everything as a synchronous command, you lose isolation. Choose per use case.

Ordering is a wish unless you design for it

Queues do not guarantee global business ordering across entities. Inventory updates for one SKU may race. Fulfillments may arrive before intake finished enriching state if you have multiple topics.

Strategies include:

  • partition keys / per-aggregate FIFO where required
  • version checks / optimistic concurrency
  • reconciliation that assumes disorder

If your business invariant needs strict order, prove the mechanism. Do not assume SQS magic.

Backpressure and fairness

A runaway reporter can starve intake if they share a naive concurrency model. Separate queues, reserved concurrency, and per-function scaling policies are part of domain design.

The “did it happen?” problem

After a timeout, you often do not know whether Shopify/ERP/DB writes committed. That uncertainty is why idempotency and state inspection matter more than heroic retries.

Design handlers so retries converge to the same end state. Design operators so they can answer what already happened without guessing.

Observability is the user interface of events

Metrics on lag, error rate, DLQ depth, and age of oldest message tell you if the business is quietly rotting. Logs without correlation across producer → queue → consumer → external API are folklore.

When I pick events

I pick event-driven workers when:

  • work can be deferred without breaking UX
  • spikes are expected
  • side effects must be isolated
  • retries are safe (or can be made safe)

I keep synchronous paths when the user must know the outcome immediately and the work is bounded.

Events are sharp tools. They cut through coupling, and through your foot if you skip the sheath: contracts, idempotency, and visibility.

← Back to blog