×marble
all posts
Jun 10, 2026·11 min read

Event Taxonomy for Marketing Engineers: The Week-One Decision That Compounds

Your event taxonomy is the most consequential decision of your week-one personalization work. How to design one that survives a year and three engineers.

Alex Shrestha·Founder, ×marble

Event Taxonomy for Marketing Engineers: The Week-One Decision That Compounds

TL;DR.

  • An event taxonomy for marketing engineers is the single most consequential decision of week one — get it wrong and every downstream segment, every model, every email is wrong with it, quietly, for months.
  • The serious teams have converged on object-action naming with snake_case past tense: product_viewed, cart_updated, subscription_canceled. The format is boring on purpose.
  • Five properties should be required on every event: event_name, timestamp, user_id, anonymous_id, source. Everything else is per-event and lives in JSON Schema, not in your head.
  • Enforce the schema at the SDK layer with codegen tools like Segment Typewriter, RudderTyper, or Avo Codegen — not at the warehouse, where bad data has already cost you.
  • A 30-event starter kit, organized around identity, browsing, intent, conversion, retention, and engagement, will carry an early-stage product through its first year of personalization.

A bad event taxonomy is invisible for the first six months and then suddenly explains why nothing works. The "active users" KPI quietly counts three different events. The personalization model trains on Add_to_Cart, add_to_cart, and addedToCart as three distinct intents. Marketing pulls a list of "churned subscribers" that misses anyone who used the iOS app last quarter, because mobile and web fire different event names for the same action. We've watched this happen at four different companies. The fix is always the same: stop, rebuild the taxonomy, backfill, apologize. The cheapest moment to design the event taxonomy is the moment you have zero events to migrate. This post is how to do it.

We'll cover why the schema is the week-one decision, the object-action vs entity-action debate, the required event property contract, schema enforcement tools you should pick from, and a 30-event starter kit specifically tuned for personalization use cases.

Why event taxonomy is the week-one decision

When marketing engineers join a company, the temptation is to start shipping. Wire up a CDP, fire some events, get something into the warehouse, prove value, then circle back to "data quality" once the dust settles. This is exactly the wrong order. Every event you fire before you've decided on a taxonomy is technical debt at compound interest.

The math is straightforward. Suppose you fire 200 events per user per month, across 50,000 monthly active users. That's 10 million events per month. Six months in, you have 60 million events written under whatever ad-hoc names the first three engineers chose. Renaming Clicked Subscribe to subscription_started now requires a backfill job, an Amplitude/Mixpanel re-import, an update to every dashboard, a comms email to the analytics team, and probably a few weeks of dual-firing the old and new names while you cut over. None of that is hard. All of it is annoying enough that nobody does it, so the bad names ossify.

Worse, the names leak into every downstream system. Your reverse-ETL pipeline syncs the event to your CRM with whatever name you chose. Your ML feature store treats Clicked Subscribe and subscription_started as separate features. Your attribution model double-counts. Your activation funnel report quietly excludes anyone on mobile. The taxonomy is the contract between every team that touches data — engineering, analytics, marketing, ML, finance — and changing the contract after the fact requires renegotiation with all of them. So write the contract first.

This is the same principle behind the reference architecture for real-time personalization: the events that flow into the system are the system. If the events are inconsistent, no amount of clever downstream code recovers.

Object-Action vs Entity-Action: pick one and don't look back

The first decision is the naming convention, and there are really two camps. Both work. Both will outlast the engineer who picks them. What matters is picking one and writing it down.

Object-action (also called noun-verb) names events as <thing>_<past-tense-verb>: product_viewed, cart_updated, subscription_canceled, email_opened. The convention is what Avo recommends, what mParticle calls the industry standard, and what every analytics tool from Amplitude to Mixpanel to PostHog defaults to in their examples. The past tense matters: an event is a thing that has already happened, not a thing about to happen. product_viewed, not view_product.

Entity-action (sometimes called verb-noun or action-object) flips the order: viewed_product, updated_cart, canceled_subscription. This reads more like English and is what Segment's older Spec V1 examples sometimes used. It's defensible. It's also weirder to sort alphabetically when you have 100 events — viewed_product and viewed_page cluster together as viewed_*, hiding the relationship to product_viewed and product_added.

We use object-action everywhere, for one reason: it groups by domain. In a UI listing 200 events alphabetically, all your product events cluster: product_added, product_removed, product_viewed. All your subscription events cluster. New engineers can scan the list and see the entity model. With entity-action, all your viewed_* events cluster — but viewing is not a meaningful domain. The grouping that matters is by thing acted on, not type of action.

A few rules of thumb that hold up regardless of camp:

  • Past tense, always. Events are historical facts.
  • snake_case, lowercase. Not Title Case, not camelCase. Casing inconsistencies are the most common taxonomy bug we see.
  • No prefixes for environment or platform. Don't name events web_product_viewed vs ios_product_viewed. The platform is a property, not part of the name.
  • No version numbers in the name. product_viewed_v2 is a smell. If the schema changes, version the schema, not the event name.

Required event properties: the five-field contract

Every event in your taxonomy should have a non-negotiable set of fields. Most analytics tools — Segment, RudderStack, Amplitude — already require some of these; we recommend you raise the bar and require five:

  1. event_name — the canonical name from your tracking plan. Validated against the registered set.
  2. timestamp — ISO-8601, UTC, millisecond precision. Set client-side and re-validated server-side (clients lie about time).
  3. user_id — the stable identifier for an authenticated user. Null when anonymous; never "anonymous" or 0.
  4. anonymous_id — the client-generated ID set at first page view. Always present, even after login. This is what stitches pre-login and post-login behavior.
  5. source — which client or service fired the event: web, ios, android, server, backfill. Critical for debugging when a number looks wrong.

Beyond the five, every event has its own property schema. product_viewed has product_id, product_name, category, price, currency. email_opened has campaign_id, email_id, subject_line. These per-event properties are where the real taxonomy work lives and where most teams fail to enforce structure.

The discipline we recommend: every event has a JSON Schema that lists required vs optional properties, with explicit types and enums. currency is required, type string, enum of ISO-4217 codes. price is required, type number, minimum zero. product_id is required, type string, regex ^prod_[a-zA-Z0-9]+$. The schema is the spec. The spec is the truth. Anything not in the spec is a bug, not a feature.

One more property worth calling out separately: context.session_id. Sessions are how most ranking and personalization models slice behavior — a contextual bandit or sequence model needs to know which events belong together. Treat session ID as functionally required on browser/app events even if your CDP makes it optional.

Schema enforcement: stop bad data before it lands

The hard part is not designing the schema. The hard part is keeping it from rotting. Schemas rot through three mechanisms: engineers fire events with the wrong name, engineers fire events with missing properties, and engineers add new events that nobody reviewed. All three are preventable with the right enforcement layer.

There are four reasonable places to enforce a tracking plan, in increasing order of leverage:

  1. In the warehouse, after the fact (dbt tests, Great Expectations). This is where most teams start. It catches bad data after it has already shipped. Useful as a backstop, useless as a primary defense.
  2. In the CDP / pipeline (Segment Protocols, RudderStack Data Governance). These tools validate events against a tracking plan in flight and can block or quarantine violations. Segment's Protocols uses JSON Schema under the hood, as does RudderStack. Better than the warehouse, but bad data still gets fired from clients — you just don't ingest it.
  3. In the SDK, at compile time (Segment Typewriter, RudderTyper, Avo Codegen). This is the highest-leverage move available. Typewriter generates strongly-typed client libraries from your tracking plan, so an engineer literally cannot fire Clicked_Subscribe if the spec says subscription_started — it's a TypeScript compile error. RudderStack's RudderTyper does the same thing on every plan tier.
  4. In the design tool (Avo, Iteratively before it was acquired). The PM proposes a new event in a UI. Engineering and analytics review. Once approved, codegen ships it to all platforms. This is the most "marketing engineer friendly" layer because it puts the schema in front of non-engineers.

Our recommendation for most teams: codegen at the SDK is non-negotiable, plus pipeline validation as a second line of defense. Warehouse tests are still useful for catching deeper semantic drift (e.g. nobody fired subscription_started for three days, suggesting a deploy broke it).

The contract should be: a new event is born in the design tool, reviewed by analytics + engineering, codegen'd into typed client libraries, fired in instrumented code, validated again at the CDP pipeline, and finally tested in the warehouse for semantic consistency. Five layers sounds like overkill. The cost of not having those layers is the six-month silent rot we opened this post with.

A 30-event starter kit for personalization

Most personalization use cases — recommendation, ranking, lifecycle messaging, content selection — work off the same small core of events. If you're building an event taxonomy for marketing engineers from scratch, start with these thirty. Six categories, five events each.

