Skip to content
Product Events

Product Events

Shipping code is half of contributing to a product flow. The other half is the data it emits, which is how anyone finds out whether the change did what you intended.

Product event pipeline — from your emit call to a queryable table Open full screen →

The schema is a published interface

An event name and its field types are an API. The moment an event reaches the stream, something downstream depends on its shape: a dashboard, an alert, an experiment readout, or a model feature.

This makes ordinary-looking edits into breaking changes:

ChangeSafe?Why
Add a new optional fieldYesExisting consumers ignore it
Add a new event typeYesNothing depends on it yet
Rename a fieldNoEvery consumer referencing it silently loses the column
Change a field’s typeNoParsing fails, or values coerce wrong and stay plausible
Remove a fieldNoSame as a rename, without even a new name to migrate to
Change what a field meansNo, and worst of allNothing fails; history becomes quietly incomparable

That last row deserves the emphasis. Redefining signup_completed from “account created” to “account created and email verified” breaks no check anywhere, and every trend line that crosses your deploy becomes a lie. If the meaning changes, emit a new event.

Validation happens at collection, not at query time

The schema registry checks each event against its registered contract before it is accepted into the stream. This is a deliberate choice about where to pay the cost.

Validating on read would mean the bad data is already in the log, already in the raw tables, already in yesterday’s dashboard — and every consumer has to defend against it independently, forever. Validating on write means one system rejects it once.

Rejected is not lost

Events that fail validation go to quarantine with their full payload and the reason they were rejected. The recovery is:

  1. Fix the emit site — usually a field type or a missing required field.
  2. Deploy the fix.
  3. Replay the quarantine window.
Quarantine exists because the alternative is silent drops. A pipeline that discards malformed events gives you a dashboard that is confidently wrong, with no signal that anything happened. A visible reject queue is the better failure.

Where your events end up

  • Raw tables are append-only and never edited in place. They are the audit trail: if a transform is wrong, the raw layer is what you recompute from.
  • Modelled marts are the tested transform layer. Analysts and dashboards build here, not on raw, because raw has no guarantees about naming, deduplication, or late arrivals.
  • Experiment readouts join your events against assignment data. This is where a feature rollout is actually judged — which is why the event has to exist before the rollout starts, not after someone asks how it went.

A practical checklist

  • Register the schema before the code that emits it ships.
  • Emit from the server where you can; client events are subject to ad blockers, offline buffering, and clock skew.
  • Include the identifiers a join will need. An event nobody can join to a user or a session is close to unusable.
  • Name events after what happened (checkout_submitted), not after where the code is (handler_v2_called).