Interview guides · Binance
Binance Data Scientist Interview Questions & Guide 2026
Diagnose a metric move for Binance data scientist prep: split by segment, rule out a logging change, separate mix shift from behaviour.
The 60-second brief
As a Data Scientist at Binance, you operate at the intersection of massive-scale user data, cutting-edge cryptocurrency markets, and high-velocity product execution. You play a critical role in shaping how millions of global users interact with the world's leading blockchain ecosystem. Your insights directly influence major business decisions, from optimizing search and recommendation algorithms to mitigating risk, securing user trust through robust KYB/KYC operations, and driving sustainable market growth.
Nearly every loop contains a round whose deliverable is a recommendation to someone non-technical. Practise stating a conclusion, the confidence attached to it, and the cost of being wrong in each direction, because that triple is the artifact being graded.
Focus areas
- Power experiments for heavy-tailed account revenue
- Measure churn only on renewal-eligible accounts
- Read NRR on a fixed account cohort
Overview
This role requires a unique blend of heavy technical execution, sharp business intuition, and unwavering resilience. You will collaborate closely with product managers, software engineers, and operations teams to solve complex, ambiguous problems unique to the digital asset space. Whether you are designing experiments to measure feature adoption or building predictive models to detect anomalies, your work moves at an exceptionally fast pace and demands a high degree of autonomy.
Expect an environment characterized by immense scale, high stakes, and intense intellectual rigor. Binance values data-driven execution and expects you to back your recommendations with rigorous statistical proof while keeping a pulse on broader product and market dynamics. Success here means translating complex datasets into clear, actionable business strategies that scale globally.
The Binance interview process
Binance candidates report 4 rounds · ≈ 3-5 weeks. The stages below are what candidates describe, not a published process.
Resume Review
Whoever runs this call is usually not a practitioner. They take notes, and a hiring manager skims those notes later, so the real question is whether your work survives being written down by someone outside the field. Test every project sentence against that: could a non-specialist repeat it correctly without knowing what a propensity score is? Carry a plain-language version of each project and one reason you want this particular role that you could not copy onto another application. Vagueness at this stage reads as inexperience, even when the underlying work was genuinely deep.
What to demonstrate
- Whether a non-specialist can restate your projects accurately, since their paraphrase is what reaches the hiring manager
- Whether your reason for wanting the role points at the work itself rather than the company's reputation
- Whether your language signals the level being screened for: what you decided yourself versus what you were handed
How to prepare
- Write a two-sentence, jargon-free version of each major project: the question nobody could answer, and the decision your work changed. Read it to someone outside data and have them repeat it back
- Point your 'why this role' answer at something concrete in the job description or the product surface you would be working on, and keep it to two sentences
- Have two questions ready about measurement: which metric the team is held to, and who acts on an analysis once it lands
HR Screening Call
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 Assessments
Much of what gets scored here happens out loud while you type. Nobody can see your reasoning inside a half-written query, so five silent minutes read as being stuck even when they are not. State the plan in plain language first: which tables, what grain you are aggregating to, and the one filter that defines the population. Then write it. The narration doubles as insurance, because a wrong plan gets caught early and cheaply while a wrong query gets caught at the end with no time left to redo it. A timed statistics section, where one exists, is a separate test with its own clock.
What to demonstrate
- Whether the query you write matches the plan you just described
- What you do with a hint, meaning whether the correction gets absorbed or the first approach gets defended
- Whether you can debug your own wrong output by reading the result set and naming which part of the query produced the anomaly
How to prepare
- Solve three problems while screen-sharing into a recording, then watch it back and mark every stretch longer than thirty seconds where you said nothing
- Practise compressing the plan into one sentence before typing, then check afterwards whether the finished query actually matched it
- Time yourself on statistics questions that carry a business reading, such as what a confidence interval does and does not claim, rather than re-reading notes without a clock
Interview Rounds
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
Reporting a mean over accounts when account revenue is heavy-tailed
When a small number of accounts hold most of the revenue, the sample mean is dominated by whichever of them happens to be in the sample, and the sample variance keeps growing as more data arrives instead of stabilising. In that regime the usual central-limit-based confidence interval understates uncertainty, and a single renewal or a single large account's batch job can flip the sign of a measured effect. The fixes are to pre-register a winsorisation or capping rule before looking at the outcome, to report account counts crossing a threshold alongside the revenue figure, or to define the estimand on a bounded transform. Choosing the cap after seeing the result is a separate and worse problem, because the cap then encodes the answer.
Computing monthly churn against the entire customer base when contracts are annual
An annual contract has no opportunity to churn except at its renewal date, so an account that is eleven months from renewal is in the denominator while being incapable of appearing in the numerator. The resulting rate is smaller than the real one by roughly the ratio of the base to the renewal-eligible base, and it oscillates with the seasonality of when deals were originally signed rather than with anything about the customers. The corresponding trap on the other side is counting a churn on the date the record was updated rather than on term_end_date, which shifts losses into whichever month the operations team did its paperwork.
Optimising accuracy on a heavily imbalanced target
State the base rate first, then choose the metric from the relative cost of a false positive against a false negative: precision and recall at the operating threshold, PR-AUC, or expected cost. At a 1 percent positive rate, predicting the majority class for everyone scores 99 percent accuracy and is worthless.
Writing SQL without stating NULL and tie-breaking behaviour
Before calling a query finished, say what it does with NULLs, ties and empty groups. NOT IN against a subquery containing a single NULL returns no rows at all, and RANK, DENSE_RANK and ROW_NUMBER differ precisely on ties, so name which one the question requires.
Questions & practice
When is it appropriate to use non-parametric tests instead of standard t-tests?
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.
- Say what the estimate is of, and over what population it generalises.
Follow-up
- How would you explain this result to someone who does not know statistics?
- What sample size would you need to detect an effect half this size?
Sessionise an API event stream with a 30-minute inactivity gap
Medium · sessionisation · event-streams · pandas
fct_api_request arrives as a DataFrame with account_id, user_id, request_at (tz-aware UTC), traffic_class and http_status, roughly 5 million rows. Assign a session_id to every human-attributable request: drop rows where user_id is null or traffic_class is in ('ci','synthetic_monitor','load_test'), then open a new session whenever the gap since that user's previous remaining request exceeds 30 minutes. Return the filtered frame plus session_id, and a per-session summary with user_id, account_id, session start, session end and request count. Do not loop over rows.
Approach
- Settle the filter-then-gap ordering before writing code. Removing CI and synthetic rows changes the gaps, so sessionising the raw stream and filtering afterwards is a different answer; the definition given filters first, and the two diverge most for accounts whose CI runs every ten minutes.
- Sort once by (user_id, request_at) with a stable kind, then gap = df.groupby('user_id', sort=False).request_at.diff(). The first row of each user yields NaT, which is exactly the boundary condition you want rather than a special case to patch.
- new_session = gap.isna() | (gap > Timedelta(minutes=30)); session_id = new_session.cumsum(). The cumsum runs over the whole sorted frame and therefore produces globally unique ids in one pass; a per-user cumcount collides across users and forces a composite key on every downstream join.
- Build the summary with a single groupby('session_id').agg(...). user_id and account_id can be carried with 'first' only because the sort key groups them — state that dependency, since it silently breaks if someone later re-sorts the frame.
- Decide explicitly what a session means when one user_id holds memberships in several accounts: either add account_id to the sort and group keys, or document that sessions may cross accounts. Leaving it undecided produces sessions whose account_id is whichever row sorted first.
Follow-up
- Where does 30 minutes come from, and how would you pick it from this data instead of from convention?
- An engineer reused their personal key for a nightly batch job, so machine traffic carries a human user_id. How would you detect that, and should those requests form sessions?
- How much does the session count change if you sessionise before dropping CI traffic rather than after?
Simulate how the renewal calendar distorts monthly churn rates
Medium · simulation · churn · numpy · cohorts
Simulate 1,200 accounts on annual contracts. Draw each account's renewal month from a deliberately lumpy calendar: 30% renew in January and the remaining 70% are spread evenly over the other eleven months. At each renewal an account churns with probability 0.18, independent of month; survivors renew and come back twelve months later. Run 24 simulated months. For each month compute two rates: churned accounts over all live accounts, and churned accounts over accounts whose term ended that month. Report the mean and the month-to-month standard deviation of each series, and state which one belongs in an executive summary.
Approach
- Build the panel with numpy state arrays rather than a per-account loop: a next_renewal_month vector, an alive boolean vector, and a loop over the 24 months only. Looping over 24 months is fine; looping over 1,200 accounts inside it is what makes the simulation too slow to iterate on.
- Maintain the live set honestly. An account that churned in month m must leave the denominator from m+1 onward and can never be renewal-eligible again; if it stays, the naive rate drifts downward for reasons that have nothing to do with churn and the calendar effect gets buried.
- Compute both series over the same months and compare dispersion, not only level. The eligible-base rate should sit near 0.18 with binomial noise scaled by that month's renewal count; the naive rate spikes in January and collapses in thin months.
- Quantify the gap instead of describing it: the ratio of the two means is roughly the reciprocal of the average monthly renewal-eligible fraction, and the naive series' standard deviation is driven by the signing calendar rather than by customer behaviour.
- Check against the closed form before trusting the output. With the live set maintained correctly the eligible-base rate is an unbiased estimator of 0.18 in every month, so a systematic offset means the bookkeeping is wrong, not that the simulation found something.
Worked solution (30 min)
- rng = np.random.default_rng(0); p = [0.30] + [0.70/11]*11; next_renewal = rng.choice(12, size=1200, p=p); alive = np.ones(1200, bool)
- For m in range(24): eligible = alive & (next_renewal == m); churn = eligible & (rng.random(1200) < 0.18); record churn.sum(), eligible.sum(), alive.sum() at month start; alive &= ~churn; next_renewal[eligible & ~churn] += 12
- naive = churned / live_at_start; eligible_rate = churned / eligible, left as NaN where eligible == 0.
- Report naive.mean(), naive.std(ddof=1), np.nanmean(eligible_rate), np.nanstd(eligible_rate, ddof=1) and the ratio of the two means.
Expected result: The eligible-base rate averages close to 0.18 (roughly 0.17 to 0.19 at this sample size) with no January spike. The naive rate averages near 0.015, about one twelfth of it, and its January values run several times a typical month, so its month-to-month standard deviation is a large fraction of its own mean.
Verify it yourself
- The 24 monthly churn counts sum to 1200 minus the final alive count.
- Switching the calendar to uniform (p = 1/12 everywhere) collapses the naive series' dispersion while leaving the eligible-base mean unchanged.
- Setting the churn probability to 0 makes both series identically zero and leaves 1,200 accounts alive.
- Quadrupling the account count roughly halves the standard deviation of the eligible-base rate without moving its mean.
Follow-up
- Compounded over twelve months the naive rate lands close to the true annual churn. Does that rescue it?
- How would you report churn in a month where only nine accounts were renewal-eligible?
- Eighteen-month terms are now being sold alongside annual ones. What breaks?
Given a table of user events, how would you write a SQL query to find the time elapsed between a user's first and second deposit?
Medium · sql and data manipulation
Approach
- State the window function and its partition and ordering out loud before writing 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 would you verify this result without re-running the same query?
- How does the query change if the join becomes one-to-many?
Write a query using SQL window functions to calculate running 30-day trading volumes for active accounts.
Medium · sql and data manipulation
Approach
- State the window function and its partition and ordering out loud before writing it.
- Compute rates by summing numerator and denominator separately, never by averaging rates.
- Say which table is the grain you start from, and join outward from it.
Follow-up
- How would you verify this result without re-running the same query?
- How does the query change if the join becomes one-to-many?
Find paid seats that never called the API
Easy · anti-join · null semantics · seat utilisation
dim_user_membership carries user_id, account_id, seat_type, is_service_account, deactivated_at and last_seen_at. fct_api_request carries user_id, which is null whenever the caller is a service account or an unattended key, plus account_id, request_at and http_status. For a single account, list every licensed_paid seat with is_service_account = false and deactivated_at null that issued no request at all in the trailing 90 days, returning user_id and last_seen_at. A first draft writes NOT IN against a subquery over fct_api_request.user_id. State exactly what that draft returns and why, then write the correct query.
Approach
- Read the column comment first: user_id is nullable on the fact, so the subquery almost certainly contains at least one NULL for any account that runs a service account or an unattended key.
- Work the three-valued logic out loud. x NOT IN (a, NULL) expands to NOT (x = a OR x = NULL); the second disjunct is UNKNOWN, so for any x not equal to a the whole predicate is UNKNOWN and the row is filtered out. The draft returns zero rows, which reads as full seat utilisation.
- Rewrite as NOT EXISTS with the 90-day and status predicates inside the correlated subquery. Putting them in the outer WHERE instead turns the anti-join into a different question and silently changes the answer.
- Correlate on both user_id and account_id. A membership is (user_id, account_id) and one person can hold memberships in several accounts, so correlating on user_id alone marks a seat as active because that human was busy somewhere else.
- Filter the seat side to seat_type = 'licensed_paid', is_service_account = false and deactivated_at IS NULL, and say what the resulting count means next to contracted_seats on the current subscription row.
Worked solution (15 min)
- Confirm the hazard with one query: SELECT count(*) FROM fct_api_request WHERE user_id IS NULL AND account_id = :account_id. Any non-zero result proves the draft returns nothing.
- Write the seat side: SELECT user_id, last_seen_at FROM dim_user_membership WHERE account_id = :account_id AND seat_type = 'licensed_paid' AND NOT is_service_account AND deactivated_at IS NULL.
- Attach AND NOT EXISTS (SELECT 1 FROM fct_api_request r WHERE r.user_id = m.user_id AND r.account_id = m.account_id AND r.request_at >= now() - interval '90 days').
- Compare the row count against the same query written as a LEFT JOIN with a WHERE r.user_id IS NULL; the two must agree exactly.
- Divide the active seat count by contracted_seats from the account's current fct_subscription_period row and state the utilisation figure.
Expected result: A list of user_id and last_seen_at for licensed, non-service, active memberships on that account with no request in 90 days. The NOT IN draft returns zero rows whenever the account has any service-account or unattended-key traffic, and returns the right answer only by accident on accounts that have none.
Verify it yourself
- The NOT EXISTS result and the LEFT JOIN ... IS NULL result return identical user_id sets.
- Idle seats plus seats with at least one request in 90 days equals the total licensed non-service active membership count for the account.
- Forcing a NULL into the subquery (union a SELECT NULL) leaves NOT EXISTS unchanged and empties the NOT IN version, which demonstrates the failure on demand.
Follow-up
- Adding AND user_id IS NOT NULL to the subquery also fixes NOT IN. Why is NOT EXISTS still the form you would leave in the repository?
- last_seen_at looks like a shortcut for the whole query. What does it actually record, and where does it disagree with API activity?
- This is a seat-reduction risk list. What threshold would you attach before handing it to an account team, and what happens to seats below it?
How do you prioritize multiple competing analytical requests from different product teams?
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.
- Fix the population and the time window before naming any metric.
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?
What would you do if your variant shows a statistically significant positive result on metric A, but a negative result on metric B?
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.
- Name one primary metric, then the guardrail that stops it being gamed.
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?
How would you define success metrics for a zero-fee trading promotion?
Medium · product sense and metrics
Approach
- Name one primary metric, then the guardrail that stops it being gamed.
- 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?
What key product metrics would you track to measure the success of a new user onboarding flow?
Medium · product sense and metrics
Approach
- State what result would change your recommendation, so the answer is falsifiable.
- Name one primary metric, then the guardrail that stops it being gamed.
- Restate the decision this analysis has to support, and who acts on the answer.
Follow-up
- What would you do if the primary metric and the guardrail moved in opposite directions?
- How would you detect that the metric is being gamed rather than genuinely improving?
How do you determine sample size and power for an experiment with low conversion rates?
Medium · a/b testing and experimentation
Approach
- Name the randomisation unit first; it decides the variance and what the test can detect.
- State the primary metric and the minimum effect worth shipping, then size the test.
- Decide the analysis before seeing data, including how long it runs and when you look.
Follow-up
- What would you do if you could not randomise at all?
- How would you handle interference between treated and control units?
What are common experimentation pitfalls when running tests on crypto trading features with highly volatile user bases?
Medium · a/b testing and experimentation
Approach
- Say whether units interfere with each other, and switch design if they do.
- Name the guardrails that would stop a launch even on a positive primary result.
- State the primary metric and the minimum effect worth shipping, then size the test.
Follow-up
- What would you do if you could not randomise at all?
- How would you handle interference between treated and control units?
How would you design a recommendation system for new trading pairs on the platform?
Medium
Approach
- Clarify what is being asked and what a complete answer would contain.
- Say what you would check first and why it is the highest-information step.
- Work from the decision backwards to the evidence you would need.
Follow-up
- What assumption would you test first?
- How would you know your answer was wrong?
Measure named coverage at an assignment threshold you cannot randomise
Hard · regression-discontinuity · instrumental-variables · selection-bias
Named customer-success coverage is assigned to accounts whose arr_cents at fiscal-year start is at least $50,000. About 88% of accounts above the line receive a named owner and about 9% below it do, through documented exceptions. Roughly 200 accounts sit within $15,000 of the cutoff, and gross logo retention on the renewal-eligible base runs near 0.88. Estimate the effect of coverage on renewal, name the design, state its identifying assumptions and the test for each, and say whether this data can answer the question at all.
Approach
- Say first why the naive comparison is unusable, because the answer turns on it. Coverage is assigned to the largest accounts because they are valuable and to distressed accounts because they are at risk, so selection runs in both directions at once and the naive estimate can come out with either sign depending on which rule dominated. Matching on observed size does not fix it: the risk signal that triggered coverage is the same signal that predicts renewal.
- Name the design and the estimand together. This is a fuzzy regression discontinuity with the $50,000 threshold as an instrument for coverage. The estimator is the Wald ratio, the jump in renewal at the cutoff divided by the jump in coverage probability of 0.88 - 0.09 = 0.79, and the estimand is a local average treatment effect for accounts near $50,000 whose coverage is actually decided by the rule, not the average effect of coverage across the book.
- Fit it as local linear regression on each side with a triangular kernel and a data-driven bandwidth (Imbens-Kalyanaraman, or Calonico-Cattaneo-Titiunik with bias-corrected robust intervals). A global polynomial fitted across the whole ARR range is not a substitute and is well documented to manufacture spurious jumps at interior points.
- Test the assumptions instead of asserting them. Run a density test for bunching just above $50,000, which is exactly what a sales team structuring a deal at $50,001 to win coverage would produce and which invalidates the design outright. Check continuity at the cutoff of employee_band, acquisition_channel, deployment_model and pre-period usage. Run placebo cutoffs at other ARR values, and plot the estimate against bandwidth.
- Do the power arithmetic before promising an answer. With about 100 accounts per side and a renewal base rate of 0.88 (standard deviation 0.325), the sharp-design MDE at 80% power and two-sided 0.05 is 2.80 * 0.325 * sqrt(2/100) = about 12.9 percentage points, and dividing by the 0.79 first stage gives about 16 points on the fuzzy estimand. No plausible effect of assigning an account manager is that large.
- Give the honest verdict with a route forward attached. This design cannot answer the question on one fiscal year. Pool several years at the same cutoff, accepting the added assumption that the coverage model and the threshold's meaning were stable across them, or ask for a randomised holdout among accounts near the threshold, which is cheap to justify precisely because the rule is already arbitrary there.
Worked solution (45 min)
- Plot the density of arr_cents in bins around $50,000 and run the manipulation test; stop and report the design as invalid if bunching is present.
- Plot the first stage, share of accounts with a named owner against ARR, and confirm the jump is close to 0.79 and locally flat on each side.
- Fit local linear regressions of renewal on ARR either side of the cutoff with a triangular kernel and data-driven bandwidth, take the Wald ratio, and report bias-corrected robust intervals.
- Run covariate-continuity checks, placebo cutoffs, and a bandwidth-sensitivity curve for the point estimate.
- Compute the MDE at the realised effective sample and write the verdict, powered or not, with the alternative design named.
Expected result: A fuzzy RD local average treatment effect at the $50,000 cutoff with a first stage of about 0.79, and an MDE of roughly 16 percentage points on the fuzzy estimand at about 100 accounts per side. The honest finding is that the design is underpowered by an order of magnitude relative to any credible coverage effect, so the recommendation is pooled fiscal years or a randomised holdout at the margin.
Verify it yourself
- The first-stage jump and the outcome jump must be estimated on the same bandwidth. A Wald ratio assembled from two different bandwidths is not the regression discontinuity estimator.
- Covariates that coverage could not have affected must be continuous at the cutoff. A jump in employee_band or acquisition_channel at $50,000 indicates either manipulation of the running variable or a second rule sharing the same threshold.
- Point estimates should vary smoothly with bandwidth. An estimate that flips sign between a $10,000 and a $20,000 bandwidth is a fitting artefact rather than a finding.
Follow-up
- The density test shows clear bunching just above $50,000. Is any part of the design salvageable, and what would you report instead?
- Coverage also correlates with support ticket severity. Does adding severity as a control help, hurt, or do nothing in a regression discontinuity?
- If you pool five fiscal years to gain power, what new identifying assumption have you taken on, and how would you check it?
Weekly active organisations fell nine percent over one week
Easy · seasonality · denominators · segmentation
A dashboard reports the weekly active organisation ratio on a trailing seven-day window ending each Wednesday. This week it reads nine percent below last week. You have fct_api_request (account_id, environment, traffic_class, http_status, request_at) and dim_account (account_id, billing_country, account_status, is_internal, is_current). Nothing was released. Decide whether usage actually fell, and hand back a corrected series plus a one-paragraph explanation that a non-analyst can repeat without you in the room.
Approach
- Count the holiday-free business days inside each window before comparing them. A trailing seven-day window spans exactly five weekdays wherever it ends, so its business-day count can only fall to four or fewer when a public holiday lands inside it and can never reach six, while usage in this domain follows a hard five-to-two weekday cycle. Two windows holding different numbers of business days are not comparable whatever the product did.
- Count distinct active accounts per calendar day for the last ten weeks and overlay the two windows. A calendar problem shows as a small number of weekdays sitting at weekend level, not as every day being uniformly lower.
- Cut the daily series by billing_country and index each country-day to that country's trailing same-weekday median, which isolates a regional public holiday from a product change.
- Check the denominator on its own: the metric divides by accounts whose account_status was in trial, free or active_paid for the whole week, so a batch suspension or status backfill moves the ratio with no change in the numerator at all.
- Report the series with each window's business-day count and the holiday dates annotated beside it, and state the residual week-over-week change that survives once the calendar effect is removed. Compare against earlier windows holding the same number of business days rather than dividing by business days, since distinct-account counts are sublinear in window length and dividing would over-correct.
Follow-up
- Distinct account counts are sublinear in the number of days in the window. Why does losing one of five business days reduce the count by noticeably less than twenty percent?
- How would you make this metric comparable across countries with different holiday calendars without hand-maintaining a holiday table forever?
Suggested practice checklist
Roughly 90 minutes a night on weekdays with one longer weekend block. The plan deliberately cuts scope rather than compressing everything, on the assumption that finishing one thing a night beats half-starting four.
Day 1 — Fix the scope and set a baseline
- Read the role description and write the three things the loop will almost certainly test, then write an explicit not-doing list for everything else and keep it visible all week.
- Take one 20-minute SQL prompt and one 10-minute metric question cold, and write the single sentence that says what blocked each attempt, since that sentence is what decides which two topics get the most evenings.
- Set the week's one rule: one problem finished to completion every night, including the night you only have 40 minutes.
Deliverable: A one-page scope with an explicit not-doing list and two cold attempts, each carrying one sentence on what blocked it.
Day 2 — One query pattern, written three times
- Choose the single pattern most likely to appear (a cohort retention grid, or a funnel counted by user) and write it three times from a blank file rather than editing the previous attempt.
- On the third attempt, write the grain of every CTE as a comment before writing its body.
- Stop at 90 minutes even if the third version is imperfect, and write the one thing you would fix with another hour.
Deliverable: Three independent versions of the same query plus a note on what changed between them.
Day 3 — Only the statistics you will be asked to defend
- Write, in under 200 words, how you would decide whether a difference between two groups is real: the test, its assumptions, and what you would switch to when an assumption fails.
- Compute a 95 percent confidence interval for a difference in proportions by hand on realistic numbers, then write in one sentence what changes if the two samples are paired rather than independent.
- Write your answer to "what does a p-value mean", check it against a definition, and delete the version that describes it as the probability the hypothesis is true.
Deliverable: A 200-word written answer and one hand-computed interval you can reproduce under pressure.
Day 4 — One case, and the assumptions holding it up
- Answer one product case aloud in 20 minutes with a recording running, then listen back with a pen and mark every claim you asserted without saying what it rested on: an assumed user behaviour, an assumed data source, an assumed baseline rate, an assumed grain.
- Pick the three assumptions the recommendation actually depends on, write how you would check each one against data, and say which one being wrong would flip the recommendation rather than merely weaken it.
- Write the four-step structure you used onto a card small enough to hold in working memory when you are nervous.
Deliverable: One recording, three load-bearing assumptions each with a written check, and a four-step structure card.
Day 5 — Your own work, timed
- Write a 90-second version and a four-minute version of your main project, and time both out loud rather than reading them.
- Prepare answers to the two follow-ups that always come: what you would do differently, and how you knew it worked.
- Put one number in the first sentence and be able to say exactly where that number came from and what it excludes.
Deliverable: Two timed narratives with one defensible number in the opening line.
Day 6 — The one full rehearsal, in a longer weekend block
- Run a 60-minute mock covering query work, a case and a behavioural question in a single sitting with no breaks, because sustained attention is the thing evenings have not trained.
- Immediately afterwards, and before hearing any feedback, write the three moments you lost the thread.
- Spend the rest of the block only on those three moments, and on nothing you merely feel shaky about.
Deliverable: Mock notes naming three failure moments with a specific fix written under each.
Day 7 — Taper
- Write the 20-minute warm-up you will actually do on the morning of the interview: one query you can already write from a blank file, one metric you can define out loud, and nothing you have never seen before.
- Re-read only your own notes from this week, and open no new material.
- Write down the logistics: the tool you will be asked to work in, whether lookups are allowed, and the sentence you will use when you do not know something.
Deliverable: A one-page card holding the case structure, the project numbers, and the logistics.
The project you argued against
Saying no well is a senior skill and it is rarely rehearsed. Think of a time you told someone their analysis was not worth doing, or that the experiment could not answer their question at the sample size available. Explain what you offered instead. Refusal without an alternative reads as obstruction rather than judgement.
Tell me about a time a model or analysis you deployed failed in production, and how you handled it.
Medium · behavioural and stakeholder questions
Approach
- 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.
- Pick a story where you drove the decision, not one where you observed it.
Follow-up
- What did you decide not to do, and why?
- What would you do differently if you ran that project again?
State the measured impact of your own work honestly
Hard · impact · causal inference · self-assessment
You are asked for the business impact of a renewal-risk worklist you shipped nine months ago. Customer success used it, and renewals in the covered segment came in 6 points above the prior year. Coverage was assigned by the team itself: they worked the top of your list and also the accounts they were already worried about. Produce the impact claim you are willing to defend, the number you refuse to claim, and the design you would have asked for at the start.
Approach
- The interviewer is probing whether you can separate what you shipped from what you caused, and whether you would have built the measurement in rather than reconstructing it afterwards. Both halves are being scored.
- Name the confound precisely. Coverage assignment is doubly selected: the largest accounts get an owner because they are valuable, and distressed accounts get one because they are at risk. The naive covered versus uncovered comparison mixes a strong positive selection with a strong negative one and can come out with either sign depending on which rule dominated. Matching on account size does not fix it, because the risk signal that triggered coverage is the same signal that predicts the outcome.
- Split the claims by what each needs to be true. Ranking quality is defensible from precision at k on out-of-time renewals. Adoption is defensible from timestamps showing what share of listed accounts were contacted. The outcome claim is not defensible without a design, and saying so is the point of the exercise.
- Look for identification before giving up on it. A capacity cut-off, a territory boundary, or a period in which the list existed but was unstaffed can assign coverage for reasons unrelated to account health, and any of those supports a bounded estimate.
- State the design you would ask for now and its price: a randomly withheld slice of the list, held for two renewal quarters, with the expected cost in renewals stated openly. That cost is what it takes to be able to answer this question at all.
- Give a bounded number rather than none. Six points with an explicit statement of how much of it you can attribute is more useful than either claiming the whole figure or declining to quantify anything.
Follow-up
- Your manager wants the 6 points in a promotion packet. What wording do you accept, and what do you strike?
- What would have had to be true for the naive covered versus uncovered comparison to be valid?
- If the holdout costs the team real renewals, how do you justify asking for it, and to whom?
Disagree with a product manager about an adoption claim
Medium · conflict · telemetry hygiene · adoption metrics
A product manager is about to present that a new SDK release drove a 40 percent rise in requests among adopting accounts, computed from fct_api_request counts grouped by sdk_version. You find the rise is concentrated in traffic_class equal to ci, that rows with is_retry true grew alongside it, and that restricting to interactive non-retry traffic leaves a 3 percent lift. The launch review is in two days. Decide how you raise this, with whom and in what order, and what you propose the claim becomes.
Approach
- The interviewer is probing whether you can correct a colleague without ambushing them, and whether your own counter-analysis carries the caveats theirs lacked. Go to the product manager privately before the review. A correction delivered in the room is a status move and loses the argument you are actually trying to win.
- Bring a decomposition rather than a verdict: the same accounts and window, requests split by traffic_class with retries held out as their own column, so their 40 percent and your 3 percent reconcile line by line and neither has to be taken on trust.
- Reproduce their figure exactly first. If you cannot land on 40 percent with their method, you do not yet know what you are disagreeing with.
- Ask whether the continuous-integration lift is itself valuable. An account wiring the SDK into its pipeline has increased integration depth, which is the dominant switching cost in this domain, so the honest claim may be that integration depth rose while interactive usage moved 3 percent. Improving the claim beats deleting it.
- Name the mechanism that makes the raw count dangerous: clients retry when the platform degrades, so retry volume climbs exactly when the customer is most at risk. Pull the 5xx rate for the same accounts and window before anyone concludes anything, and note that billable_units is zero on 5xx rows, so request counts and billable quantities diverging is itself the signal.
- Close with a standing definition for launch metrics so the next release does not repeat the exercise.
Follow-up
- The product manager argues that continuous-integration traffic is real usage and declines to split it out. Is that position defensible, and under what metric definition?
- Suppose the 5xx rate for those same accounts also rose 40 percent. What is the claim now?
- The review happens and the raw number is presented regardless. What do you do next, and what do you not do?