Events, Messages & Commands: The Concepts That Make or Break Your Serverless Architecture

You might have created a Lambda function that "handles events." But take a moment to question yourself about what an event actually is.
Let's forget the object that you can access on the lambda, and think of its concept: what makes something an event, and not a command, or a message.
In serverless, I believe knowing this concept matters a lot. The whole ecosystem is built on a deeply event-driven model. EventBridge, SQS, SNS, DynamoDB Streams, and S3 notifications all depend on events.
In this post, we'll return to the basics. We'll explain what events really are, how they differ from commands and messages, and why these differences matter in every serverless system you create.
What is an event
A few years ago, during a talk by James Eastham, I learned something crucial: an event is a fact and cannot be undone. Exactly, you can't reverse an event. Consider writing a post for this blog; once you publish it, the action is irreversible. The post.published event has already been triggered.
You might wonder: if I delete the post, have I undone the action? Not quite. You haven't reversed the publication; instead, you've added another event to the sequence.
That's the essence of an event. In simple terms, an event represents an action that has occurred in the real world within your system.
What is a Command
If an event is something that has happened, a command is something you're asking to happen. It's a request, not a fact. And unlike events, commands can be rejected.
Think of it this way: when a user clicks "Publish" on your blog editor, your frontend might send a PublishPost command to your backend. That command can fail. The post might not meet validation rules, the user might not have the right permissions, or the system might be temporarily unavailable. The command is an intention, not a truth.
This distinction has real architectural consequences. Commands generally have an intended recipient. You don't broadcast a command to anyone who might be listening. You send it to the one service or function responsible for handling it. There's an implicit contract: someone is expected to act on it.
In serverless terms, an SQS queue carrying a ResizeImage instruction is a good example of a command channel. One producer, one consumer, one clear responsibility.
What is a Message
A message is the broadest of the three. Both events and commands travel as messages. The word "message" tells you about the transport, not the intent.
This is where a lot of confusion creeps in. Developers see SNS delivering a payload and call it "just a message." Technically, yes. But what matters architecturally is what's inside. Is it announcing something that happened, or requesting something to be done?
Getting that wrong leads to systems where consumers start making assumptions they shouldn't. A consumer that receives an event shouldn't be the one deciding whether the action was valid. That ship has sailed. But a consumer that receives a command absolutely should validate it before acting.
Why These Differences Matter in Serverless
In a distributed architecture, the distinction between events and commands changes how you design your application, how you deal with errors, and how do you handle a retry logic.
With events, every listener is an observer. They react to facts. If a user.registered event triggers a welcome email Lambda and that function fails, you don't "undo" the registration — you retry the email. The event remains true regardless.
With commands, the linesteners are executors. They own the outcome. A failed ProcessPayment command is not something you silently retry without careful thought. The intent hasn't been fulfilled, and that matters.
EventBridge is a great example of an event bus done right: it's designed around broadcasting facts to multiple consumers. SQS, on the other hand, lends itself naturally to commands. It's point-to-point, with visibility timeouts and dead-letter queues that reflect the expectation that someone must handle this.
Conclusion
Understanding the difference between events, commands, and messages is more than academic — it's foundational to building reliable, scalable serverless systems.
Events are immutable facts about things that have already happened; commands are intent to perform an action; messages are the vehicles that convey either. Treating them correctly changes how you design APIs, choose services, handle failures, and reason about system behavior.
Key takeaways and practical guidance:
Name things clearly: events in past tense (e.g.,
post.published), commands as imperatives (e.g.,createPost), messages as contextual envelopes.Model events as immutable facts: persist them, append rather than overwrite, and use them to drive downstream state and side effects.
Use commands when you need explicit intent and control over execution (and choose queuing patterns that preserve ordering and retries).
Expect duplicates and out-of-order delivery in distributed systems: make consumers idempotent and design for eventual consistency.
Keep schemas explicit and versioned; consider a registry or strict contracts for producers and consumers.
Pick the right tool for the job:



