Skip to main content
8 min readby Zsolt Kocsmarszky
Product AnalyticsEvent TaxonomyData

Event Taxonomy: How to Name Analytics Events

We could easily spend days arguing about whether it's Product Viewed or product_viewed. Neither is wrong. That's exactly what makes it a trap.

Most taxonomy debates are about taste wearing the costume of debates about correctness. And because nobody can win on correctness, nobody commits — so you end up with all of them at once (yeah that happened to me). Four naming styles, three tenses, two casings, and an event called sign-up_completed_email 🙃

And that's how you end up on the event naming landfill.

So instead of one "right" answer (there's no right answer here, believe me), here's a tour of the key decisions and the options that come with them: what each costs, what each buys you. Then I'll tell you what I pick and walk you through my decision-making process — knowing full well it's a preference, not a law.

These break into four mostly-independent decisions. You can mix and match across them as you wish. You just can't mix and match inconsistently within one.

New to product analytics? Start with Product Analytics for Designers to get the basics, then come back here.

Decision 1: Event name structure

The big one — how you compose the name itself.

ConventionExampleProsCons
Object–ActionProduct Viewed, Checkout Flow Step StartedSorts related events together; groups naturally by object; scales cleanly to hundreds of eventsSlightly unnatural to write
Action–ObjectViewed Product, Completed Checkout Flow StepReads like plain English; intuitive for newcomersSorting scatters related events; Viewed Product and Clicked Product Card end up rows apart
Category–Object–ActionEcommerce Product Viewed, Ecommerce Checkout Flow Step StartedExtra grouping layer for very large products; suits big orgsVerbose; the category boundary is always arguable; over-built for most teams
Flat / descriptivehomepage signup button click, checkout flow payment step submittedZero rules to learn, this is where most beginners startBecomes a landfill almost immediately; impossible to query consistently

The field has mostly converged on Object–Action (Segment popularized it early and it stuck).

And then: what divides the parts?

Pick a pattern and you've still got one decision left — the character that physically separates object from action. It feels trivial until an object or action runs more than one word, and suddenly nobody can tell where checkout flow ends and step started begins.

DividerExampleProsCons
SpaceCheckout Started, Checkout Flow Step StartedMost readable for a human; natural in reports and dashboardsBreaks in SQL, URLs, and code; stray double-spaces creep in silently
Dot .checkout.started, checkout_flow.step_startedFamiliar namespacing (object.method); sorts and reads as a clean hierarchyMany warehouses read . as nested-field access — BigQuery especially; can collide with property paths
Colon :checkout:started, checkout_flow:step_startedStrong, unambiguous separator; classic namespace style (user:created)Less conventional, so less tooling muscle memory; looks odd to some eyes
Slash /checkout/started, checkout/flow/step/startedReads as an explicit path; scales to deep hierarchy (checkout/payment/started)Collides with URLs and file paths; implies more nesting than you usually have

The divider earns its keep the moment a group runs multi-word — checkout_flow, step_started. With a few single-word events you can lean on the underscore and skip the question. But that's a luxury of small taxonomies. Grow the event count and multi-word names become the norm, not the exception — and the divider stops being optional.

Decision 2: Casing

Purely mechanical, weirdly emotional.

StyleExampleProsCons
snake_casecheckout_started, checkout_flow_step_startedMachine-friendly; unambiguous; plays nicely with SQL and warehousesLooks "engineery" in dashboards
Title CaseCheckout Started, Checkout Flow Step StartedReads cleanly in reports and UI; human-facingSpaces break things downstream; easy to fat-finger inconsistent capitals
camelCasecheckoutStarted, checkoutFlowStepStartedCompact; familiar to JS devsHarder to scan; word boundaries blur
kebab-casecheckout-started, checkout-flow-step-startedReadable; URL-safeBreaks in tools that read - as subtraction

There's really no winner here — only a warehouse to please. If your data lands in SQL, snake_case saves you from the day Checkout Started, Checkout started, and checkout started show up as three separate events — same click, three rows, and a number that's quietly wrong.

Decision 3: Tense

Small decision, big consistency cost if you waffle.

TenseExampleNotes
Past tenseCheckout Completed, Checkout Flow Step CompletedAn event describes something that already happened. Reads as a fact.
Present / imperativeCheckout Complete, Checkout Flow Step CompleteReads like a command, not a record. Confuses events with actions you want a user to take.

Nearly settled. Events are history, so past tense matches what an event actually is.

Decision 4: Where meaning lives — name vs. properties

Not a naming style, but it's the decision that most determines whether your taxonomy stays small or explodes.

ApproachExampleProsCons
Encode in the nameVideo Played HD Mobile, Checkout Flow Step Completed MobileDead simple to read one eventCombinatorial explosion; every new dimension multiplies your event count; rigid forever
Push into propertiesVideo Played + {quality, device, duration}, Checkout Flow Step Completed + {step, device}One event, infinite slices; flexible; tiny event listNeeds properties defined up front; one more thing to govern

Properties keep your event count flat while your analytical power grows. If you're adding adjectives to an event name, they're almost always properties in disguise.

The habits that beat any convention

Pick whatever lane you want from the menu above. Then protect it with these three habits — they matter more than which lane you picked.

Stay consistent, religiously. The fastest road to a landfill is a second style that "just this once" felt easier. So pick one convention and never quietly break it. Put one person in charge of saying no to off-spec events, or wire a check into CI — whatever it takes so event number 300 looks exactly like event number one.

Pre-define your objects and actions. Before you ship the second event, write the two lists: the objects (Checkout, Payment, Video) and the actions (Started, Completed, Failed). Every new event gets assembled from that vocabulary, never invented on the spot. It's the one move that stops Checkout Started, Checkout Begin, and Checkout Initiated from becoming three events for the same click.

Keep a living document. One file with every event — its name, its properties, and a one-line description of what it actually means.

Not because it's tidy. Because it's the context your AI needs.

The moment you start pulling data points with an AI in the loop — and you will — it has to know that Checkout Started fires on button click, not on page load, and that quality is one of sd | hd | 4k. Without that doc, you're re-explaining your schema every single session. With it, you paste one file and the model already speaks your taxonomy.

A taxonomy nobody writes down is a taxonomy only you can query. So write it down.

What the biggest providers recommend

Before I inflict my own preferences on you, here's where the companies that sell analytics for a living have landed. I read the naming guidance from PostHog, Amplitude, Mixpanel, Heap (now Contentsquare), Statsig, and Segment. The interesting part isn't where they disagree — it's how little they do.

ProviderName structureCasingTenseDetail lives in
PostHogcategory:object_actionsnake_casepresent (click, submit)properties
Amplitudeobject_actionsnake_casepast (checkout_completed)properties
Mixpanelobject_actionsnake_casepast (song_played)properties
Heap / Contentsquarelocation-action (enforced in-app)your defined listpresent (view, click)properties
Statsigconsistent + descriptive (no hard rule)properties
SegmentObject ActionTitle Case events, snake_case propertiespast (Order Completed)properties

A line on each:

PostHog is the strictest of the bunch: lowercase snake_case, a category:object_action shape, and — unusually — present-tense verbs drawn from a fixed, approved list (click, submit, create). It also tells you to version events (registration_v2:...) and to keep names static.

Amplitude wants object_action in snake_case, past tense, grouped into a product-area hierarchy — with a full governance layer bolted on top: owners, an approval workflow, change logs, deprecation windows.

Mixpanel lands on the same (object) (verb) shape and recommends snake_case specifically because it survives the trip to your warehouse. It hammers one point hardest: push variants into properties (Add to Cart + an item property), never mint a new event per variation.

Heap (now Contentsquare) is the only one that enforces the convention inside the tool: you define the prefixes — by default location-action — and a controlled list of values for each, and new events have to fit. Governance by guardrail.

Statsig stays deliberately high-level: a "comprehensive taxonomy with consistent, descriptive naming," planned against your goals and audited regularly — but it won't pick a casing or structure for you.

Segment is the casing rebel: Title Case for event names, snake_case for properties, an Object + Action framework, past tense (Order Completed). Plus the familiar trio — few core events, rich properties, one tracking plan as the single source of truth.

Strip away the house styles and the same rules keep falling out:

Everyone composes names as object plus action. Nobody argues for action-first, and nobody defends flat descriptive blobs.

Everyone pushes detail into properties instead of the event name — one Video Played with a quality property, never video_played_hd_mobile.

Everyone bans dynamically generated names — static strings only, no purchase_${date}.

And everyone tells you to write it down and govern it — a tracking plan, a style guide, an owner, a review step.

Where they split is exactly the two calls that are pure taste: casing (snake_case for the warehouse-first crowd, Title Case for Segment) and tense (PostHog and Heap lean present; Amplitude, Mixpanel, and Segment lean past). That's the whole thesis of this post in one paragraph — the structure is settled, the styling is preference.

What I actually pick

Everything above is a menu. Here's my preference: the Human-Readable Event Naming taxonomy. My only focus is making event names as readable for humans as possible — so querying and presentation come easy.

Object:Action with a colon divider, Title Case, past tense, and properties carry the nuance.

So: Product:Viewed, Payment:Failed, Video:Played — with quality, device, and duration riding along as properties. And the colon earns its keep the moment a name runs multi-word: Checkout Flow:Step Started stays readable, where Checkout Flow Step Started leaves you guessing where the object ends and the action begins.

The reasoning, fast:

  • Title Case, because I read these names all day — in dashboards, in reports, in a query I'm explaining to someone who's never seen the schema. Checkout:Started beats checkout_started every single time.
  • Object–Action, because at 300 events I want every Checkout:* to collapse into one tidy block.
  • The colon divider, because multi-word objects and actions are where naming falls apart. Checkout Flow Step Started could split three ways; Checkout Flow:Step Started never leaves a doubt about what's the object and what's the action.
  • Past tense, because an event is a receipt, not an instruction.
  • Properties over name-encoding, because I'd rather add one property next quarter than deprecate forty events.

Yes, spaces can annoy your warehouse in some cases. I've decided I care more about the human reading the dashboard than the query that has to wrap a name in quotes. Your call may land differently — that's allowed.

But the load-bearing word is mine. If your team already writes product_viewed in snake_case and does it religiously — don't migrate. A consistent convention you dislike beats a "correct" one applied half the time.

The only real sin is inconsistency. That's the thing that turns a taxonomy into a landfill.

Design your own event taxonomy

Four decisions, one combined result. Toggle the options below and watch the same five events rename themselves in real time — it's the fastest way to feel why some combinations read clean and others fall apart.

Interactive · Event Taxonomy

Try the four naming decisions.

There's no single right answer — only consistency. Change a rule and every event below moves with it.

01Structure
02Casing
03Divider
04Tense

Live preview

Checkout:Started

  • User:Signed UpAccount
  • Checkout:StartedConversion
  • Payment:FailedConversion
  • Product:ViewedProduct
  • Video:PlayedMedia

Object·Action · Title Case · past tense · colon