When something happens in your system - an order placed, a user invited, an entity archived updated — emit an event.
Always.
It doesn’t matter if you think you’ll use it later or not.
This is complimentary to having an "audit log" db table.
Send Everything to Pub/Sub
Push all events into a message bus like Pub/Sub. (My preference for DX, native bigquery destinations and getting the job done is GCP pub/sub)
Give each one as much contextual data as makes sense. Don’t overthink schema perfection early.
It’s better to have a rich event history than a minimal one that needs patching later.
Every event should describe what happened, when, and with what metadata.
Then let it flow downstream.
I usually fan these events into BigQuery. It becomes the historical ledger of the system.
Even if you never query it, the data accumulates quietly.
One day, someone will want to know how many users upgraded within 30 seconds of a push, or how long a device stayed offline.
If you send events, the answer will already exist.
Why It Matters
-
Analytics
You get instant access to time series data across everything.
No extra tracking layer needed. -
Extensibility
Any teammate can build a new service off the event stream.
Want to send a webhook when a payment fails? Just subscribe. -
Side Effects as Code
Internal services can react asynchronously — log, update, notify — without coupling. -
Future-Proofing
You can build real-time dashboards, train models, or detect anomalies later, all from the same stream. You have ALL historical timeseries data from day 1, and its basically free since events and bigquery storage is dirt cheap.
Setups that have worked for me
- Application emits JSON events to Pub/Sub
- Pub/Sub → BigQuery via a subscription that always lands in bq.
- Optional consumers subscribe to topics for side effects
No complex pipelines. Just events flowing downstream, waiting to be useful.
What Makes a Good Event
{
"event_name": "user_invited",
"timestamp": "2025-01-01T00:00:00Z",
"event_data": {
// whatever data you want to store for that event
// unique attributes for that event
// you probably dont need to store the previous state, just "changed" matters. in the data you can compare each change.
},
"event_context": {
// any metadata/context you have
// eg useragents, ip, userid, timestamps, latency, whatever
// this should be reasonably consistent and ALWAYS the same best-effort shape for all events no matter what
}
}
You don’t need to know all use cases up front.
Events are an investment in optionality.
The Mindset
Treat your system as a stream of facts.
Every event is a record of something that happened.
Once you start emitting them, you stop worrying about what data you forgot to collect.
Don't overthink whether you are "event sourcing" or "event driven" - you can do both at the same time.
Just send events.