Interview guides · Deliverr
Deliverr Data Scientist Interview Questions & Guide 2026
Data scientist interview prep for Deliverr: original practice on experiment design, SQL and metric reasoning, with worked solutions.
The 60-second brief
As a Data Scientist at Deliverr, you will play a pivotal role in shaping the future of logistics and e-commerce through data-driven insights and solutions. Your work will directly impact the optimization of supply chain processes, enhance customer experiences, and influence strategic business decisions. As a critical member of the team, you will engage with vast datasets and complex algorithms to extract actionable intelligence, driving efficiency and innovation within the organization.
In modelling rounds the live question is usually why this model class for this problem, and how you would know six weeks after launch that it is still working. Deriving gradients by hand is rarely what is being probed.
Focus areas
- Trace an on-time miss to one node
- Evaluate forecasts at the lag ordering actually uses
- Separate true demand from censored, stocked-out sales
Overview
The importance of this role cannot be overstated. At Deliverr, you will work on real-world challenges, such as predicting demand trends, improving inventory management, and developing models that optimize delivery routes. You will collaborate closely with cross-functional teams, including engineering, product management, and operations, to implement your findings and solutions. This dynamic environment offers an exciting opportunity to make a tangible impact on the business and its customers.
The Deliverr interview process
Deliverr candidates report 4 rounds · ≈ 3-5 weeks. The stages below are what candidates describe, not a published process.
Technical Assessment
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
Recruiter Screening
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
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
Experience Discussion
Rounds outside the standard loop often open with something deliberately under-specified: a loose business problem, an open question about a product area, a dataset described in one sentence. The common failure is surveying, listing six plausible approaches and committing to none of them. The thing that separates a strong answer is scoping out loud. State what you are treating as the goal, name the metric you would move, say what you are choosing not to do and why, then take one path through to an actual answer. An interviewer can follow you down a narrow path. Nobody can grade a menu.
What to demonstrate
- Whether you turn an ambiguous prompt into a stated question with a measurable outcome before doing any work
- The judgement visible in what you cut, and whether you say why you cut it rather than silently dropping it
- Whether you land on a concrete recommendation with its caveat attached, rather than an unranked set of options
How to prepare
- Take three vague prompts, such as 'is this feature working', 'why did retention drop', and 'should we expand into a new segment'. For each, write one sentence of goal, one primary metric with its window, and two things you are explicitly not doing.
- Practise giving the recommendation first and the reasoning second, in five minutes. Loosely defined rounds are usually time-boxed, and an answer that arrives last often does not arrive.
- Keep a running assumption list as you talk, on paper or in the shared doc, so the interviewer can challenge one assumption instead of your whole answer.
Where candidates lose points
Sizing safety stock as z times sigma_D times the square root of lead time
That form assumes lead time is deterministic. When lead time itself varies, the standard deviation of demand over lead time is sqrt(L_bar * sigma_D^2 + D_bar^2 * sigma_L^2), and the second term dominates whenever supply is unreliable, so the familiar formula can understate the requirement severalfold for a long, variable inbound lane. Two further preconditions are routinely forgotten: demand is assumed independent across periods, which promotions and order batching break, and z maps to cycle service level (the probability of no stockout in a replenishment cycle), not to fill rate, which additionally depends on order quantity through the unit normal loss function. Quoting a z-derived number as a fill rate overstates achieved service, and the gap widens as order quantity shrinks.
Treating shipped units as demand
Shipments are censored at available inventory: on a stockout day the recorded quantity is a supply ceiling, not customer intent, and orders that were never placed because the item showed as unavailable leave no row at all. A forecast fitted on that history learns the constraint, under-forecasts the fast movers that stock out most often, and produces the replenishment that causes the next stockout, so the error compounds in one direction rather than averaging out. The fix is to model demand_qty rather than shipped_qty, flag stockout days with stockout_flag and treat them as censored (fit with a censored likelihood, or estimate unconstrained demand from uncensored periods and comparable locations), and to report how much of the history was censored alongside any accuracy number.
Sizing estimates built on unnamed, unrevisable assumptions
Write each assumption as a named number you can change, then show the arithmetic so the interviewer can challenge one input instead of the whole answer. Finish by saying which assumption the result is most sensitive to, which matters more than the point estimate.
Ignoring interference between units in a marketplace experiment
Ask whether one unit's treatment can change another unit's outcome through shared inventory, a matching pool, a social graph or a common budget. Where it can, randomise at a level that contains the spillover, such as region or time slice, and say explicitly what that costs you in statistical power.
Questions & practice
Can you describe the process of building a predictive model?
Medium · machine learning and modelling
Approach
- 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.
- Frame the prediction: the label, the moment of prediction, and the action it triggers.
Follow-up
- Where could label leakage enter this setup?
- How would you choose the decision threshold, and who owns that choice?
Weighted forecast error at the lag ordering actually consumes
Easy · wmape · forecast bias · aggregation grain · pandas
You are given fct_inventory_daily as a DataFrame with inventory_date, sku_id, location_id, demand_qty and forecast_qty_lag7. Replenishment is decided weekly per SKU per node, so score the forecast at that grain over a trailing 8 weeks. WMAPE is the sum of absolute weekly errors over the sum of weekly demand; weighted mean percentage error is the same ratio with the error left signed. forecast_qty_lag7 is null before a series existed. Return one row per location_id carrying both statistics, the shared denominator, and the count of rows excluded. No forecasting library.
Approach
- Decide the null rule before writing any aggregation: a sku-location-week where any constituent day has a null forecast must be dropped whole. Signed error here is forecast minus demand, so summing 7 days of demand against the 5 days of forecast that survived leaves the numerator short by two days of forecast and drives the ratio negative, which reads as systematic under-forecasting the model never committed.
- Aggregate demand_qty and forecast_qty_lag7 to sku x location x week first, then difference. Differencing daily and summing the absolute values afterwards answers a different question, since intraday timing error cancels inside a week and the ordering decision never sees it.
- Build week buckets from inventory_date with a fixed anchor (a Monday-start ISO week or a rolling 7-day offset), and use the same buckets for both series so no partial week sits at either end of the 8-week window.
- Compute the two statistics by summing numerator and denominator to the location level and dividing once. Never average sku-level ratios: a C-class item with 3 units of weekly demand would otherwise carry the same weight as an A-class item with 3,000.
- Return the denominator and the excluded row count as columns, not as a printed aside, so a reader can tell a genuinely accurate node from one with almost no scored history.
Follow-up
- The same series is scored at lag 28 and WMAPE roughly doubles. Is that a model problem or an expected property of the horizon?
- A node shows WMAPE of 0.35 and bias of 0.01. What can you and can you not conclude about its inventory position?
- How would you report accuracy for a SKU whose weekly demand is zero in 40 of the 52 weeks?
Audit a daily inventory snapshot for silent corruption
Easy · data quality · invariants · completeness
You receive fct_inventory_daily with inventory_date, sku_id, location_id, on_hand_qty, allocated_qty, blocked_qty, available_qty, in_transit_qty, demand_qty, shipped_qty, stockout_flag and standard_cost_cents. The table is supposed to carry one row per active sku-location per date, including days with no movement. Write a check suite that returns one row per named check with the number of sku-location-days affected, the share of the scope, and the on-hand value at standard cost sitting behind the failures. Do not repair anything; the deliverable is the evidence.
Approach
- Start with the arithmetic identity the table declares: available_qty must equal on_hand_qty minus allocated_qty minus blocked_qty. Report exact row counts rather than a boolean, because a handful of violations is a feed bug and a uniform offset is a definition change upstream.
- Check sign constraints separately from the identity. Negative on_hand_qty usually means receipts posted out of order, while negative available_qty is legitimate at some sites when allocation is allowed to over-commit, so flag it and ask rather than assuming corruption.
- Check calendar completeness per sku-location by comparing the observed row count against the number of dates between that pair's first and last appearance. Count the missing dates without reindexing the frame, because filling them with zeros converts a feed gap into a plausible run of zero-demand days.
- Check stockout_flag in one direction only: available_qty of 0 at the cut-off with stockout_flag false is a contradiction, while flag true with positive available_qty is not, since the flag records an intraday touch of zero that a late receipt can recover.
- Weight every failure by on_hand_qty times standard_cost_cents so the summary orders checks by money at risk rather than by row count, and state the scope denominator on each row.
Worked solution (20 min)
- Define the scope explicitly as the distinct sku-location pairs and the date range under audit, and store the scope row count for use as every check's denominator.
- Evaluate the identity, sign and stockout-flag checks as boolean masks over the frame and record the count and affected value for each.
- For completeness, group by sku_id and location_id, take min and max inventory_date and the row count, and compare the count to the number of calendar days spanned.
- Assemble one output row per check with check_name, failing_rows, share_of_scope and failing_on_hand_value_cents, sorted by value descending.
- Spot-print five failing rows per check so the output is actionable rather than a set of counts.
Expected result: A small DataFrame with one row per check. Shares are between 0 and 1 against a stated scope denominator, and the value column is computed as on_hand_qty times standard_cost_cents on failing rows only.
Verify it yourself
- Inject a known defect into a copy (flip one available_qty by +1) and confirm the identity check count rises by exactly one.
- Confirm the completeness check reports zero missing dates for a sku-location pair you verify by hand against a printed date list.
- Confirm the sum of on-hand value over all rows exceeds the value reported on any single check, and that no check's share exceeds 1.0.
- Confirm no check silently drops nulls: count nulls per column first, since a comparison against null returns false and hides the row.
Follow-up
- Someone proposes asserting shipped_qty is at most demand_qty on the same row. Why does that fire on thousands of healthy rows?
- Half the missing dates fall on Sundays at one set of nodes. What is the most likely explanation and does it change the severity?
- Which of these checks would you run as a blocking gate before a nightly planning job, and which as a monitored report?
Provide a Python snippet to clean and preprocess a given dataset.
Medium · sql and data manipulation
Approach
- Check whether any join is one-to-many before aggregating, or the sums inflate.
- 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.
Follow-up
- How does the query change if the join becomes one-to-many?
- What breaks if events arrive late or out of order?
Write a SQL query to find the top 10 products with the highest sales in the last month.
Medium · sql and data manipulation
Approach
- Say which table is the grain you start from, and join outward from it.
- Check whether any join is one-to-many before aggregating, or the sums inflate.
- Handle the rows that do not match: a LEFT JOIN with a NULL check is usually the question.
Follow-up
- How does the query change if the join becomes one-to-many?
- How would you verify this result without re-running the same query?
Dead stock cells excluding SKUs used as substitutes
Medium · anti-join · null semantics · dead stock
Find dead stock: sku_id x location_id cells whose latest fct_inventory_daily row has on_hand_qty > 0 and which have no fct_order_line with that sku_id and ship_from_location_id in the last 90 days. Also exclude any SKU that filled another line as a substitute in that window; fct_order_line.substituted_sku_id is NULL on every line that was not substituted. Value each cell at on_hand_qty * standard_cost_cents taken from the snapshot, not from dim_sku. Return the cell, days since its last demand line, and value, ordered by value. Treat line_status = 'cancelled_no_supply' as demand.
Approach
- Take the latest snapshot per sku-location with ROW_NUMBER() OVER (PARTITION BY sku_id, location_id ORDER BY inventory_date DESC) = 1, or an equivalent MAX(inventory_date) join, and keep rows with on_hand_qty > 0.
- Anti-join demand with NOT EXISTS against fct_order_line on both sku_id and ship_from_location_id over the 90-day window, counting every line status including cancelled_no_supply, because unmet demand is still demand and a cell with it is not dead.
- Write the substitute exclusion as NOT EXISTS, or force substituted_sku_id IS NOT NULL inside the subquery. NOT IN over a column that contains NULLs evaluates to NULL for every candidate that is not an explicit match, so the predicate is never true and the result set is empty.
- Value from the snapshot's standard_cost_cents rather than dim_sku, since the dimension carries only the cost in force today and is revised at period close without back-dating.
- Get days since last demand from a LEFT JOIN LATERAL MAX(ordered_at_utc), and report cells that never had a line in a separate bucket instead of as a very large number.
Worked solution (30 min)
- CTE latest_snap: ROW_NUMBER over sku_id, location_id ordered by inventory_date DESC, filtered to rn = 1 and on_hand_qty > 0.
- CTE last_demand: MAX(ordered_at_utc) grouped by sku_id, ship_from_location_id over all of fct_order_line, with no status filter.
- Select from latest_snap LEFT JOIN last_demand, keeping rows where the max is NULL or older than 90 days.
- Add AND NOT EXISTS (SELECT 1 FROM fct_order_line o WHERE o.substituted_sku_id = latest_snap.sku_id AND o.ordered_at_utc >= now() - interval '90 days').
- Project value_cents = on_hand_qty * standard_cost_cents, order by it descending, and run the same query once with NOT IN to see the empty result for yourself.
Expected result: One row per dead sku-location cell with value_cents computed from the latest snapshot's own cost column, days_since_last_demand (or an explicit never-ordered bucket), ordered by value_cents descending. The substitute exclusion removes some rows; it must not empty the result.
Verify it yourself
- Swapping NOT EXISTS for NOT IN on substituted_sku_id collapses the output to zero rows. That is the diagnostic for the NULL semantics, not a data finding.
- Total value across returned cells is strictly less than total on-hand value at the same snapshot date.
- No returned cell appears in SELECT DISTINCT sku_id, ship_from_location_id FROM fct_order_line over the same 90 days.
Follow-up
- The query returns zero rows. Name the two likeliest causes and say which you would test first, with the one-line test.
- How do you separate genuinely dead stock from a seasonal item out of season and from a lifecycle_status = 'pre_launch' SKU?
- The same SKU is dead at one node and short at another. Write the query that finds that pattern and say what it implies about the policy.
Describe how you would approach optimizing delivery routes for a logistics company.
Medium · product sense and metrics
Approach
- State what result would change your recommendation, so the answer is falsifiable.
- 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
- What would you do if the primary metric and the guardrail moved in opposite directions?
- Which segment would you cut first, and what would that rule out?
Discuss the trade-offs between using cloud storage versus on-premise solutions for large datasets.
Medium · product sense and metrics
Approach
- 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.
- 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?
How do you prioritize tasks when working on multiple projects?
Medium · product sense and metrics
Approach
- Name one primary metric, then the guardrail that stops it being gamed.
- Fix the population and the time window before naming any metric.
- State what result would change your recommendation, so the answer is falsifiable.
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?
Given a dataset with sales information, how would you identify trends and seasonality?
Medium · product sense and metrics
Approach
- 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.
- State what result would change your recommendation, so the answer is falsifiable.
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?
What is A/B testing, and how would you design a test for a new feature?
Medium · a/b testing and experimentation
Approach
- State the primary metric and the minimum effect worth shipping, then size the test.
- Say whether units interfere with each other, and switch design if they do.
- Decide the analysis before seeing data, including how long it runs and when you look.
Follow-up
- How would you handle interference between treated and control units?
- What would you do if you could not randomise at all?
Explain the difference between supervised and unsupervised learning.
Medium
Approach
- Work from the decision backwards to the evidence you would need.
- State your assumptions explicitly before working the problem.
- Say what you would check first and why it is the highest-information step.
Follow-up
- What assumption would you test first?
- How would you know your answer was wrong?
Recover the effect of expediting from an automatic threshold rule
Hard · regression discontinuity · fuzzy rd · late · manipulation test
The transport system upgrades service_level to 'expedite' whenever projected slack at tender falls below 12 hours, and charges expedite_premium_cents for it. Projected slack is promised_delivery_at_utc minus the planned arrival implied by planned_transit_hours, and it is recoverable from two years of fct_shipment_leg. Finance wants to know what the premium buys. No randomised holdout is possible, and the rule has run unchanged throughout. Estimate the causal effect on on-time delivery, state which shipments your estimate applies to, and give the checks that would invalidate it.
Approach
- Recognise the design: treatment is a deterministic function of an observed continuous running variable with a known cutoff, so a regression discontinuity identifies the effect at that cutoff with no randomisation, provided nothing other than the expedite rule changes at 12 hours.
- Estimate with local linear regression on each side of the cutoff, a triangular kernel and an MSE-optimal data-driven bandwidth, reporting robust bias-corrected confidence intervals. Do not fit a global high-order polynomial: it imports weight from observations far from the cutoff and manufactures jumps that are artefacts of the functional form.
- Test manipulation, because planners can edit the promise date and that moves slack directly. Run a density test of the running variable at the cutoff and inspect the histogram for heaping just below 12 hours. If slack is recorded to the whole hour, treat the running variable as discrete, cluster by its value, and consider a donut estimate that drops the bins adjacent to the cutoff.
- Test continuity of everything that is not the treatment: weight_kg, cube_m3, mode, stop_count, lane and carrier should not jump at 12 hours. Add placebo cutoffs at 8 and 16 hours, where the effect should be indistinguishable from zero.
- Handle overrides. If dispatchers sometimes expedite above the cutoff or decline below it, the design is fuzzy: estimate the jump in the probability of expediting and divide the outcome jump by it, equivalently two-stage least squares with the below-cutoff indicator as the instrument, and report the first-stage jump, because a weak first stage makes the ratio unstable.
- Finish with the arithmetic Finance asked for: the jump in on-time probability at the cutoff against mean expedite_premium_cents per unit inside the bandwidth, expressed as cost per incremental on-time delivery and labelled as applying only near 12 hours of slack.
Worked solution (40 min)
- Reconstruct the running variable at tender time from tendered_at_utc, planned_transit_hours and promised_delivery_at_utc, then verify it reproduces the observed expedite assignment on both sides of 12 hours.
- Plot binned means of on-time delivery against slack with a data-driven bin width, and look at the picture before fitting anything.
- Fit local linear RD with a triangular kernel and MSE-optimal bandwidth; report the estimate with robust bias-corrected intervals plus sensitivity at half and double the bandwidth.
- Run the density test at the cutoff, the covariate-continuity checks, and placebo cutoffs at 8 and 16 hours.
- Convert to cost per incremental on-time delivery using mean expedite_premium_cents per unit within the bandwidth.
Expected result: A local average treatment effect on on-time delivery for shipments with projected slack near 12 hours, reported with robust bias-corrected intervals, accompanied by the first-stage jump if the rule turns out to be fuzzy, and converted to a cost per incremental on-time delivery from the premium actually paid inside the bandwidth.
Verify it yourself
- The assignment check should be near-deterministic; if a material share of legs above 12 hours are expedited anyway, the design is fuzzy and the sharp estimate is wrong by the size of the first stage.
- The estimate should be stable when the bandwidth is halved and doubled; a sign flip means the functional form is driving the result.
- Placebo cutoffs at 8 and 16 hours should return effects indistinguishable from zero.
- Any jump at the cutoff in weight, cube, mode mix or carrier share means something besides the expedite rule changes at 12 hours, and the design fails.
Follow-up
- The jump is 4 points at the cutoff. What would you need before recommending the threshold move to 18 hours?
- Slack is recorded to the whole hour and 9% of legs sit exactly at 12. What does that heaping do to the estimate, and how do you handle it?
Perfect order rate falls only in the newest two weeks
Medium · right censoring · reporting lag · metric maturation
A weekly perfect order chart holds at 94 percent for eleven weeks, then reads 91 percent and 84 percent in the two most recent weeks. The metric requires every line shipped_full, actual_delivery_at_utc on or before original_promised_delivery_date, and no joined shipment leg carrying exception_code IN ('damage','address_issue') or leg_status IN ('refused','lost'). It is published on a 14-day lag. Using fct_order_line and fct_shipment_leg, decide whether service regressed and specify what the chart should show for incomplete weeks.
Approach
- Start from the structure of the metric: the denominator is fixed at promise date while the numerator requires an event that may not have happened yet, so any week younger than the full delivery-plus-claims window is mechanically depressed. Establish that before hunting for a cause.
- Quantify the incompleteness instead of assuming it. Per promise week, report the share of lines with actual_delivery_at_utc IS NULL and the share of shipments whose legs are still leg_status IN ('tendered','in_transit').
- Build the maturation curve from history. For each of the eleven settled weeks, recompute the metric as it would have looked at 3, 7 and 14 days after the promise date, so a recent week can be compared against a healthy week at the same age rather than against a settled one.
- Only after age-matching, test whether any residual is real by cutting it by ship_from_location_id, mode and exception_code, and asking whether the loss concentrates where a genuine failure would concentrate.
- Fix the chart rather than only the analysis: suppress or grey weeks younger than the publication lag, or publish an age-matched estimate with an interval, and write the rule down so the next reader is not caught by the same shape.
Follow-up
- Damage claims can arrive up to thirty days later. Does that argue for a longer lag or for a different numerator?
- How would you detect a genuine regression inside the lag window without waiting two weeks for it to settle?
- P90 order cycle time shows the same maturation shape but biased the other way. Why, and which metric is more dangerous to read early?
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.
What the behavioural round is actually scoring
Interviewers here are not checking whether you can describe a project. They want the decision you made, why you made it under the information you had, and what changed afterwards that someone else could measure. A story that ends at 'I built a model' has no ending. Say what the model caused, or what you stopped doing because of it.
How do you handle missing data in a dataset?
Medium · behavioural and stakeholder questions
Approach
- Quantify the outcome, including what you would not claim credit for.
- 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.
Follow-up
- What did you decide not to do, and why?
- What would you do differently if you ran that project again?
Disagree with a planning manager about a safety stock cut
Medium · disagreeing with data · censoring · forecast evaluation
A planning product manager proposes cutting safety stock 30 percent on every C and Z class sku-location cell, citing a MAPE improvement from 41 to 28 percent over two quarters. Checking the query, you find MAPE is computed over fct_inventory_daily rows where the actual is greater than zero, and the actual used is shipped_qty rather than demand_qty; stockout_flag is true on 14 percent of the rows that were kept. You have fifteen minutes in his planning review. Make the disagreement, and propose what you would do instead of the flat cut.
Approach
- Lead with the decision at risk, not the metric error: a 30 percent cut on intermittent items is the cheapest way to convert a measurement artefact into stockouts eight weeks later, by which time nobody will connect the two.
- Name the two defects precisely. Dropping zero-actual rows is not a rounding choice, it removes most of the history for a C-class item at a forward-stocking location and it removes the rows where over-forecasting is penalised, so the surviving MAPE is biased toward whichever series stocked out. Using shipped_qty as the actual scores the forecast against a supply ceiling, so a series looks more accurate the more often it ran out.
- Offer the replacement metric in the same breath: WMAPE, sum of absolute errors over sum of actuals against demand_qty, computed at the sku-location-week grain the ordering decision uses and at the lag it uses, reported next to signed bias so a persistently short series cannot hide inside an accuracy number.
- Propose the smaller action that is defensible now: recompute on the corrected basis, rank cells by bias rather than accuracy, and cut cover only where the corrected series is unbiased or over-forecasting, in a staged rollout with a service guardrail.
- Give him the win he actually wants: if the corrected numbers still support cuts on a subset, say so in advance, so the disagreement is about evidence rather than about territory.
Follow-up
- He says demand_qty is itself incomplete because customers stop ordering what shows out of stock. Is he right, and what do you do about it?
- Which items would you leave alone regardless of what the corrected metric says?
Explain forecast uncertainty to a non-technical general manager
Easy · communicating uncertainty · service level · executive communication
A general manager who does not use statistics must approve a 2.1 million dollar finished-goods build. Your recommendation rests on a demand forecast with a wide error band on the Z-class items and on a 97 percent cycle service target. You get two minutes, no formulas, and the question you will get back is whether the forecast is right. Deliver the explanation as you would say it aloud, including how you describe the error band in physical terms and what the 97 percent number does and does not promise.
Approach
- Convert the error band into the units the approver already thinks in: weeks of cover, pallets, or dollars at risk, not a percentage or a confidence interval.
- Answer the right-or-wrong question directly rather than deflecting: the forecast will be wrong, the size of the wrongness is what you measured, and the build is sized against that size.
- State precisely what the 97 percent buys: it is a cycle service level, the probability of not running out during one replenishment cycle, so roughly three cycles in a hundred see a stockout. It is not the share of units shipped from stock. Unit fill rate also depends on the replenishment quantity, and when that quantity is large relative to the standard deviation of lead-time demand, which is the normal case outside lot-for-lot ordering, the fill rate sits above the cycle service number, often above 99 percent at a 97 percent cycle target.
- Give the two-sided consequence in money: what the build costs to carry at the applicable cost of capital plus obsolescence risk on shelf-life items, against the margin at risk from the stockouts it prevents.
- Close with the decision you want and the trigger that would reverse it, for example a lag-7 bias check after four weeks that reopens the number.
Follow-up
- The GM says just give me one number. What do you give, and what do you refuse to give?
- How would your answer change if the items were frozen with a 90-day shelf life?