Message Queue Design
Queues move work you can afford to do later. Default is at-least-once. Exactly-once is a property of the handler + store, not the broker logo.
For HTTP callbacks, use webhook-design. For sockets, use websocket-design. For API contracts, use api-design.
Workflow
- Work type: command vs event, latency budget, loss tolerance.
- Broker shape: competing consumers (queue) vs fan-out (pub/sub).
- Keying / partition / shard: what must stay ordered, what can race.
- Payload: schema, version, PII, size, idempotency key.
- Producer: transactional outbox if the DB write and publish must not diverge.
- Consumer: visibility timeout / ack, retry + backoff, poison / DLQ, idempotent handler on the event id.
- Failure: replay, rewind, and who pages when the DLQ grows.
- Ops: lag, oldest-unacked, poison rate, schema-compat checks.
Output format
## Queue design: <workflow>
**Broker / pattern:** queue | bus | log
**Delivery promise:** at-least-once (state how you make it safe)
### Topics / queues
…
### Keys / ordering
…
### Producer
outbox? schema?
### Consumer
ack, retry, DLQ, idempotency
### Failure / replay
…
### Metrics
lag, poison, age
Rules
- Do not promise exactly-once from the broker alone.
- Handlers must be idempotent on a stable event id.
- Retry without a DLQ is how you infinite-loop a bad payload.
- Visibility timeout > p99 handler time, or you double-process healthy work.
- Ordering is per key/partition, never global, unless the user accepts a single-threaded consumer.
- PII in payloads needs retention and redaction like any store.
- Do not invent broker-specific settings you cannot see; name the control (retention, DLQ redrive, IAM).
Edge cases
- "Just use Kafka": ask the delivery and replay needs first.
- Huge payloads: store a pointer; do not stuff 50MB into the broker.
- Request/reply over a queue: usually the wrong tool; prefer a sync API with a job id.
- Poison that used to work: version the schema; do not silently drop.
---