Back to Blog
data engineering event tracking

Event Schemas That Age Well

Bence Kovács

By month six, a schema that seemed clear when one engineer wrote it can be difficult to interpret. A PM joining then may find its events cryptic when trying to understand a user's first session.

This is usually a naming failure, not a technical one. Early choices about event names, properties, and relationships decide whether the schema stays readable at month twelve or demands reverse engineering before anyone can query it.

Event Naming Flaws

Event names identify things that happened. They should state the action, not the system's handling of it. The principle is simple, yet often ignored.

Consider these two names for the same event: api_call_success versus query_executed. The first describes a system outcome. The second describes what the user did. If a PM wants to know how often users run queries, they should be able to read the event name and know immediately which event they need. api_call_success does not tell you that. It might be triggered by a user action, a background sync, a webhook, or a scheduled task. query_executed is unambiguous.

The naming convention that ages best is object_verb in past tense, where the object is the primary thing being acted on and the verb is the action that occurred. query_executed, source_connected, report_shared, account_created. Each name tells you what happened without knowing the codebase. A new PM reading these names six months later can understand the event log without documentation.

Avoid implementation verbs. saved, triggered, processed, handled all describe what the system did, not what the user did. Avoid location names as events. dashboard_visited tells you a user loaded a page. dashboard_opened is marginally better but still not informative about what the user was trying to accomplish. The more informative event might be query_result_viewed or saved_query_opened, depending on what is actually happening when a user opens the dashboard.

Properties Are a Common Failure Point

Names make events discoverable; properties provide analytical depth. With only a timestamp and user ID, an event is nearly useless for segmentation or funnels. Inconsistent or poorly named fields can mislead, especially when the needed field has a 60% null rate.

The minimum property set for any user action event is: user identifier, timestamp, and the primary identifier of the object being acted on. For a query_executed event, that means a workspace ID or session identifier, the timestamp, and something that identifies the query (a query ID, or at minimum a hash). Beyond that minimum, add properties that segment the event in ways you know will be useful: plan tier, account age bucket, feature flag state if any flags are in play.

A common trap is adding every conceivable property during instrumentation because fields cost little then. That can produce 40-property events with 30 fields null 80% of the time because their conditions are rare. The schema becomes harder to read and slower to query, while its inheritor must identify the 10 reliably populated properties before writing against it.

Shape properties around queries you expect to write in the next three to six months. Include segmentation dimensions you will actually use, not speculative fields "in case we need them later." They can be added later. Older events will lack them, but new events will have them, and historical analysis can often accommodate the gap.

Inconsistency Costs

The most expensive form of schema debt is inconsistency in how the same concept is represented across different events. If user plan is encoded as plan_type in some events and subscription_tier in others, every query that needs to segment by plan across multiple event types requires a join or a case statement to normalize the values. If some events use plan names as strings and others use internal plan IDs, you need a mapping table to interpret either one.

This happens incrementally. The first engineer to add a property called it plan_type. Three months later, a different engineer added a property and used subscription_tier without checking what name was already in use. Neither choice was wrong in isolation. Together they create a schema that requires institutional knowledge to query correctly.

Keep a property glossary beside the schema. It need not be elaborate: define canonical names for the 20 to 30 concepts used often across events, such as User ID, Account ID, Plan, Channel, and Feature area. Consistent vocabulary lets anyone with log access write a query without first uncovering an engineer's preferred term.

We keep a short schema-spec document that every new event must follow before release. It defines the naming convention, minimum property set, and glossary. The first version takes about 20 minutes, and within the first month a new PM can understand the event log without asking an engineer to explain it.

Versioning and Retirement

Schemas evolve as features are renamed and flows redesigned. An event that described the product at launch may represent a flow that vanished six months later. Change management determines whether the log remains useful for trends over time.

Our rule is simple: do not delete event names or reuse them for different actions. After a feature rename, create an event for the renamed feature and let the old one stop receiving data. For a redesigned flow, create new events. The old ones remain historical markers. Transition-spanning queries must include both names, but each stays present and clear.

Reusing event names for changed behavior is the worst outcome. If source_connected originally fired when a user finished an OAuth flow and you later change the flow to use an API key setup, if you keep the same event name, you lose the ability to compare pre-change and post-change connection rates without accounting for behavioral differences between the two flows. The event name no longer means the same thing, but the data does not reflect that.

What Good Schemas Make Possible

A clear event schema lets teams answer product questions quickly. While Bence and I were shaping Mitzuo, we examined why natural language queries on event data fail. A major cause is ambiguity: "how many users connected a data source?" has no deterministic answer when three differently named events, with different properties and firing conditions, could represent that action.

Schema design is more than a technical practice. It decides whether the log clearly records user behavior or becomes an archaeology project for someone present when each event was added. The outcome rests on naming conventions chosen in the first month, before changing a large schema becomes painful.

Spend the 20 minutes. Create the glossary. Describe user actions rather than system behavior. Six months from now, you will benefit most when answering a question with data gathered before you knew you would ask it.

More from the blog