~13 min
Two services that call each other directly and wait for a response are tightly coupled: if one slows down or fails, the other feels it immediately. Amazon SQS breaks that coupling with a durable message queue sitting between them. A producer writes a message and moves on; a consumer pulls messages at its own pace. When a consumer receives a message, that message becomes invisible to other consumers for the visibility timeout — long enough, ideally, for the consumer to finish processing and delete it. If the consumer never deletes it, the message reappears for someone else to try.
A message that keeps failing shouldn't loop forever. A dead-letter queue (DLQ), configured through a redrive policy, catches any message that has been received more times than a maximum you set, so you can inspect and debug it without it blocking the main queue. Standard queues give the highest throughput with at-least-once delivery and best-effort ordering; FIFO queues trade some throughput for exactly-once processing and strict ordering — the right choice when message order actually matters, like a sequence of account transactions.
bash
aws sqs set-queue-attributes \
--queue-url https://sqs.us-east-1.amazonaws.com/123456789012/orders \
--attributes '{"RedrivePolicy":"{\"maxReceiveCount\":\"5\",\"deadLetterTargetArn\":\"arn:aws:sqs:us-east-1:123456789012:orders-dlq\"}"}'SQS pairs one producer with one logical set of consumers pulling from the same queue. When several independent systems all need to react to the same event, Amazon SNS is the better fit. SNS is publish/subscribe: a publisher sends one message to a topic, and SNS delivers a copy to every current subscriber — SQS queues, Lambda functions, HTTP endpoints, email, and more. This is the fan-out pattern: the order service publishes "order placed" once, and email, inventory, and analytics each subscribe their own SQS queue to that topic. Adding a fourth subscriber later is just a new subscription — the order service's code never changes.
Handling the resulting request volume is a job for an Application Load Balancer distributing traffic across an EC2 Auto Scaling group. A target tracking scaling policy picks a metric (like average CPU utilization) and a target value, and Auto Scaling adds or removes instances to hold that target — much like a thermostat. Auto Scaling always leans toward availability: it scales out the moment any policy calls for more capacity, but scales in only once every policy agrees it's safe to.
Once traffic is decoupled and load-balanced, you still have to decide what runs the code. Containers, orchestrated by Amazon ECS or Amazon EKS, give you control over the runtime and are a natural fit when you're migrating an existing containerized application or need to run many related services together. Serverless compute — AWS Lambda, or containers run through AWS Fargate without managing servers — scales from zero and bills only while code is actually running, which suits workloads that are idle for long stretches and then burst unpredictably.
For a process with several steps that depend on each other's outcome — charge a card, then reserve inventory, then schedule a shipment, rolling back cleanly if any step fails — AWS Step Functions coordinates the sequence as a state machine, so the coordination logic lives in one place instead of being scattered across each service's error handling.
No — publishing to a topic doesn't require the publisher to know about subscribers, so a new subscription doesn't touch the order service.