The Difference Between a Message Queue and an Event Stream

by Eric Hanson, Backend Developer at Clean Systems Consulting

The Conflation Problem

"We need a queue" is how the conversation usually starts. Sometimes what the team actually needs is a queue — SQS, RabbitMQ. Sometimes they need an event stream — Kafka, Kinesis. These systems have overlapping use cases at the surface and divergent behavior underneath. The conflation causes real problems: teams build on SQS and discover they need replay capability; teams adopt Kafka and discover they have taken on operational complexity for a use case that SQS would have handled more simply.

The core difference is in consumer semantics and retention model.

Message Queues: Consume Once, Then Delete

A message queue (SQS, RabbitMQ) delivers a message to one consumer, waits for acknowledgment, and deletes the message. The message is consumed exactly once (with at-least-once delivery guarantees — if acknowledgment fails, the message is redelivered). The queue holds messages only until they are consumed or a retention period expires.

This model is appropriate for task distribution: background jobs, work that needs to be done exactly once by one worker. Sending an email, resizing an image, processing a payment. Each task needs to happen once. Multiple workers consume from the same queue, each getting a different task. The queue distributes work.

What a message queue does not give you: the ability to replay messages, multiple independent consumers for the same message, or a persistent log of everything that was processed.

Event Streams: Persistent Log, Multiple Consumers

An event stream (Kafka, Kinesis) is a durable, ordered log of events. Producers append events to the log. Consumers read from the log at their own offset — each consumer group maintains an independent position in the log. Events are not deleted when consumed; they are retained for a configured period (hours, days, weeks) or forever (with Kafka's log compaction).

This model is appropriate when: multiple independent services need to react to the same event, you need the ability to replay events (for new consumers, for backfill, for debugging), or you need a persistent audit trail of state changes.

# Message Queue semantics (SQS):
Producer -> [message] -> Queue -> Consumer A (message deleted after ack)
                               -/ Consumer B (never receives the message)

# Event Stream semantics (Kafka):
Producer -> [event] -> Topic/Partition -> Consumer Group A (offset advances)
                                       -> Consumer Group B (independent offset)
                                       -> Consumer Group C (can replay from beginning)
# Event persists in the log until retention expires

The Operational Difference

SQS is fully managed. You send messages, they arrive at your consumer. No brokers to run, no partition management, no broker replication to configure. Throughput scales automatically. The operational overhead is minimal.

Kafka requires running brokers (or using a managed service like Confluent Cloud or MSK). Partition count affects parallelism and must be planned. Consumer group offsets must be managed. Log retention and compaction must be configured. Storage grows with retention period and write volume. The operational overhead is significant — a Kafka cluster is a system to operate, not just a service to call.

This is not an argument against Kafka. It is an argument that Kafka's operational overhead should be justified by the use case.

Choosing Between Them

Use a message queue (SQS, RabbitMQ) when:

  • Each message needs to be processed by exactly one worker
  • You do not need replay capability
  • The workload is task distribution (background jobs)
  • Operational simplicity is a priority

Use an event stream (Kafka, Kinesis) when:

  • Multiple independent consumers need to process the same event
  • You need replay capability (new consumers, backfill, debugging)
  • You need an ordered, persistent log of events
  • Event sourcing or CQRS patterns are in use

A common hybrid: use SQS for task distribution (one worker per task), and use Kafka for domain events that multiple services need to react to. They serve different purposes. Run both if you have both use cases. Do not use Kafka for everything because it is more powerful — the power comes with a cost.

Scale Your Backend - Need an Experienced Backend Developer?

We provide backend engineers who join your team as contractors to help build, improve, and scale your backend systems.

We focus on clean backend design, clear documentation, and systems that remain reliable as products grow. Our goal is to strengthen your team and deliver backend systems that are easy to operate and maintain.

We work from our own development environments and support teams across US, EU, and APAC timezones. Our workflow emphasizes documentation and asynchronous collaboration to keep development efficient and focused.

  • Production Backend Experience. Experience building and maintaining backend systems, APIs, and databases used in production.
  • Scalable Architecture. Design backend systems that stay reliable as your product and traffic grow.
  • Contractor Friendly. Flexible engagement for short projects, long-term support, or extra help during releases.
  • Focus on Backend Reliability. Improve API performance, database stability, and overall backend reliability.
  • Documentation-Driven Development. Development guided by clear documentation so teams stay aligned and work efficiently.
  • Domain-Driven Design. Design backend systems around real business processes and product needs.

Tell us about your project

Our offices

  • Copenhagen
    1 Carlsberg Gate
    1260, København, Denmark
  • Magelang
    12 Jalan Bligo
    56485, Magelang, Indonesia

More articles

Your Pipeline Is Flaky and That Is a Bigger Problem Than You Think

Flaky pipelines don't just waste time — they erode the team's trust in automation, leading developers to ignore failures that matter. Fixing flakiness is a prerequisite for meaningful CI, not an optional cleanup task.

Read more

How to Know When Your Team Needs a Tech Lead

Sometimes a team can feel like it is running fine, but small problems keep piling up. A Tech Lead can be the person who stops these issues before they become big.

Read more

Docker Compose Is Not Just for Local Development

Most teams use Docker Compose only to spin up local dependencies, then switch to a completely different toolchain for staging and production. That gap is narrower than you think, and Compose handles more production-adjacent use cases than its reputation suggests.

Read more

Why Design Patterns Are Useful Until They Become an Obsession

Design patterns are solutions to recurring problems — but the pattern is only justified when the problem actually recurs. Applying them in advance is how you create complexity that nobody asked for.

Read more