Identity (5)

  • user_signed_up
  • user_signed_in
  • user_signed_out
  • user_identified (anonymous → known stitching)
  • user_profile_updated

Browsing (5)

  • page_viewed
  • screen_viewed (mobile equivalent)
  • search_performed
  • category_viewed
  • product_viewed

Intent (5)

  • product_added (to cart / favorites / saved-for-later)
  • product_removed
  • cart_viewed
  • wishlist_updated
  • share_clicked

Conversion (5)

  • checkout_started
  • checkout_completed
  • payment_method_added
  • subscription_started
  • subscription_canceled

Retention (5)

  • session_started
  • session_ended
  • notification_received
  • notification_opened
  • feature_used (generic, with feature_name property)

Engagement (5)

  • content_viewed
  • content_completed
  • content_reacted (likes, hearts, ratings)
  • content_shared
  • recommendation_clicked

A few notes. recommendation_clicked is the single most underrated event in this list — it is the event that lets you measure your personalization layer against itself, and it's the event most teams forget to fire. Same for notification_opened — if you ship lifecycle messaging without it, you can never compute open rate by segment, which means you can never improve your messaging.

This 30-event kit is opinionated for product-led growth, SaaS, and ecommerce. Media and streaming companies will swap conversion for content-completion variants; B2B will add demo-request and lead-qualification events. The shape stays the same: six domains, five events each, all object-action snake_case, all with the five required properties and a per-event JSON schema.

This taxonomy is also what we recommend for handling the cold-start problem. Day-zero personalization doesn't need a thousand events; it needs the right thirty, fired consistently from day one, so that the moment a user crosses the threshold from anonymous to known, you have a coherent history to personalize on.

How ×marble fits in

×marble is a personalization knowledge-graph product. We ingest events from whatever taxonomy you've designed — Segment, RudderStack, Snowplow, or a custom pipeline — and turn them into a graph that powers real-time recommendation. The catch is the same one we've been writing about: your events have to be clean before any knowledge graph or recommendation engine can do anything useful with them. So we spend the first call with every customer reviewing their tracking plan. We're opinionated about it.

If you'd rather not build the marketing engineer's personalization stack yourself, we built it as a product. Our sub-products Vivo, Video, and Marble×Music all run on the same graph, and the graph runs on a clean, object-action event stream. See ×marble or talk to us about your tracking plan.

FAQ

What is an event taxonomy?

An event taxonomy is the set of rules that defines how events are named, structured, and categorized in your analytics and personalization pipeline. It covers the naming convention (e.g. object-action, snake_case, past tense), the required properties on every event, the per-event property schemas, and the governance process for adding new events. A good event taxonomy is the single source of truth that engineering, analytics, marketing, and ML all build against.

What is the best event naming convention for personalization?

For personalization use cases, the best convention is object-action with snake_case past tense: product_viewed, cart_updated, subscription_canceled. This format groups related events alphabetically (all product_* events cluster together), reads consistently across platforms, and matches what Segment, Amplitude, and Avo recommend. The exact convention matters less than the discipline of applying it everywhere — pick one and don't deviate.

What event properties should be required on every event?

Five properties belong on every event regardless of type: event_name, timestamp (ISO-8601 UTC), user_id (null when anonymous), anonymous_id (always present), and source (web/ios/android/server). Beyond the five, each event has its own JSON Schema defining its specific required and optional properties, with types and enums explicit. This contract is what lets downstream systems trust the data.

How do you enforce an event tracking plan?

The strongest enforcement is at compile time using codegen tools like Segment Typewriter, RudderTyper, or Avo Codegen — these generate typed client libraries from your tracking plan so engineers physically cannot fire malformed events. Layer in pipeline validation (Segment Protocols, RudderStack Data Governance) as a backstop, and warehouse tests (dbt, Great Expectations) for semantic drift. Don't rely on warehouse tests alone — by the time bad data lands, it's already cost you.

How many events should I start with?

For most teams building a personalization layer, 30 well-defined events organized across six domains — identity, browsing, intent, conversion, retention, engagement — will carry you through the first year. Resist the urge to fire 200 events on launch; you'll spend more time deduplicating them than analyzing them. Add events incrementally, with each new one going through your design review process.

Further reading

the product behind these notes

×marble is the personalization graph.

One API. A living knowledge graph per user. Day-zero ready, explainable by construction. We built it so you don't have to.

See ×marble