Design and Product Analytics Questions
Using quantitative metrics to inform product and design decisions. Covers key user engagement metrics such as conversion rates, task completion, retention, and feature adoption, and how to instrument and interpret these signals using analytics platforms and product dashboards. Explains how quantitative data complements qualitative research, how to identify design problems from metrics, design experiments and metrics for validation, and how to translate findings into design priorities and success criteria.
MediumTechnical
53 practiced
A new design increases task completion time but increases task success rate. As a data analyst, how would you quantify the trade-off and recommend whether to adopt the design? Describe the metrics, visualization, and stakeholder framing you would use.
Sample Answer
Situation: You ran an A/B test and the new UI (B) raised task success from 70% → 82% but increased median completion time from 45s → 70s. The product team asks whether to adopt B.Approach (how I'd quantify the trade‑off)- Primary metrics: - Task Success Rate (TSR): % tasks completed successfully (lift and absolute delta). - Completion Time Distribution: median, 75/90th percentiles, and CDF to show tail effects. - Business impact metrics: conversion downstream (e.g., purchases, retention), support ticket rate, and cost per failed task.- Statistical tests: - Test significance for TSR (chi-square/bootstrapped CI) and for time (Mann–Whitney or bootstrapped medians). - Compute effect sizes and confidence intervals; run power analysis and Heterogeneous Treatment Effects by user segment.Quantification and recommendation model- Build a utility function: Utility = w_success * TSR + w_time * f(time) where weights map to business value. Example: if an extra successful task yields $5 revenue and extra 25s costs $0.20 (server/attention cost), compute net expected value per user = ΔTSR*$5 − ΔTime*$cost/sec.- Alternative: compute expected downstream revenue uplift = ΔTSR * conversion_value; compare to opportunity cost of longer sessions (bounce, capacity).Visualizations to present- Bar chart with lift and CIs for TSR.- CDF/violin plot of completion time to show tail impact.- Scatter/quad chart: x=ΔTime, y=ΔTSR with iso‑utility contours (lines of equal net value).- Segment heatmap: impact by device, new vs returning users, geography.- Expected value table showing per-user and aggregated monetary impact.Stakeholder framing and recommendation- For PM/exec: "B increases success by 12pp, yielding estimated $X/month in revenue, but increases median time by 25s with estimated cost $Y/month; net impact = $Z (±CI)."- For UX: show CDF and segments where time spike is unacceptable (e.g., mobile low‑bandwidth).- For Eng: propose mitigations (progressive enhancement, lazy load) and an experiment to optimize speed while preserving success.- Decision rule: adopt if net expected value > 0 AND no critical UX SLA breach for key segments; otherwise iterate (A/B test speed improvements) or roll out to high‑value segments first.Key takeaways- Use statistically robust estimates, translate metrics into business value, visualize trade-offs (CDF + iso‑utility), and recommend based on net expected value and segment-level risks.
HardTechnical
102 practiced
Technical coding: Given a table events(user_id, event_name, event_time) and a list of funnel steps, implement (in Python/pandas) a function that computes per-user funnel completion step (highest step reached) and returns aggregated conversion counts for each step. Include handling of out-of-order events and duplicate events.
Sample Answer
Approach:- Normalize events to each user's earliest timestamp per event (dedupe).- For funnel steps in order, for each user find the earliest occurrence of that step that occurs after the previous step's matched time — this enforces step order despite out-of-order events.- Record highest step reached per user, then aggregate counts per step.Key points:- We dedupe by taking earliest per user/event to remove duplicates.- Enforce sequential ordering by requiring each step time > previous matched time.- Returns per-user matched times and highest step, plus aggregated cumulative counts.Time/Space complexity:- O(N log N) dominated by sort; pivot uses O(U * E) space (users × events). Iteration is O(U * S) where S = funnel steps.Edge cases:- Simultaneous timestamps: code requires strictly greater (>) so simultaneous events won’t count as later — change to >= if business logic allows.- Users with repeated events: dedupe keeps earliest; if you need later occurrences for ordering, you'd keep all and scan per user.- Missing events in data are handled (NaT). Alternative: stream per-user sorted events and two-pointer match if memory is tight.
python
import pandas as pd
import numpy as np
def funnel_completion(events_df: pd.DataFrame, funnel_steps: list,
user_col='user_id', event_col='event_name', time_col='event_time'):
"""
events_df: columns [user_id, event_name, event_time] where event_time is datetime-like
funnel_steps: ordered list of event names e.g. ['view', 'signup', 'purchase']
Returns: (per_user_df, agg_counts_df)
per_user_df: DataFrame with user_id, highest_step_index (-1 if none), highest_step_name (None if none), and matched_times for steps
agg_counts_df: DataFrame with step_index, step_name, users_reached (cumulative counts)
"""
# ensure timestamptype
events = events_df.copy()
events[time_col] = pd.to_datetime(events[time_col])
# Keep earliest time per user+event (dedupe duplicates)
first_times = (events
.sort_values(time_col)
.drop_duplicates([user_col, event_col], keep='first')
.loc[:, [user_col, event_col, time_col]])
# pivot to wide for faster lookups: index user, columns event -> earliest time or NaT
pivot = first_times.pivot(index=user_col, columns=event_col, values=time_col)
# Prepare per-user record of matched times for funnel steps
users = pivot.index
matched = pd.DataFrame(index=users, columns=funnel_steps, dtype='datetime64[ns]')
# For each step, find earliest time greater than previous matched time
prev_time = pd.Series(pd.NaT, index=users)
for step in funnel_steps:
if step in pivot.columns:
candidate_times = pivot[step] # earliest time user did this event (may be NaT)
# We need candidate_times > prev_time (if prev_time is NaT, allow any time)
valid = candidate_times.where(candidate_times.notna() & (
prev_time.isna() | (candidate_times > prev_time)
))
matched[step] = valid
# update prev_time to matched time (so next step must be after this matched)
prev_time = matched[step].fillna(prev_time) # if no match for this step, keep prev_time
else:
# no user performed this event at all -> remains NaT
matched[step] = pd.NaT
# prev_time stays unchanged
# Determine highest step index reached per user
def highest_step(row):
# find last non-null in order
for i in range(len(funnel_steps)-1, -1, -1):
if pd.notna(row[funnel_steps[i]]):
return i, funnel_steps[i]
return -1, None
hs = matched.apply(lambda r: pd.Series(highest_step(r), index=['highest_step_index','highest_step_name']), axis=1)
per_user = pd.concat([hs, matched], axis=1).reset_index().rename(columns={user_col: 'user_id'})
# Aggregate counts: number of users who reached at least step i (cumulative)
totals = []
n_users = len(per_user)
for i, step in enumerate(funnel_steps):
reached = (per_user['highest_step_index'] >= i).sum()
totals.append({'step_index': i, 'step_name': step, 'users_reached': int(reached)})
agg = pd.DataFrame(totals)
return per_user, aggHardSystem Design
61 practiced
You have event data with user_id, device_id, anonymous_id, event_name, and timestamp across web and mobile. Describe a robust strategy to deduplicate users, create a single user profile for measurement, and handle anonymous-to-identified merges while preserving historical metrics. Explain trade-offs and privacy concerns.
Sample Answer
Requirements:- Unify identities across web/mobile for measurement (DAU, conversions).- Preserve historical metrics when anonymous users later identify.- Support joins by user_id (logged-in), device_id, anonymous_id.- Low-latency lookups for analysis; batch recomputation acceptable for some aggregates.- Privacy/compliance (GDPR/CCPA) and deletion support.High-level architecture:- Ingestion → Raw event store (immutable) + Identity graph builder → Identity store (canonical profiles) → Enriched event store (events annotated with canonical_id) → Analytics layer (OLAP / reporting).Core components & responsibilities:1. Raw event store: append-only events with user_id, device_id, anonymous_id, ts.2. Identity graph service: maintains edges (user_id ↔ device_id ↔ anonymous_id) with edge metadata (first_seen, last_seen, confidence, source). Use probabilistic links only with clear confidence scores.3. Canonical profile resolver: deterministically chooses canonical_id (prefer authenticated user_id); supports merges and historical backfill.4. Enrichment/Backfill pipeline: when merges occur, re-materialize or virtualize enriched views to attribute historical events to canonical_id (store mapping table of event_id → canonical_id to avoid rewriting raw events).5. Analytics layer: aggregates can be computed on-the-fly by joining enriched mapping or computed periodically and stored in OLAP cubes.Deduplication & merge strategy:- Deterministic first: if event has authenticated user_id, map to canonical_id immediately.- Link anonymous_id → device_id via device fingerprinting and to user_id when login event occurs. On login, perform atomic merge: create canonical mapping linking previous anonymous events to user_id.- Preserve history by maintaining mapping table and backfilling aggregates (incremental jobs that reassign counts rather than deleting historical rows).- Keep provenance: for each merge store timestamp, reason, and confidence.Trade-offs:- Rewriting raw events is costly; mapping table + enrichment avoids rewriting but requires joins at query time (slower).- Aggressive probabilistic merges increase recall but risk false merges (polluted metrics). Conservative approach loses some merges but safer.- Real-time enrichment vs. batch backfill: real-time improves dashboards but is more complex.Privacy & compliance:- Support deletion by marking all linked identifiers and removing mappings; provide removal from enriched stores and downstream indices.- Minimize retention of raw PII; hash identifiers with per-team salts; encrypt identity graph.- Log consent and do not merge cross-device identifiers without consent where required.- Expose audit logs for merges and provide opt-out flows.Operational considerations:- Monitor merge rates and false-merge incidents.- Surface confidence in reports and allow analysts to filter by strictness level.- Document assumptions so stakeholders understand metric implications.
HardTechnical
66 practiced
You need to assess feature adoption elasticity: how much additional revenue per user is expected for a 10% increase in feature adoption. Outline an analysis plan using historical event data and revenue, including modeling approach (e.g., regression), features, and how you'd control for confounding variables.
Sample Answer
Situation & goal: Estimate elasticity — incremental revenue per user (ARPU) from a 10% absolute increase in adoption of a feature — using historical event and revenue data. I’d treat this as a causal question (not just correlation), combining regression for point estimates with causal checks.1) Data collection & preprocessing- Unit: user-week (or user-month) with columns: user_id, time, feature_adopted (binary), adoption_rate cohort metrics, revenue (period), covariates (signup_date, plan, region, usage metrics, marketing_exposures), instrumentation flags.- Clean: deduplicate, align timestamps, impute/flag missing, winsorize extreme revenue, create lag features (prior revenue, prior adoption).2) Exploratory analysis- Summary stats by adopter vs non-adopter, time series of adoption & ARPU.- Visualize dose-response: binned adoption intensity vs mean revenue.3) Modeling / causal approach (primary + robustness)Primary: panel regression with user fixed effects and time fixed effects:Revenue_it = β * FeatureAdopt_it + γ'X_it + α_i + δ_t + ε_it- β interpretable as within-user effect of adoption on revenue. For elasticity per 10% adoption increase, scale β by 0.10.Controls X_it: prior revenue (lag), engagement metrics (sessions, time), plan/tier, marketing exposures, recent product changes, seasonal indicators.Robustness / causal checks:- Difference-in-differences if roll-out happened across cohorts: compare treated vs control pre/post with parallel trends test.- Propensity-score weighting / matching: estimate propensity to adopt using pre-treatment covariates, then use weighted regression or match and estimate average treatment effect on treated (ATT).- Instrumental variable if adoption endogenous: find instrument correlated with adoption but not revenue (e.g., randomized marketing push, feature A/B assignment) and run 2SLS.- Event-study specification to inspect dynamics and pre-trends (leads/lags).4) Heterogeneity & uplift- Estimate treatment effect heterogeneity by segment (plan, tenure, region) via interaction terms or uplift models (two-model or causal forest) to predict incremental revenue per user segment.5) Validation & sensitivity- Check common support for matching, placebo tests, falsification outcomes, alternative windows, and inclusion/exclusion of controls.- Bootstrap or cluster-robust SEs (cluster by user or cohort).6) Output & interpretation- Report point estimate: β_scaled = β * 0.10 => dollars per user for 10% adoption increase, with CI.- Provide uplift by segment, overall revenue impact = β_scaled * total users * projected adoption uplift.- List assumptions (no unobserved time-varying confounders, valid instrument if used) and recommend an A/B test or phased roll-out to confirm causality if feasible.This plan balances rigorous causal identification with practical analytics tools (SQL for data prep, regression/DiD/PSM/IV in R/Python or BI-friendly summaries) and produces actionable estimates and uncertainty for stakeholders.
MediumTechnical
59 practiced
Explain cohort analysis. Given a user_events table (user_id, event_name, event_time), outline SQL steps to build D7 retention cohorts (cohort on user’s first event date). Explain potential edge cases and how you would present the results to product stakeholders.
Sample Answer
Cohort analysis groups users by a common start point (here: date of first event) and measures how many return over subsequent days — D0 is day of first event, D7 is 7 days after. For D7 retention cohorts we: 1) assign each user a cohort_date = DATE(min(event_time)); 2) compute for each user whether they had any event on cohort_date + 7; 3) aggregate retention rate per cohort_date.SQL steps (example in PostgreSQL):Key reasoning:- We use first-event date to define natural cohorts and check presence on exactly day+7.- Use DATE truncation so timezones and timestamps don't split cohorts.Edge cases and mitigations:- Users with first event and D7 event in different timezones — normalize timestamps to a single timezone before DATE().- Users with multiple events on D7 — flag still 1 (presence/absence).- New users fewer than 7 days of data (right-censoring) — exclude cohorts whose cohort_date + 7 > max(event_time) of dataset or annotate as incomplete.- Bots/test accounts — filter known test user_ids or impl. activity thresholds.- Duplicate/late-arriving events — deduplicate and consider event ingestion lag window.Presentation to stakeholders:- Show a table + line chart of retention_pct_d7 by cohort_date; include cohort_size and a confidence note for small cohorts.- Add rolling weekly average and segmentation (acquisition channel, country, platform).- Call out actionable insights (e.g., if retention drops for recent cohorts, recommend onboarding improvements), and highlight data quality caveats (incomplete recent cohorts, timezone assumptions).
sql
-- 1) cohort per user
WITH user_cohort AS (
SELECT user_id, DATE(MIN(event_time)) AS cohort_date
FROM user_events
GROUP BY user_id
),
-- 2) events shifted to event_date
user_events_dates AS (
SELECT user_id, DATE(event_time) AS event_date
FROM user_events
),
-- 3) mark if user was active on cohort_date + 7
retention_flag AS (
SELECT uc.cohort_date,
uc.user_id,
CASE WHEN EXISTS (
SELECT 1 FROM user_events_dates ued
WHERE ued.user_id = uc.user_id
AND ued.event_date = uc.cohort_date + INTERVAL '7 day'
) THEN 1 ELSE 0 END AS retained_d7
FROM user_cohort uc
)
-- 4) cohort-level retention %
SELECT cohort_date,
COUNT(user_id) AS cohort_size,
SUM(retained_d7) AS retained_d7,
ROUND(100.0 * SUM(retained_d7) / NULLIF(COUNT(user_id),0),2) AS retention_pct_d7
FROM retention_flag
GROUP BY cohort_date
ORDER BY cohort_date;Unlock Full Question Bank
Get access to hundreds of Design and Product Analytics interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.