Interview guides · Chefs' Warehouse
Chefs' Warehouse Data Scientist Interview Questions & Guide 2026
Diagnose a metric move for Chefs' Warehouse 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 Chefs' Warehouse, you play a critical role in transforming raw data into actionable insights that drive strategic decision-making across the organization. Your work influences product offerings, optimizes supply chain logistics, and enhances customer experiences, making data analytics integral to the company’s mission of delivering high-quality culinary products to chefs and foodservice professionals. This role involves collaboration with various teams, including product management, marketing, and operations, to ensure that data-driven strategies align with business goals.
A large share of questions open as "how would you measure X", where the real work is choosing the metric, fixing its denominator, and defining the population it applies to. Any computation comes last and is frequently not required at all.
Focus areas
- Evaluate forecasts at the lag ordering actually uses
- Join daily snapshots to shipment events safely
- Price a service-level change in working capital
Overview
The complexity and scale of data processed at Chefs' Warehouse present unique challenges and opportunities. You will work with large datasets, applying advanced statistical methods and machine learning techniques to uncover trends, forecast demand, and improve operational efficiency. This position not only requires technical expertise but also strategic insight to interpret findings and communicate them effectively to stakeholders. Ultimately, your contributions will directly impact the success of products and services, making the role both exciting and rewarding.
The Chefs' Warehouse interview process
Chefs' Warehouse candidates report 3 rounds · ≈ 3-5 weeks. The stages below are what candidates describe, not a published process.
HR Call
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.
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
Behavioral Interviews
Most of the weight in this round sits on the disagreement questions. Data work routinely produces an answer someone senior did not want, and the interviewer is trying to learn what you do in that hour. Both failure modes are common: folding as soon as a director pushes back, and treating the pushback as ignorance to be corrected with a better chart. A strong answer usually contains a specific thing the other person knew that you did not, and describes how you found out whether it changed the conclusion.
What to demonstrate
- Whether you can state the other side's argument accurately before you explain why you disagreed
- What you treated as evidence during the disagreement, such as a rerun under their assumption or a holdout check, rather than persuasion technique
- Whether you distinguish being overruled from being wrong, and can give an example of each
How to prepare
- Write out one disagreement where you turned out to be wrong, and say what in the data misled you. Candidates prepare the story where they were right, and the follow-up asks for the other one.
- For your main disagreement story, be ready to say what result would have made you drop your position. If no such result exists, you were not arguing from the data.
- Practise stating the opposing position out loud in one sentence the stakeholder would accept, then continue the story.
Where candidates lose points
Scoring intermittent demand with MAPE
MAPE is undefined whenever the actual is zero, which is most days for a C-class item at a forward-stocking location, and it is asymmetric even where it is defined: under-forecasting is bounded at 100 percent error while over-forecasting is unbounded. Optimising it therefore drives systematic under-forecasting on exactly the items whose stockouts are most expensive, and the bias is invisible in the headline because the zero-actual rows were dropped before averaging. Use WMAPE (sum of absolute errors over sum of actuals), a scaled error such as RMSSE, or a pinball loss at the service quantile the policy targets, and always state the aggregation level and forecast lag, because the same series scores very differently at daily SKU-store level and weekly SKU-region level.
Computing average inventory from period-end snapshots
Shipments cluster before period close, so the month-end on-hand position is systematically the lowest point of the month; turns computed against it are biased high and days of supply biased low, frequently by ten to twenty percent, and the bias grows precisely when close-period push is strongest. The same shape of error appears when a daily snapshot is joined to shipment events on date equality: a SKU with several legs on one day fans the snapshot out and multiplies the valued inventory. Average across every daily snapshot in the window for the denominator, and join snapshots to events with an explicit as-of condition and a row-count check at each grain before aggregating.
Averaging per-user rates to produce a population rate
Decide which quantity you want: the mean of per-user ratios and the ratio of summed numerator to summed denominator are different estimands, and heavy users dominate one but not the other. For a ratio metric, aggregate numerator and denominator separately and use the delta method for its variance.
Reporting a p-value with no effect size or interval
Give the estimated difference with a confidence interval in the units the business cares about, then say whether that whole interval is worth acting on. A p-value only addresses whether you can rule out exactly zero; it says nothing about magnitude.
Questions & practice
Explain how you would approach optimizing a complex algorithm.
Medium · machine learning and modelling
Approach
- Frame the prediction: the label, the moment of prediction, and the action it triggers.
- Say how the offline result would be validated online before it is trusted.
- Check what information would not exist at prediction time, and exclude it.
Follow-up
- What would you monitor after launch to know the model is still valid?
- Where could label leakage enter this setup?
Write a function to implement linear regression from scratch.
Medium · machine learning and modelling
Approach
- Check what information would not exist at prediction time, and exclude it.
- Set a baseline first, so any model has something honest to beat.
- Frame the prediction: the label, the moment of prediction, and the action it triggers.
Follow-up
- What would you monitor after launch to know the model is still valid?
- How would you choose the decision threshold, and who owns that choice?
Measure demand amplification hop by hop up the network
Hard · bullwhip · hierarchy traversal · variance
Using dim_location (location_id, parent_location_id, echelon, location_type) and fct_shipment_leg (leg_id, shipment_id, origin_location_id, destination_location_id, direction, tendered_at_utc, shipped_units), quantify how order variability grows upstream. fct_shipment_leg carries no sku_id and no link back to order lines, so shipped_units is the node's whole mixed-SKU flow and the measurement is node-level by construction. For each node compute weekly units flowing out (what it served downstream) and weekly units flowing in (what it ordered up), then the ratio of their variances on detrended, deseasonalised residuals. Walk parent_location_id from a store to echelon 0 and report the ratio at each hop, naming the hop where amplification is introduced. dim_location has no path column; build the chain yourself.
Approach
- Date orders by tendered_at_utc, not delivered_at_utc. Tender is the closest observable proxy for the moment the ordering decision was made; dating by delivery shifts the series by transit time and smears the variance you are trying to measure.
- Build both series on the same calendar weeks and in the same units. Variance scales with the aggregation window, so a ratio formed from daily inbound against weekly outbound measures the calendar, not the ordering rule. The units are mixed-SKU counts because the leg table carries no sku_id, so a node whose product mix drifts toward smaller or larger pack sizes moves both series for reasons unrelated to its ordering rule; print that caveat next to the number rather than implying a per-SKU result.
- Detrend and deseasonalise before taking variances, or compute the ratio on residuals from a simple weekly seasonal baseline. A growing node otherwise scores as amplifying, when all you have measured is its trend.
- Walk the parent chain iteratively: start from the store rows, join dim_location to itself on parent_location_id, and repeat until parent is null, capping the loop at the known echelon depth and asserting the path length matches the echelon difference so a data cycle raises rather than hangs.
- Read the output as a sequence, not a set of numbers. A pass-through node such as a cross-dock should sit near 1.0, and the hop where the ratio jumps is where a batching rule, a minimum order quantity or truckload rounding lives. That is the node to fix, not the node that is complaining.
Worked solution (45 min)
- Assign an ISO week from tendered_at_utc and build two weekly series per node from fct_shipment_leg alone: outbound units summed where origin_location_id is the node, inbound units summed where destination_location_id is the node. No SKU filter is applied because the table has no sku_id, so both series are total unit flow.
- Regress each series on a linear trend plus week-of-year dummies, or subtract a centred moving average, and keep the residuals.
- Compute the amplification ratio per node as var(inbound residuals) / var(outbound residuals) over the shared weeks, requiring a minimum of 26 weeks before reporting a ratio.
- Build the parent chain from a chosen store by iteratively joining dim_location on parent_location_id until parent_location_id is null, asserting the loop terminates within the echelon depth.
- Emit the chain in order with echelon, location_type, both variances, the ratio, the week count and the mixed-SKU caveat, and mark the hop with the largest increase.
Expected result: An ordered table from store to echelon 0, one row per hop, each with the two residual variances, the ratio, the number of weeks it rests on, and a stated caveat that units are pooled across SKUs. Cross-dock and pass-through nodes sit near 1.0; at least one hop should sit materially above 1.0 and that hop is the finding.
Verify it yourself
- Mass balance at a pass-through node: over a long window total inbound units should track total outbound units to within scrap, adjustments and the change in stock held. A persistent one-sided gap usually means one direction of leg is missing from the filter. Where fct_inventory_daily is available, sharpen this by comparing inbound minus outbound against the change in that node's on_hand_qty summed across SKUs.
- Assert the parent walk terminates, contains no repeated location_id, and produces a path length equal to the echelon difference between the endpoints.
- Construct a synthetic node that passes every unit straight through with no batching and confirm the ratio comes out at 1.0.
- Recompute both variances after re-aggregating to 4-week buckets: the ratio should stay close while the individual variances grow, since the ratio is the scale-free quantity. Do not push this past the point where only a few dozen buckets remain, where the variance estimates themselves become too noisy to compare.
Follow-up
- The ratio at one hop is 4.2 and the node insists it orders exactly to forecast. What ordering rules would produce that number anyway?
- What would it take to run this for a single SKU family rather than total units, given fct_shipment_leg carries neither a sku_id nor any link to order lines, and which hops would still be unmeasurable after you added that link?
- What would you expect this measurement to look like during a promotion, and does that change your conclusion?
How would you optimize a slow-running SQL query?
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.
- 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?
- What breaks if events arrive late or out of order?
Describe a situation where you had to clean and preprocess data for analysis.
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.
- 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?
Write a SQL query to find the top 10 products by sales in the last quarter.
Medium · sql and data manipulation
Approach
- State the window function and its partition and ordering out loud before writing it.
- 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.
Follow-up
- What breaks if events arrive late or out of order?
- How would you verify this result without re-running the same query?
Weekly unit fill rate by ship-from node
Easy · ratio aggregation · service metrics · conditional aggregation
fct_order_line holds one row per customer order line per ship-from node in its terminal state, with ordered_qty, shipped_qty, requested_ship_date, actual_ship_at_utc, line_status and ship_from_location_id; dim_location carries location_code. Produce weekly first-pass unit fill rate by node. Numerator is shipped_qty on lines shipped on or before requested_ship_date; denominator is ordered_qty on every line requested that week except line_status = 'cancelled_by_customer'. Return node, week, numerator, denominator and rate, plus one network total row. State in the output how substituted lines are counted.
Approach
- Anchor the query at order-line grain filtered on requested_ship_date, never at shipments: a line that was never filled has no shipment row, and starting from shipments deletes exactly the failures the metric exists to count.
- Build the numerator as a conditional SUM over the same row set, SUM(CASE WHEN actual_ship_at_utc IS NOT NULL AND its ship date <= requested_ship_date THEN shipped_qty ELSE 0 END), so short and backordered lines stay in the denominator instead of vanishing behind a WHERE clause.
- Exclude only line_status = 'cancelled_by_customer'. A line cancelled for lack of supply is a service failure and belongs in the denominator at full ordered_qty.
- Group by node and by DATE_TRUNC('week', requested_ship_date), then build the network row by re-summing numerator and denominator across nodes, not by averaging the node rates.
- Declare the substitution rule explicitly in one column or one comment: lines with substituted_sku_id NOT NULL count toward the numerator only if substitution is an accepted fill in the service definition.
Worked solution (20 min)
- Filter fct_order_line to requested_ship_date inside the window and line_status <> 'cancelled_by_customer'; count the rows removed by that filter and keep the count.
- Compute num = SUM(CASE WHEN actual_ship_at_utc::date <= requested_ship_date THEN shipped_qty ELSE 0 END) and den = SUM(ordered_qty) in the same SELECT.
- GROUP BY ship_from_location_id, DATE_TRUNC('week', requested_ship_date) and join dim_location for location_code.
- Add the network row from a second aggregate over the same base set: SUM(num) / SUM(den).
- Report the excluded-line count and the substitution rule beside the rate so the number is auditable.
Expected result: One row per node-week with numerator, denominator and rate = numerator / denominator, plus a network row whose rate equals SUM(numerator) / SUM(denominator) across nodes and which will generally differ from the mean of the node rates.
Verify it yourself
- Rate never exceeds 1. If it does, shipped_qty is being counted on lines whose ordered_qty was filtered out of the denominator.
- Denominator per node-week matches a separate plain SUM(ordered_qty) with no CASE logic in it.
- Dropping the largest node moves the network rate. If it does not move, the total is an average of rates rather than a re-summed ratio.
Follow-up
- actual_ship_at_utc is UTC but requested_ship_date is a local calendar date at the node. How does the comparison change for a node at UTC+9, and which direction does the error run?
- Fill rate rose two points while a node's short_reason_code mix moved from 'no_stock' toward 'credit_hold'. Is that a supply improvement?
- How would you hold SKU mix fixed so the network number is comparable month over month?
If you were given a dataset containing customer transactions, how would you approach analyzing it to identify purchasing trends?
Medium · product sense and metrics
Approach
- Fix the population and the time window before naming any metric.
- Decompose the metric into the rates that drive it, and say which one you would check first.
- 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?
How do you prioritize tasks when working on multiple projects?
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.
- Decompose the metric into the rates that drive it, and say which one you would check first.
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?
How would you design an experiment to test the effectiveness of a new marketing campaign?
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.
- Name the randomisation unit first; it decides the variance and what the test can detect.
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 handle missing data in a dataset?
Medium
Approach
- Say what you would check first and why it is the highest-information step.
- State your assumptions explicitly before working the problem.
- 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?
Cut variance on a safety-stock test with CUPED and strata
Medium · cuped · stratification · variance reduction · ratio metrics
You are testing a new safety-stock formula on 300 SKU-location cells inside one region, randomised 150/150. The outcome is eight-week unit fill rate per cell from fct_order_line; you also have 26 weeks of pre-period fill rate per cell, and each cell carries abc_class and xyz_class in dim_sku. The cell-level correlation between pre-period and post-period fill rate is 0.7. Show the CUPED adjustment, state exactly what it does to the standard error, and say which cells it will fail on and why.
Approach
- Define the adjusted outcome: Y* = Y - theta * (X - Xbar), where X is the pre-period cell fill rate and theta = Cov(Y, X) / Var(X). Because randomisation makes X independent of assignment, the treatment effect estimate is unbiased for any theta; theta only controls how much variance is removed.
- Quantify the win rather than asserting it: Var(Y*) = Var(Y) * (1 - rho^2) = 0.51 * Var(Y), so the standard error falls by a factor of sqrt(0.51) = 0.71, the MDE falls 29%, and the same precision would otherwise have needed 51% of the cells.
- Estimate theta pooled across both arms on the observed data. Fitting it per arm re-introduces a treatment-dependent term into the adjustment; the O(1/n) bias from estimating theta at all is negligible at 300 cells but should be named rather than ignored.
- Stratify the randomisation itself on abc_class by xyz_class and fit with stratum fixed effects. Strata remove between-class variance that a single pre-period covariate does not, and XYZ class is precisely the axis along which demand variability, and therefore fill-rate variance, differs.
- Name where it fails. Fill rate is a ratio whose denominator is random and small for C and Z cells, so the pre-period value is a noisy estimate of the cell's true rate, rho collapses and the adjustment buys little; a cell with zero pre-period demand has no covariate at all. Handle the ratio by adjusting numerator and denominator and applying the delta method for the variance, never by averaging cell-level ratios.
Worked solution (30 min)
- Build a cell-level table: 26-week pre-period fill rate X, 8-week post-period fill rate Y, arm, abc_class, xyz_class, and pre-period ordered_qty as a weight.
- Regress Y on X pooled across arms to obtain theta, then compute Y* = Y - theta * (X - Xbar).
- Estimate the effect by regressing Y* on treatment with abc by xyz stratum fixed effects and heteroskedasticity-robust standard errors.
- Compare the adjusted and unadjusted standard errors; the ratio should sit near sqrt(1 - 0.49) = 0.71.
- Split by xyz_class and report rho within each; expect a materially lower correlation and a much smaller reduction on Z cells.
Expected result: theta = Cov(Y, X) / Var(X). At rho = 0.7 the adjusted variance is 51% of the unadjusted, the standard error falls 29%, and the MDE falls by the same 29%. Unbiasedness does not depend on theta being right; only the size of the reduction does. The adjustment buys almost nothing on low-volume C and Z cells whose pre-period rate is itself noisy.
Verify it yourself
- An A/A replay on the pre-period must give a CUPED effect estimate centred on zero; if it does not, the covariate is contaminated by post-assignment data.
- The empirical ratio Var(Y*) / Var(Y) should land near 1 - rho^2, which is 0.51 here.
- The adjusted and unadjusted point estimates should agree within roughly one unadjusted standard error; a larger move means theta is absorbing a real imbalance rather than noise, and the randomisation deserves a second look.
Follow-up
- The policy change alters ordered_qty through substitution behaviour. What does a treatment-affected denominator do to a ratio metric, and does CUPED still help?
- Three cells carry 40% of regional volume. What does that do to your variance, and which estimand should you actually report?
Plant schedule instability generated two echelons downstream
Hard · bullwhip · echelon attribution · variance
A plant reports that the replenishment orders it receives swing far more than end-customer demand, and asks for a larger finished-goods buffer. You have fct_order_line for customer demand at store and forward-stock nodes, fct_inventory_daily (on_order_qty, demand_qty per sku-location-date), dim_location (echelon, parent_location_id) and dim_sku (units_per_case, cases_per_pallet). Measure amplification at each echelon, identify the node whose ordering rule generates it, and price the buffer request against fixing that rule. Deliverable: a per-echelon amplification table and a recommendation.
Approach
- Build two series per node at a fixed weekly bucket per SKU: the quantity the node ordered from its parent_location_id, and the demand it received from its children or from customers. Both series must cover the same SKU, the same weeks and the same node set, or the ratio between them means nothing.
- Compute amplification per node as Var(orders placed) / Var(demand received), and report the squared coefficient-of-variation form alongside it. Mean volumes differ by an order of magnitude between echelons, so a raw variance ratio is not comparable across nodes of different size while the CV-squared ratio is scale-free.
- Read the table echelon by echelon and find where the ratio steps up. The generating node is where it first exceeds one by a wide margin, not the node that feels the pain: amplification observed at the plant is the product of every ratio below it along that chain.
- Name the mechanism at that node rather than asserting bullwhip generically. Minimum order quantity and lot-size multiple, the review period, batching up to a full truckload, and forecast-driven ordering that reacts to its own safety stock recalculation each leave a distinct signature in the order-size histogram.
- Price both options rather than choosing one. The buffer request converts to units, then to cost at standard_cost_cents and to carrying cost at the applicable cost of capital. The rule change converts to more frequent, smaller replenishment and therefore more freight per unit. Present the trade with both numbers attached.
Follow-up
- Orders cluster on exact truckload multiples. Would you break the batching rule, and what does that cost in freight per unit?
- How would you test an ordering-rule change when there are twelve nodes, they draw on the same stock and the same outbound loads, and one replenishment cycle is three weeks?
- Part of the plant's variance is its own production batching. How do you separate inbound amplification from what the plant creates for itself?
Suggested practice checklist
For a candidate whose interviews will centre on A/B testing, metric movement and causal claims. Design comes before arithmetic, arithmetic before analysis, and the week ends by rehearsing the readout rather than the derivation.
Day 1 — Design one test end to end on paper
- Take a single feature change and write the full design: randomization unit, the exact point of exposure, the primary metric with its grain, guardrails, allocation, planned duration, and the decision rule committed before any data exists.
- Write why the randomization unit must sit at or above the level where treatment can spill over, and give one case where user-level randomization is still contaminated (shared accounts or devices, or two participants in the same marketplace).
- State in advance what you will do if the primary metric is flat while a secondary metric is significant.
Deliverable: A one-page test design with a decision rule written before launch.
Day 2 — Power arithmetic until it is automatic
- Compute required sample size per arm for a binary metric with the normal approximation, n is approximately 2 times (z for alpha/2 plus z for power) squared times p(1 minus p) divided by delta squared, for baselines of 2, 10 and 40 percent at a 5 percent relative lift, and note that for a fixed relative lift the requirement falls as the baseline rises because delta grows proportionally with p.
- Redo the calculation for a continuous metric using variance in place of p(1 minus p), and show why a heavy-tailed quantity such as revenue per user needs either far more traffic or a capped version with a stated cap.
- Convert one of the results into weeks given a weekly eligible traffic figure, then list the two honest ways to shorten it (accept a larger detectable effect, or reduce variance) and write why quietly lowering the power target is a decision to miss more real wins, not a speedup.
Deliverable: A small script or sheet that maps baseline, minimum detectable effect, alpha and power to sample size and weeks, cross-checked against a published calculator.
Day 3 — Variance and the unit-of-analysis problem
- Take a ratio metric whose denominator is not the randomization unit (clicks per session, randomized by user) and compute the standard error twice, once naively at session level and once by the delta method or a user-level bootstrap, then record how much the naive version understates it.
- Implement CUPED on simulated data: choose a pre-period covariate X measured before assignment, estimate theta as Cov(Y, X) divided by Var(X), and analyse Y minus theta times (X minus its mean) in place of Y. Confirm the variance of the adjusted outcome equals the raw variance multiplied by one minus the squared correlation between Y and X, so a correlation of 0.45 removes about 20 percent of the variance and not 80.
- Now run that simulation a few hundred times and confirm the adjusted effect estimate is unbiased for the same effect rather than numerically identical to the raw one. Within any single run the two differ, sometimes by a large fraction of the true effect, because the two arms' pre-period covariate means never coincide exactly in a finite sample; they agree in expectation, which is the property that matters and the one to state out loud.
Deliverable: A notebook showing the adjusted estimator with a measurably smaller variance than the raw one, plus a repeated-simulation table showing the two estimators agreeing on average while differing run by run.
Day 4 — Validity threats you can actually test for
- Run a sample ratio mismatch check as a chi-square goodness-of-fit test against the intended allocation, and write the three causes you would chase first (assignment logged before exposure, an arm-specific redirect or load failure, bot filtering applied asymmetrically).
- Simulate peeking: generate A/A data, test daily at alpha 0.05 across 14 looks, record the inflated false positive rate, then apply an alpha-spending boundary or commit to a fixed horizon and confirm the rate returns to nominal.
- Write how you would separate a novelty effect from a durable lift using the treatment effect plotted against days since first exposure, and what shape would change your recommendation.
Deliverable: One table showing the peeking false positive rate before and after correction, plus a written SRM triage list.
Day 5 — When randomization is not available
- Write the identifying assumption for difference-in-differences (parallel trends in the absence of treatment), then plot pre-period trends for two candidate control groups and justify rejecting one of them.
- Design a switchback test for a change where user-level randomization would leak across participants, choosing a time-block length against the carryover you expect and saying how you would detect carryover in the data.
- List what an interrupted time series or a synthetic control buys you and the one thing neither can rule out: an unobserved shock that coincides with the launch.
Deliverable: A one-page memo recommending a single quasi-experimental design and naming its weakest assumption explicitly.
Day 6 — The readout query
- Write the assignment-to-exposure join that returns exactly one row per unit per experiment, and handle units appearing in both arms by excluding and counting them rather than silently keeping one.
- Compute the per-arm metric, its variance and the relative lift with a confidence interval in SQL, then reproduce the identical numbers in a notebook as a cross-check.
- Add a segment breakdown and write the sentence that keeps it from being p-hacking: segments declared in advance, everything else reported as exploratory and corrected for multiplicity.
Deliverable: A single query that outputs the full readout table, matched to a notebook recomputation.
Day 7 — Present it to someone who will not read the appendix
- Give a 10-minute readout of a real or simulated experiment in the order decision, number, uncertainty, caveat.
- Have your listener ask "can we ship it" in the case where the primary is flat and a guardrail moved, and answer with a recommendation rather than a request for more data.
- Rewrite your opening line so the recommendation lands before any methodology.
Deliverable: A one-page readout whose first line is the recommendation.
Your part, not the team's
Most data work is done by groups, so an interviewer has to work out which piece was yours. An answer that runs on 'we' for several minutes gets interrupted with a question about what you personally did, and by then the answer sounds defensive even when it is true. Mark your own contribution as you go, and name the parts that belonged to someone else instead of leaving them ambiguous. Keep a few specifics back as well, like the name of the metric or who actually objected, so a probe can be answered with something you had not already said.
Describe a situation where you faced a significant challenge in your work and how you overcame it.
Medium · behavioural and stakeholder questions
Approach
- Name the disagreement or constraint, and how you resolved it with evidence.
- Close with what you would do differently, concretely.
- Quantify the outcome, including what you would not claim credit for.
Follow-up
- What did you decide not to do, and why?
- What would you do differently if you ran that project again?
Defend a finding that the expedite program bought nothing
Medium · defending findings · counterfactual reasoning · landed cost
Over two quarters, expedite_premium_cents on outbound legs in fct_shipment_leg rose from 0.4 to 1.3 percent of landed cost, while perfect order rate moved from 91.2 to 91.5 percent, inside the week-to-week spread. The transport lead who sponsored the expedite program disputes the finding in a review in front of his director, arguing that your window contains a port disruption that would have made service worse without the spend. Present the finding, say what you concede on the spot, say what you hold, and name the evidence that would change your conclusion.
Approach
- Restate his objection in its strongest form before answering it, because a counterfactual worsening is a legitimate argument and treating it as an excuse ends the conversation.
- Separate what you measured from what you claimed: the data show no detectable service gain, not that expedite has no effect, and the difference is the entire argument.
- Test his hypothesis with the data you already have rather than defending in the abstract: split legs by exception_code = 'customs_hold' and by lane, and compare expedited against non-expedited legs on the same lanes in the same weeks, since if expedite were holding the line the expedited lanes should show a service gap over comparable non-expedited ones.
- Concede what is true: without a holdout you cannot rule out a protective effect, and the pre-period is contaminated by the disruption, so the honest statement is an upper bound on the gain rather than a zero.
- Hold the part that survives: the spend is real, it is concentrated in a small set of lanes, and nobody set a decision rule for when a leg gets expedited, which is a controllable problem independent of the counterfactual.
- Name the next measurement: a lane-level staggered switch-off with a stated burn-in, and say what it would cost and how long it would take.
Follow-up
- He offers to run the switch-off only on his two best lanes. Why is that a problem, and what do you counter with?
- His director asks you for a yes or no on cutting the budget today. What do you say?
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?