skip to content

Amplitude

Product analytics built around behavioural questions — funnels, retention curves, and cohorts over an event stream keyed by user. Interviewers ask because getting useful answers out of it depends entirely on a disciplined event taxonomy upstream.

on this pageshow

explore

questions

5

In Amplitude, what is the difference between an event property and a user property?

level: juniorimportance: must knowfreq 65%

answer

  1. Two bags of key/value pairs per event
  2. One describes the action, one the person
  3. track() versus the Identify object
  4. Values are stamped at ingest, never rewritten

basics

~20 s

An event property describes one occurrence — the plan clicked, the search term — and is stored on that event. A user property describes the person and is stamped onto their profile, applying to events from then on.

solid answer

~40 s

Amplitude stores an event stream keyed by user, and each event carries two bags of key/value pairs. **Event properties** (`event_properties`) qualify that single occurrence: `genre`, `search_term`, `cart_total`. **User properties** (`user_properties`) describe the person: `plan`, `signup_channel`, `company_size`. In the Browser SDK you set the first inline — `amplitude.track('Song Played', { genre: 'jazz' })` — and the second through an `Identify` object: `amplitude.identify(new amplitude.Identify().set('plan', 'pro'))`. The important consequence is that Amplitude stamps the user property values current at ingest time onto each event, so changing a user property does not rewrite history: events sent while the user was `free` keep `plan = free` forever. Choose event properties for facts about the action, user properties for state you want to segment and break down by.

code

javascript · 14 lines
javascript
amplitude.init(API_KEY);

// event property: describes this one occurrence
amplitude.track('Order Completed', {
  cart_total: 42.5,
  currency: 'USD',
  payment_method: 'card'
});

// user property: describes the person from now on
const id = new amplitude.Identify()
  .set('plan', 'pro')
  .setOnce('signup_channel', 'organic');
amplitude.identify(id);

go deeper

for a junior

Be ready to state the split in one sentence and name the calls: track carries event properties, the Identify object carries user properties. Give a concrete example of each from a product you have instrumented.

for a middle

Explain the stamping rule — user property values are written onto each event at ingest and history is never rewritten — and what that does to a breakdown after a plan upgrade. Know set, setOnce and add.

for a senior

Show judgment about taxonomy: cardinality limits on property values, few event names with rich properties, and the cost of renaming anything after months of collection. Mention tracking-plan enforcement rather than trusting code review alone.

for a principal

Own the governance question. Who approves a new event, how the plan is versioned, how typed wrappers or CI checks stop drift, and how you decide what stays in Amplitude versus what only ever lives in the warehouse.

