Interview guides · Electronic Arts (Ea)
Electronic Arts (Ea) Data Scientist Interview Questions & Guide 2026
Electronic Arts (Ea) data scientist prep on data pitfalls: selection bias, target leakage, and checking an outlier's provenance before you drop it.
The 60-second brief
As a Data Scientist at Electronic Arts (Ea), you sit at the intersection of massive player data, core game design, and strategic business decision-making. Your work directly influences how millions of players experience blockbuster franchises like Battlefield, EA Sports titles, and expansive live-service ecosystems. You translate terabytes of telemetry, in-game behavior, and player engagement signals into actionable insights that shape game economies, matchmaking systems, player retention strategies, and live-events.
Ask early whether the loop includes an asynchronous take-home or a timed live case, because the two are graded on different things. A take-home is read as an artifact: the question you decided to answer, what you did about missing or malformed records, and a conclusion stated plainly enough for someone to act on. A reviewer who cannot rerun your notebook discounts the result whatever score is printed in it. Hold to the stated time box and write down what you would have done with more of it, since the follow-up round is usually a live defence of the same work.
Focus areas
- Diagnose churn by progression stage, not calendar day
- Compute expected cost of pity-timer tables
- Price cosmetics without cannibalising existing sinks
Overview
This role requires a unique blend of product intuition, rigorous statistical grounding, and advanced technical execution. Whether you are modeling churn predictors for a live-service title, designing complex telemetry pipelines, or evaluating the success of a new feature rollout, your contributions dictate how game teams optimize the player journey. You will partner closely with product managers, software engineers, game designers, and studio executives who rely on your data-driven recommendations to make high-stakes product decisions.
Expect an environment that moves at the speed of the games industry, characterized by high data scale, complex player dynamics, and creative problem spaces. While the work is intensely rewarding and technologically fascinating, it demands a candidate who can balance mathematical rigor with practical business pragmatism. You will need to communicate complex findings to both deeply technical engineers and non-technical creative directors with clarity and authority.
The Electronic Arts (Ea) interview process
Electronic Arts (Ea) candidates report 3 rounds · ≈ 3-5 weeks. The stages below are what candidates describe, not a published process.
HR Screen
Most candidates lose this call inside the first two minutes, during the walkthrough of their own background. The account runs chronologically, sits at the level of tools and titles, and never arrives at a decision anyone could have disagreed with. Anchor on a problem instead of a timeline: what the team could not answer, what you did about it, what happened next. Ninety seconds is enough, and stopping on time leaves room for the half of the call that belongs to you. What you ask about how work gets prioritised signals your level more reliably than the walkthrough does.
What to demonstrate
- Whether your background summary has a shape (problem, decision, consequence) or is a chronological list of tools and employers
- Whether you can account for gaps, short stints and the reason you are looking, unprompted and without hedging
- The substance of the questions you ask back, which an experienced screener reads as a level signal
How to prepare
- Time your opening walkthrough against a clock. If it runs past two minutes, compress the earliest role into a single clause and spend the recovered time on the most recent one
- Write one honest sentence for every gap or short stint visible on your resume and offer it before being asked about it
- Prepare questions about how work arrives and gets prioritised: who writes the request, how often priorities change, and what happens to an analysis after it is delivered
Technical Interviews
A handful of shapes account for most of what gets asked in this format: a ranking or deduplication inside groups, a running or rolling total, a period-over-period comparison, and a cohort tracked forward over time. Recognising the shape quickly is most of the speed here; deriving it from scratch while a clock runs is where the time goes. Know that a window function keeps every row while a GROUP BY collapses them, and know which one the question needs. If the exercise is in Python instead of SQL, the same shapes arrive as groupby with transform, shift and merge, and the same grain mistakes are available.
What to demonstrate
- Whether you reach the right construct without a detour, such as ROW_NUMBER over a partition to deduplicate instead of a self-join against a MAX subquery
- Whether you know what your window frame actually is, since adding ORDER BY inside OVER changes the default frame and silently changes a running total
- Whether the thing runs. A near-miss that throws an error scores below a plainer query that returns the right rows.
How to prepare
- Write each of the four shapes once from memory against a small schema and keep the working version somewhere you will reread it: dedupe with ROW_NUMBER, a running total, a month-over-month change with LAG, and a retention table
- Compute one running total twice on data with tied timestamps, once on the default frame and once with ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, and look at where the two disagree
- If Python is on the table, rebuild the dedupe and the running total with groupby and cumsum, then assert the two implementations return identical rows
Stakeholder Meetings
An extra round usually exists because something is still open after the standard loop: a skill the earlier interviews did not sample, a level decision, or two interviewers who disagreed. It is rarely a rerun of what you already did well. Ask the recruiter who you are meeting, what function they sit in, and how long the session runs. That is an ordinary scheduling question, and the answer changes what you should prepare. What separates a strong candidate here is treating the round as a fresh evaluation with its own bar, rather than assuming earlier performance carries you through or sinks you.
What to demonstrate
- Whether you can answer well on ground the earlier rounds did not cover, without leaning on what you already said to someone else
- Consistency of the facts in your stories: the same sample size, timeframe, team size and scope of your own role as in earlier conversations
- How you handle an unfamiliar format live, including whether you ask what kind of answer is wanted before producing one
How to prepare
- Ask the recruiter for the interviewer's function, the length, and whether to expect a coding surface, a discussion, or a presentation. Preparing for a 30 minute conversation with a partner team is not the same work as preparing for a 60 minute technical block.
- Write out what each earlier round actually covered, then list the two or three areas nobody probed. That gap is the most likely subject of the extra round.
- Re-read the numbers in the project stories you have already told, so a second telling does not quietly contradict the first.
Where candidates lose points
Randomising players individually for a change that acts on a shared pool.
Matchmaking parameters, queue populations, tradeable-item supply and event leaderboards are shared resources: a treated player is matched against a control player, and treated supply lands in the control arm's market. The treatment leaks across arms, so the measured difference understates or reverses the true effect, and the control arm is no longer a clean counterfactual. Cluster by region and mode, or switchback on time slices with a burn-in long enough to clear carryover in the shared state.
Testing revenue with a difference in means on a heavy-tailed spend distribution.
Player spend is approximately log-normal with a long Pareto-like tail, so the variance of the sample mean is dominated by a few accounts and the central limit approximation converges slowly at realistic sample sizes. A t-test on raw ARPDAU can flip sign when one account buys a large currency pack. Winsorising or capping at a pre-registered percentile, or testing conversion and conditional spend separately, fixes the variance problem, but note the cost: capping biases toward zero exactly when the true effect lives in the tail, so state the cap before you look.
Reading experiment results before checking the arm split
Compare observed arm counts against the intended allocation ratio, not an assumed even split, and set the alarm far below the conventional 0.05: at 0.05 roughly one healthy experiment in twenty trips it, which is why sample-ratio checks usually run at p < 0.001 or stricter. The test's power scales with sample size, so it misses a real diversion on a small experiment and fires on an imbalance too small to move the estimate on a very large one. A flag means go find the assignment or logging fault before reading any outcome, not report a mismatch.
Over-explaining the method and under-explaining the implication
Lead with the answer and what you would do about it, then give the approach when asked. Roughly one sentence of method per three of implication is the right ratio for a stakeholder-facing answer; the interviewer already knows what a regression is.
Questions & practice
These questions assess your foundational grasp of probability theory, hypothesis testing, and statistical modeling in practical scenarios.
Medium · statistics and probability
Approach
- Sanity-check the answer against a simple bound or a simulated case.
- Write down the assumption the method needs before you use the method.
- Quantify uncertainty explicitly rather than reporting a point estimate alone.
Follow-up
- What sample size would you need to detect an effect half this size?
- Which assumption here is most likely to be violated in practice?
Design an A/B test for a new matchmaking algorithm in a competitive multiplayer title, defining your primary metric and sample size requirements.
Medium · machine learning and modelling
Approach
- Frame the prediction: the label, the moment of prediction, and the action it triggers.
- Check what information would not exist at prediction time, and exclude it.
- Pick an evaluation metric that matches the cost of each error type, not a default.
Follow-up
- What would you monitor after launch to know the model is still valid?
- Where could label leakage enter this setup?
Rebuild running balances and find the first ledger divergence
Easy · running balance · reconciliation · sort stability
Using the same ledger frame plus balance_after (int64), rebuild each player's running balance per currency_code by accumulating delta_amount in (occurred_at, ledger_id) order, then report the first row per (player_id, currency_code) where your computed balance disagrees with balance_after. You may not use cumsum, groupby().cumsum() or expanding(); accumulate explicitly. Assume each group starts from a balance of zero. Reversals and corrections do count toward the balance. Return player_id, currency_code, ledger_id, computed_balance, balance_after and diff, sorted by absolute diff descending.
Approach
- Sort once on (player_id, currency_code, occurred_at, ledger_id). The ledger_id tiebreak is load-bearing: entries routinely share occurred_at to the second, and an unstable order manufactures divergences that move between runs.
- Accumulate in one pass over itertuples with a dict keyed by (player_id, currency_code), resetting to zero when the key changes. One pass is O(n) and beats any reshape; the ban on cumsum is about showing the accumulation, not about speed.
- Record the first index per key where computed != balance_after and then stop tracking that key. After a break every later row inherits the same offset, so reporting all of them buries the single row that matters.
- Keep reversals and corrections in the accumulation. They are genuine balance movements, which is the exact opposite of the rule for flow aggregates, and mixing the two rules up is the common error in this domain.
- Sort the output by abs(diff) descending. A one-unit diff is usually a rounding artefact somewhere upstream; a six-figure diff is a lost grant, and the ordering puts the second kind on the first screen.
Worked solution (25 min)
- Sort on the four-column key and reset the index so positional recording is unambiguous.
- Walk the frame once, carrying a dict of running balances and a set of keys already flagged, appending a record the first time a key disagrees.
- Assemble the flagged records into a frame, compute diff = computed_balance - balance_after, and sort by its absolute value descending.
- Spot-check by re-accumulating one flagged group by hand from its first row and confirming the break lands on the reported ledger_id.
Expected result: At most one row per (player_id, currency_code), each identifying the earliest ledger_id at which the accumulated balance and the stored balance_after part company, with diff carrying the signed size of the break.
Verify it yourself
- A group with no break contributes zero rows, not a row with diff 0.
- Re-running on a shuffled copy of the input reproduces the identical output, which is what the ledger_id tiebreak buys you.
- Injecting a single corrupted balance_after into a clean group produces exactly one flagged row, at that row's ledger_id.
- The sum of delta_amount up to the flagged row, computed independently, equals your computed_balance.
Follow-up
- Your check assumes each group starts at zero. How would you distinguish a player whose early history was trimmed by a retention policy from one who genuinely started at zero?
- Divergence appears on exactly one currency and only after a specific date. What is your next query?
- How would you run this as a daily monitor over hundreds of millions of rows without rebuilding every balance from the beginning of time?
Extract daily active user trends using advanced joins and conditional aggregation over telemetry log tables.
Medium · sql and data manipulation
Approach
- Say which table is the grain you start from, and join outward from it.
- Handle the rows that do not match: a LEFT JOIN with a NULL check is usually the question.
- Compute rates by summing numerator and denominator separately, never by averaging rates.
Follow-up
- How does the query change if the join becomes one-to-many?
- What breaks if events arrive late or out of order?
Optimize a poorly performing SQL query that aggregates microtransaction purchases across millions of global accounts.
Medium · sql and data manipulation
Approach
- Handle the rows that do not match: a LEFT JOIN with a NULL check is usually the question.
- Say which table is the grain you start from, and join outward from it.
- State the window function and its partition and ordering out loud before writing it.
Follow-up
- What breaks if events arrive late or out of order?
- How does the query change if the join becomes one-to-many?
Report per-SKU net revenue alongside granted premium currency
Medium · fan-out · pre-aggregation · revenue
fct_iap_transaction has transaction_id, player_id, sku_id, purchase_ts, price_usd_net, status. fct_currency_ledger has transaction_id (nullable), currency_code, delta_amount, source_system. A single completed purchase can produce several ledger rows with source_system = 'iap_grant': the base pack, a first-purchase bonus and an event bonus are separate entries. For last month's completed transactions, return per sku_id the transaction count, net USD revenue and total premium currency granted. Write it so the revenue figure is not inflated by the grant rows.
Approach
- State the grain of each side before writing any join. fct_iap_transaction is one row per transaction; the iap_grant rows are one-to-many against it. Joining them directly produces a result at grant grain, where any SUM of price_usd_net counts each purchase once per grant row.
- Pre-aggregate the ledger to transaction grain in a CTE: SUM(delta_amount) FILTER (WHERE currency_code = 'premium') grouped by transaction_id, restricted to source_system = 'iap_grant'. The CTE is now one row per transaction and the join becomes many-to-one.
- Left join that CTE onto the filtered transactions so a completed purchase that produced no grant row still appears with a NULL grant total. That combination is a fulfilment bug worth surfacing, not hiding behind an inner join.
- Filter transactions on status = 'completed' and purchase_ts inside last month. Because this table mutates in place, a transaction later refunded now carries status = 'refunded' and is already excluded; say that out loud rather than adding a redundant refund subtraction that would double-count the adjustment.
- Aggregate to sku_id and add a count of transactions with no matching grant, so the fulfilment gap is legible in the same output instead of needing a second query.
Worked solution (25 min)
- Count transactions in the window, then count the iap_grant ledger rows referencing them. That ratio is how badly a naive join would inflate revenue.
- Write the grant CTE grouped by transaction_id and verify its row count is at most the transaction count.
- Join, filter on status and purchase_ts, and aggregate by sku_id.
- Cross-check total net revenue against a standalone SUM over fct_iap_transaction with the same filters and no join at all. The two must be identical.
Expected result: One row per sku_id with transactions (count of completed purchases), net_revenue_usd (sum of price_usd_net, matching the join-free total exactly), premium_granted (sum of premium delta_amount, NULL or zero where the SKU grants no premium currency) and ungranted_transactions.
Verify it yourself
- net_revenue_usd summed across all rows must equal a plain SUM(price_usd_net) over the same filtered transactions with no join. Any excess is fan-out.
- The grant CTE's row count must be less than or equal to the number of distinct transaction_ids appearing in the ledger window.
- SKUs that are not currency packs, such as directly purchased cosmetics, should show premium_granted of NULL or zero rather than a positive figure.
Follow-up
- How would you prove the fan-out was really happening, using only row counts and no prior knowledge of the data?
- Last month's transactions have not finished refunding. At what cohort age would you publish this table, and what do you show a stakeholder in the meantime?
- A currency_pack SKU shows net revenue but zero granted premium currency. Walk me through the next three queries you run.
A core feature in our live-service game experienced a sudden metric drop in daily active users over the weekend. How would you investigate and diagnose this issue?
Medium · product sense and metrics
Approach
- Decompose the metric into the rates that drive it, and say which one you would check first.
- Fix the population and the time window before naming any metric.
- Restate the decision this analysis has to support, and who acts on the answer.
Follow-up
- How would you detect that the metric is being gamed rather than genuinely improving?
- Which segment would you cut first, and what would that rule out?
How would you design a product metric framework to measure engagement in a newly launched multiplayer game mode?
Medium · product sense and metrics
Approach
- State what result would change your recommendation, so the answer is falsifiable.
- Decompose the metric into the rates that drive it, and say which one you would check first.
- Fix the population and the time window before naming any metric.
Follow-up
- Which segment would you cut first, and what would that rule out?
- How would you detect that the metric is being gamed rather than genuinely improving?
How would you measure the success of an in-game store redesign intended to drive cosmetic purchases?
Medium · product sense and metrics
Approach
- Decompose the metric into the rates that drive it, and say which one you would check first.
- Restate the decision this analysis has to support, and who acts on the answer.
- Name one primary metric, then the guardrail that stops it being gamed.
Follow-up
- How would you detect that the metric is being gamed rather than genuinely improving?
- What would you do if the primary metric and the guardrail moved in opposite directions?
These questions focus on defining key performance indicators and applying predictive or deep learning models to game telemetry.
Medium · product sense and metrics
Approach
- Restate the decision this analysis has to support, and who acts on the answer.
- State what result would change your recommendation, so the answer is falsifiable.
- Fix the population and the time window before naming any metric.
Follow-up
- Which segment would you cut first, and what would that rule out?
- What would you do if the primary metric and the guardrail moved in opposite directions?
Interviewers use these questions to gauge your understanding of randomized controlled trials, experimental design, and causal inference in live environments.
Medium · a/b testing and experimentation
Approach
- Name the guardrails that would stop a launch even on a positive primary result.
- Decide the analysis before seeing data, including how long it runs and when you look.
- State the primary metric and the minimum effect worth shipping, then size the test.
Follow-up
- What would you conclude if the result is positive but the test is underpowered?
- How would you handle interference between treated and control units?
How would you calculate the p-value and statistical significance for an online experiment with highly skewed player spend distributions?
Medium · a/b testing and experimentation
Approach
- Say whether units interfere with each other, and switch design if they do.
- State the primary metric and the minimum effect worth shipping, then size the test.
- Name the guardrails that would stop a launch even on a positive primary result.
Follow-up
- How would you handle interference between treated and control units?
- What would you do if you could not randomise at all?
Two players are matched in an online soccer game. Before the game begins, predict who will win.
Medium
Approach
- Clarify what is being asked and what a complete answer would contain.
- State your assumptions explicitly before working the problem.
- Work from the decision backwards to the evidence you would need.
Follow-up
- How would you know your answer was wrong?
- What assumption would you test first?
Size an activation test on install-to-first-match conversion
Easy · power analysis · mde · conversion
Install-to-first-match conversion at 24 hours currently runs at 62.0%: players in the daily install cohort with a fct_match_participant row where started_at < install_ts + 24h and result <> 'abandon', over all non-test installs in that cohort. A tutorial change is expected to add roughly 1.5 percentage points. You receive 9,000 non-test installs a day, bucketed 50/50 at install time, with no interaction between players. At alpha 0.05 two-sided and 80% power, give the per-arm sample size and the calendar time needed, and say what you would do if that runtime is refused.
Approach
- Confirm the unit first. One install is one independent observation here because nothing about a tutorial change reaches another player, so a two-proportion calculation is valid. That check is what separates this from a matchmaking or economy change, where it would not be.
- Apply the two-proportion formula with n per arm = (z_{1-alpha/2} + z_{1-beta})^2 * [p1(1-p1) + p2(1-p2)] / delta^2. With z_{0.975} + z_{0.80} = 1.960 + 0.842 = 2.802, the squared term is 7.849; p1(1-p1) = 0.2356, p2(1-p2) = 0.2318, delta^2 = 0.000225.
- Convert to calendar time: 4,500 installs per arm per day gives 3.6 days of intake, and because the metric is only evaluated once the cohort is 24 hours old, the last cohort needs another day. Five days is the floor.
- Round up to seven days for a reason you can state: a five-day window weights the estimate toward weekday install mix, so the effect you measure is conditional on that mix. Both arms are contemporaneous, so this is an estimand question, not an imbalance question.
- If five to seven days is refused, name the honest levers rather than fudging: raise the target effect, reduce variance by post-stratifying on install_platform and acquisition_channel, accept lower power and write the resulting MDE down in advance, or pick a lower-variance proxy. Reading the test daily at nominal 0.05 is not on the list.
Worked solution (15 min)
- Set p1 = 0.620, p2 = 0.635, delta = 0.015, alpha = 0.05 two-sided, power = 0.80.
- Compute (1.960 + 0.842)^2 = 7.849 and p1(1-p1) + p2(1-p2) = 0.2356 + 0.2318 = 0.4674.
- Compute n per arm = 7.849 * 0.4674 / 0.000225 = 16,305, so 32,610 installs in total.
- Divide by 4,500 installs per arm per day to get 3.6 days of intake, add one day of cohort maturity, and recommend a seven-day run.
- State the MDE you would actually get from a three-day run, so the trade-off is explicit if runtime is cut.
Expected result: About 16,305 installs per arm, 32,610 total; 3.6 days of intake plus 24 hours of maturity, so 5 days minimum and 7 days recommended to cover a full weekly install mix.
Verify it yourself
- The pooled-variance form 2 * (z_{1-alpha/2} + z_{1-beta})^2 * pbar(1-pbar) / delta^2 with pbar = 0.6275 gives 16,308, within 0.1% of the unpooled figure.
- Sample size scales as 1/delta^2: halving the target effect to 0.75 points should quadruple the requirement to roughly 65,000 per arm.
- Power at the computed n, checked by simulation of two binomials, should come out near 0.80 rather than near 0.5.
Follow-up
- The team wants D7 retention from the same test. What happens to the runtime, and why is it not simply seven more days?
- Installs are 70% mobile. Would you power the mobile-only subgroup, and what would that cost in runtime?
- The observed effect lands at +1.4 points with p = 0.08. What do you report, and what would you have needed to decide beforehand to make that report credible?
Revenue per player-day rose while every channel got worse
Hard · mix shift · guardrails · revenue decomposition
Net revenue per active player-day over a trailing 28 days rose from $0.1440 to $0.1656, up 15%, while net revenue per active player-day fell about 4% inside every acquisition_channel. Organic share of player-days went from 60% to 75% as a cross-promotion campaign ended. Using dim_player (player_id, acquisition_channel), fct_session (player_id, session_start_ts) and fct_iap_transaction (player_id, purchase_ts, price_usd_net, status), decompose the rise and state whether monetisation improved.
Approach
- Rebuild the metric from segment parts and confirm the rebuild before decomposing. For each channel and period, compute player-days, net revenue and their ratio, then check the player-day-weighted channel ratios reproduce both reported overall figures. A decomposition on a table that does not rebuild the headline attributes arithmetic error to mix.
- Apply the two-term split with the weighting stated. Mix effect is the sum over channels of (share_after minus share_before) times ratio_before; within-segment effect is the sum over channels of share_after times (ratio_after minus ratio_before). Name the pairing, because the alternative assigns the interaction term to the other side.
- Recognise that the denominator moved for a reason unrelated to monetisation. A cross-promotion campaign ending removes player-days that carried near-zero revenue, so the blended average rises mechanically while nothing about how players monetise improved. Check the absolute player-day counts, not only the shares, to confirm the shift came from the low-monetising channel shrinking rather than the high-monetising one growing.
- Run the guardrails attached to this metric rather than stopping at the decomposition. Compute the share of net revenue contributed by the top 1% of payers, and D28 retention of players who have never completed a purchase. A rise in the first alongside a fall in the second means the within-segment decline is a real deterioration of the free experience, not noise.
- Decompose the within-segment 4% one level further into paying conversion per player-day and net spend per converting player-day, so the recommendation points at a mechanism. These are different problems with different owners.
- State the verdict without hedging: total net revenue and the per-channel ratios are the numbers that answer whether monetisation improved, and both say it did not. The blended metric rose because its denominator shrank where revenue was thinnest.
Follow-up
- Total net revenue in absolute dollars is the obvious cross-check. What does it do here, and why is it not sufficient on its own?
- The cross-promotion channel was unprofitable per player-day. Does ending it make this metric a better or a worse measure of the business?
- What would you put in place so that a denominator change of this size cannot be reported as a monetisation win again?
Suggested practice checklist
For someone who has spent the last year in notebooks, dashboards or modelling work and has not written raw SQL under time pressure. The first four days rebuild query fluency against a fixture you control and can verify by hand; the last three attach that fluency to the rest of the loop.
Day 1 — Build a fixture you can check answers against
- Create a local Postgres or SQLite database with four tables (users, sessions, events, orders) holding roughly 200 rows you generated yourself, so you know the contents well enough to predict every result.
- Deliberately seed the cases that break queries: a user with no sessions, a session with no events, two orders sharing a timestamp, a NULL in one join key, and one duplicated user row.
- Before writing any SQL, hand-compute five answers on paper (how many users placed at least one order, median orders per ordering user, and three others) and save them as the ground truth for the week.
Deliverable: A one-command seed script plus a text file of five hand-computed answers to grade every later query against.
Day 2 — Joins, filters and NULL semantics
- Answer "which users have no orders" three ways (LEFT JOIN with IS NULL, NOT EXISTS, NOT IN) and confirm that the NOT IN version returns zero rows once the subquery contains a NULL, because the comparison is never TRUE.
- Reproduce the LEFT JOIN that silently collapses to an inner join by putting a right-table predicate in WHERE, then fix it by moving the predicate into the ON clause, and record both row counts.
- Create a fan-out bug on purpose by joining orders to order_items and summing the order total, then correct it with a pre-aggregated subquery and explain in one line which table changed the grain.
Deliverable: One annotated .sql file holding the three join traps, each with the wrong result and the corrected result side by side.
Day 3 — Window functions and frames
- Write three window queries against the fixture: a running order total per user, the rank of each order within its user by value, and the day gap to that user's previous order, then check each against the day-one ground truth.
- Run ROW_NUMBER, RANK and DENSE_RANK over a column containing ties, print all three side by side, and write one sentence on when each is the correct choice.
- Switch one query from the default frame (RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, which is what you get when ORDER BY is present and no frame is written) to ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, and explain why the output differs only when the ORDER BY column has duplicates.
Deliverable: Three verified window queries plus a short note explaining the RANGE versus ROWS difference in your own words.
Day 4 — The four analytical query patterns
- Write a monthly retention grid: first order month per user, then months-since-first as the column, and verify that month zero equals the cohort size exactly.
- Sessionize the events table under a 30-minute inactivity rule using LAG plus a cumulative sum over a new-session flag.
- Build a four-step funnel that counts distinct users rather than events at each step, and state the rule you applied to a user who reaches step three without ever logging step two.
Deliverable: One file with the retention, sessionization and funnel patterns, each carrying a one-line note on the assumption it bakes in.
Day 5 — Write SQL the way you will have to write it live
- Set a 12-minute timer and solve three medium prompts in a plain editor with no execution and no autocomplete, then run them and tally syntax errors separately from logic errors.
- Narrate one solution aloud while writing it, stating the grain of each intermediate result (one row per user, one row per user-day) before you type its body.
- Rewrite your slowest solution as a CTE chain where every CTE name states its grain, and time yourself re-solving it from blank.
Deliverable: A recording of one narrated solution plus an error tally that separates syntax from logic.
Day 6 — One day for everything that is not SQL
- Write the preconditions of the two-sample t-test from memory, then check them: independent observations, and a difference in means whose sampling distribution is approximately normal, which at large sample sizes follows from the central limit theorem rather than from normality of the raw values.
- Write the difference between an odds ratio from logistic regression and a relative risk, and state the condition under which the two are close (low outcome prevalence).
- Prepare a 90-second answer to "how would you know this model is any good" that names the metric, the baseline you would beat, and the cost of the errors you care about.
Deliverable: One page of notes covering test preconditions, the odds-ratio caveat and the model-quality answer.
Day 7 — Full loop rehearsal
- Run a 45-minute mock with someone willing to interrupt: 20 minutes of SQL, 15 minutes defining a metric, 10 minutes on a past project.
- Re-solve from blank the two queries you were slowest on this week and compare the times against day five.
- Write a five-line answer to "walk me through a project" that puts a number in the first sentence and names the decision the work changed.
Deliverable: Mock feedback notes plus a timed project narrative you can deliver without reading it.
Stories where someone disagreed with you
Have two ready. In one, the data was on your side and you had to move someone who outranked you. In the other, the pushback was correct and you changed position. The second is the harder story and it lands better, because it shows you separate being right from being attached to an answer. Name the person's actual objection.
These scenarios explore how you collaborate with cross-functional partners, navigate ambiguity, and drive impact within studio teams.
Medium · behavioural and stakeholder questions
Approach
- Pick a story where you drove the decision, not one where you observed it.
- Name the disagreement or constraint, and how you resolved it with evidence.
- State the situation in two sentences and spend the rest on your reasoning.
Follow-up
- What did you decide not to do, and why?
- What would you do differently if you ran that project again?
Turn 'engagement is down' into a scoped answerable brief
Easy · scoping · release calendar · triage · metric definition
A studio lead messages: engagement is down, can you look into it. You have dim_player, fct_session, fct_match_participant and the release calendar. Daily core-loop players, defined as distinct players per UTC day with at least one match reaching a terminal result other than abandon, is down 6% week over week, and a content release landed nine days ago. You have 30 minutes before a standup. Produce the scoping questions you would ask, the first three cuts you would run, and the one-paragraph brief you send back before doing deeper work.
Approach
- Pin the metric and the comparison before touching data. Ask which number the lead actually saw and over what window, because a week-over-week read nine days after a release is measuring post-release decay by construction, and that alone may be the whole answer.
- Ask the decision question, not more metric questions: what would the lead do differently if this turns out to be new players versus returning, one platform versus all, one region versus global. Scope follows the decision, and an investigation with no decision attached should be declined or deferred.
- Run three cheap cuts that split the space rather than confirm a hunch. First, new versus existing by install cohort age, which separates an activation problem from a retention problem. Second, platform crossed with app_version, where a bad build shows as a concentration of fct_session.ended_reason = 'crash' and truncated duration_seconds. Third, server_region, where an infrastructure incident shows as elevated match abandon rate and p95 matchmaking wait rather than as fewer app opens.
- Re-baseline against the matched day in the previous release cycle instead of against last week, so the comparison is not dominated by the release calendar.
- Send a brief that states what you confirmed, what you ruled out, the current best explanation with its confidence, and the size of the next block of work with the question it would close.
Follow-up
- The crash concentration is on one device_model at one app_version. What do you send, to whom, and how urgently?
- How would you tell a genuine drop apart from an instrumentation change that altered which sessions get logged?
- If all three cuts come back flat, what is your fourth cut and why that one?
Disagree with a queue-tolerance change using guardrail data
Medium · guardrails · conditioning on success · switchback · disagreement
A product manager proposes widening matchmaking skill tolerance in one region because p95 matchmaking wait there is 71 seconds against 34 seconds elsewhere. You have fct_match_participant with matchmaking_wait_seconds, skill_rating_before, result, disconnected, server_region and game_mode. Your concern is that the wait percentile is computed only over matches that actually formed, and that wider tolerance raises mismatch and abandon rate. The PM has a deadline and reads your concern as blocking. Make the disagreement in a way that keeps the decision moving, and state the evidence that would change your mind.
Approach
- Agree on the problem in the first sentence. The wait gap is real and worth fixing; the disagreement is about the price of this particular fix, which is a far easier conversation than one that sounds like a veto.
- Name the measurement defect concretely rather than generally. p95 matchmaking wait is an order statistic over matches that formed, so players who gave up in queue are absent from it entirely. A queue that fails outright reports an excellent p95. Pair it with a queue-abandonment rate measured on queue entries, and with match abandon rate defined over result = 'abandon' or disconnected = true.
- Bring a quantity instead of an objection. From current data, bucket matches by absolute skill gap between teams using skill_rating_before and plot abandon rate and disconnect rate per bucket, cut by game_mode and server_region. That converts 'mismatch is bad' into a rate per unit of tolerance, which the PM can trade against seconds of wait.
- Propose the design that settles it, and explain why the obvious one is invalid: queue population is shared, so a treated player is matched against a control player and the treatment leaks across arms. Randomise by region and mode cluster, or switchback on time slices with a burn-in long enough for the queue to clear, and analyse at the randomisation unit.
- State your falsifier explicitly, for example that if abandon rate is flat across the top two skill-gap buckets in this region, you withdraw the objection.
- Give the PM something shippable inside the deadline, such as a bounded tolerance increase in the worst hours only, with the guardrail thresholds pre-registered before launch.
Follow-up
- How long a burn-in would you use for a switchback here, and what in the data tells you it is long enough?
- Queue-abandonment is not in the tables listed. What would you instrument, and what would you do for this decision in the meantime?
- The PM ships it globally without a test. What do you measure afterwards, and what can that measurement honestly support?