Technical
SQS Patterns That Held Up Under Real Traffic
SQS is one of those services that looks trivial on day one and reveals its edges by month three. I've run non-trivial workloads through SQS since May. Three patterns have proven themselves, and one I discarded.
Pattern 1: The Dead Letter Queue Is Not Optional
Every SQS queue I deploy now ships with a DLQ wired up from day one. Without it, poisoned messages retry forever, clog the queue, and rack up Lambda invocations. With a DLQ and a max-receive-count of three, bad messages move aside and the rest of the queue flows.
Pattern 2: FIFO for Ordering, Standard for Throughput
I default to standard queues and reach for FIFO only when ordering matters for correctness, not just aesthetics. Most of my use cases (emails, webhooks, analytics events) tolerate reordering. FIFO's throughput ceiling bites faster than people expect.
Pattern 3: Idempotency at the Consumer
SQS delivers at-least-once. This isn't a bug, it's the contract. Every consumer I write checks a dedup key before processing.
def handle(message):
key = message['dedup_key']
if dedup_table.exists(key):
return # already processed
do_the_work(message)
dedup_table.put(key, ttl=3600)Three lines of idempotency saves you from every duplicate-delivery scenario.
What I Discarded
I tried using SQS for scheduled tasks (delayed messages). It technically works. It's a worse version of EventBridge Scheduler. I moved the scheduling off SQS and let SQS do what it's good at.
See the SQS developer guide.
RELATED READING
The Consulting Shift I Am Making In Year Two
After a year of writing and building, my consulting practice is changing shape. Shorter engagements. Sharper outcomes.
ReadThe Frontend Shift: Shipping Less JavaScript In Year Two
A year ago I reached for Next.js for everything. This year I often reach for nothing.
ReadThe Serverless Lesson I Would Write On A Sticky Note
After a year of shipping serverless projects, one rule explains most of the wins and all of the losses.
Read