## Amplitude's data model in one paragraph Amplitude is a product-analytics platform whose entire storage model is an event stream keyed by user. Every row is an event with a name (`event_type`), a timestamp, an identity (`device_id`, `user_id`, or both), and two dictionaries of key/value pairs: **event properties** and **user properties**. Everything the product does — funnels, retention curves, behavioural cohorts, segmentation — is a query over that stream, so the quality of the answers is bounded entirely by how disciplined those two dictionaries are. ## Event properties: facts about the occurrence An event property qualifies the single thing that just happened. `Song Played` carries `genre`, `duration_seconds`, `is_offline`; `Order Completed` carries `cart_total`, `currency`, `payment_method`. The value belongs to that moment and is meaningless outside it. In the Browser SDK you pass them as the second argument to `track`: ```javascript amplitude.track('Order Completed', { cart_total: 42.5, currency: 'USD' }); ``` On the HTTP API v2 payload the same thing appears as an `event_properties` object on the event JSON. ## User properties: state about the person A user property describes the human, not the click: `plan`, `account_type`, `signup_channel`, `experiment_variant`. You set them through an `Identify` object, whose methods are the whole vocabulary — `set` overwrites, `setOnce` writes only if the property has never been set (ideal for acquisition attributes like `first_referrer`), `add` increments a numeric counter, `append`/`prepend` push onto a list, `unset` removes it: ```javascript const id = new amplitude.Identify().set('plan', 'pro').setOnce('signup_channel', 'organic'); amplitude.identify(id); ``` The HTTP API equivalent is a `user_properties` object, either on a regular event or on an `$identify` event. ## The stamping rule that trips everyone up Amplitude records the user property values that were current at ingest onto each event as it arrives. It does **not** retroactively rewrite past events when a property changes. This is a feature, not a bug: it lets you ask "how did users convert *while they were on the free plan*" rather than only "how did users who are on the free plan *today* convert". But it surprises people who expect a profile-style database where the latest value wins everywhere. If you upgrade a user from `free` to `pro`, every event they sent yesterday still reads `free`, and a breakdown by `plan` will split one human across two series. When you genuinely want current-state segmentation, segment on the user rather than breaking down the historical events, and be explicit in the analysis about which of the two you meant. ## Choosing between them Ask what the value is *about*. If the same user could produce two different values in two events on the same day, it is an event property. If it is a state that persists until something changes it, it is a user property. Two practical constraints sharpen this further: - **Cardinality.** Property values feed dropdowns and breakdowns. Raw identifiers, timestamps, free-text and URLs with query strings blow up the distinct-value count and make a breakdown unreadable. Bucket them (`price_band`, `page_type`) or leave them out of the property and keep them in the warehouse copy. - **Mutability.** A user property you rewrite hourly (say, a live score) turns into a stream of profile updates and makes historical breakdowns noisy; that value usually belongs on the event. ## Groups sit alongside both B2B products often need an account dimension: many users belong to one organisation and the question is about the organisation. Amplitude models that with **groups** — `amplitude.setGroup('org_id', 'acme')` — and group properties that live on the account rather than the person. Group-based analysis is an account-level capability rather than something every project has enabled, so confirm before designing a taxonomy that depends on it. ## Taxonomy is the real interview subject The reason an interviewer asks this is that most bad Amplitude implementations fail on the split, not on the SDK. The standard shape is **few event names, rich properties**: one `Button Clicked` with a `button_name` property beats two hundred bespoke event names, because funnels and breakdowns can slice properties but cannot un-fuse event names. Amplitude ships governance for this — a tracking plan in Amplitude Data with planned-versus-observed schemas, and the Ampli CLI, which generates a typed wrapper from the plan so a typo becomes a compile error rather than a phantom event nobody notices for a quarter. Decide the split once, write it down, and enforce it in code review; renaming a property after six months of collection means either backfilling or living with two names forever.

  • When would you reach for setOnce instead of set on an Amplitude user property?
    Use `setOnce` for attributes that describe the user's origin and must never be overwritten by a later session — `first_referrer`, `signup_channel`, `initial_plan`. `set` overwrites on every call, so a returning user arriving from a paid ad would clobber their true acquisition channel. `setOnce` writes only if the property has never been set, preserving first-touch attribution.
  • A user upgrades from free to pro. Why does a breakdown by the plan user property still show their old events under free?
    Amplitude stamps the user property values that were current at ingest onto each event and never rewrites history. Yesterday's events keep `plan = free`, so a breakdown splits one human across two series. That is intentional — it lets you analyse behaviour as it was at the time — but if you want current-state segmentation you must segment on the user rather than break down historical events.
  • Why do experienced implementers prefer few event names with rich properties over many bespoke event names?
    Properties can be sliced, filtered and grouped after the fact; event names cannot be merged after the fact. One `Button Clicked` with a `button_name` property answers both the specific and the aggregate question, whereas two hundred distinct names make "how many buttons were clicked overall" impossible without listing them all, and they bloat the taxonomy that every analyst has to navigate.

Think of a receipt versus a loyalty card: the receipt records what this purchase was, the card records who the shopper is — and the receipt keeps whatever tier was printed on it that day.

saying these in an interview costs you the question

  • Says changing a user property retroactively updates past events
  • Treats user properties as a place for per-action values like cart total
  • Creates a distinct event name per button instead of a property
  • Uses raw IDs or URLs as property values, exploding cardinality
  • Assumes group properties are the same as user properties

context

open as a page

In Amplitude's HTTP API v2, what does the insert_id field on an event do?

level: middleimportance: should knowfreq 40%

basics

~10 s

insert_id is a client-supplied unique key on an Amplitude event. Amplitude drops a later event carrying an insert_id it has already seen within its recent deduplication window, so a retried upload does not double-count.

open as a page

In Amplitude, why should a web app call reset() when a user logs out?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Because the browser's device ID stays mapped to the user who just logged out. reset() clears the user ID and issues a new device ID, so the next visitor's anonymous events start a fresh identity instead of being attributed to the previous person.

open as a page

Why would an Amplitude funnel report fewer conversions than the same funnel computed in the warehouse?

level: seniorimportance: should knowfreq 50%

basics

~20 s

Usually three causes stack up: client-side events never reached Amplitude, the funnel enforces a conversion window and a step order the SQL ignores, and the two systems count different entities — Amplitude counts resolved users, the query counts rows or raw user ids.

open as a page

In Amplitude's Retention Analysis, how does N-Day retention differ from Unbounded retention?

level: middleimportance: nice to knowfreq 34%

basics

~20 s

N-Day retention counts a user as retained on day N only if they returned exactly on day N. Unbounded retention counts them if they returned on day N or any day after, so its curve is always the higher, smoother one.

open as a page