Interview guides · Arrive Logistics
Arrive Logistics Data Scientist Interview Questions & Guide 2026
Data scientist interview prep for Arrive Logistics: original practice on experiment design, SQL and metric reasoning, with worked solutions.
The 60-second brief
As a Data Scientist at Arrive Logistics, you sit at the intersection of complex supply chain logistics and advanced predictive modeling. Your work directly influences how the company optimizes freight movement, manages carrier capacity, and improves pricing accuracy in a highly volatile market. You are not just building models; you are solving real-world logistical puzzles that impact the bottom line of a fast-growing, tech-forward freight brokerage.
Seniority shifts the scope more than the words in the title do. Earlier-career loops mostly check that you execute a well-posed analysis correctly; senior loops check that you can decide which question is worth answering and defend what you chose not to do.
Focus areas
- Size safety stock under variable lead times
- Trace an on-time miss to one node
- Price a service-level change in working capital
Overview
The role requires a blend of technical rigor and business intuition. You will collaborate with engineering and product teams to translate ambiguous business problems into scalable data products. Whether you are forecasting market trends or automating operational workflows, your contributions drive the efficiency that defines Arrive Logistics as a leader in the industry. Expect to work on high-impact projects that require both deep mathematical understanding and the ability to communicate findings to non-technical stakeholders.
Where to focus your preparation
Treat the sections below as preparation areas and confirm the format with your recruiter.
Preparation focus
No round sequence has been reported for this company, so work the categories below and confirm the format with your recruiter.
What to demonstrate
- Breadth across SQL, experimentation and product reasoning
- Ability to state assumptions before choosing a method
How to prepare
- Drill the practice exercises below and time yourself
- Prepare three quantified stories about decisions you drove
Where candidates lose points
Averaging rates across SKU-locations instead of re-summing
Fill rate, turns, OEE and on-time rate are all ratios whose denominators differ by orders of magnitude between cells, so an unweighted mean gives a slow-moving C item at a small node the same vote as a high-volume A item at a national node. The blended figure then moves whenever the portfolio mix moves, and it can improve in every cell while the company-level ratio worsens, or the reverse, which is Simpson's paradox with a warehouse attached. Always sum numerator and denominator to the reporting level and divide once, and when a rate must be compared across nodes, standardise on a fixed SKU mix before reading anything into the difference.
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.
Over-explaining the method and under-explaining the implication
Lead with the answer and what you would do about it, then give the approach when asked. Roughly one sentence of method per three of implication is the right ratio for a stakeholder-facing answer; the interviewer already knows what a regression is.
Reaching for a model before the target metric exists
Before naming an algorithm, write down the label, the prediction time, and the action that changes when the score crosses a threshold. If you cannot say what decision the output drives, any modelling choice is guesswork dressed up as method.
Questions & practice
Simulate service levels under a variable inbound lead time
Medium · safety stock · monte carlo · service level
Daily demand for one SKU at one node is Poisson with mean 40. Replenishment lead time is 7 days with probability 0.7, 10 days with 0.2, and 14 days with 0.1, drawn independently per order. The policy is continuous review: order Q = 400 whenever the inventory position drops to the reorder point. Size safety stock two ways for a nominal 95 percent target, once as z times sigma_D times sqrt(mean lead time) and once with the variable-lead-time term. Simulate both and report achieved cycle service level and achieved unit fill rate.
Approach
- Compute both safety stock numbers analytically before simulating, so the simulation is a check on arithmetic you already understand rather than the source of the answer.
- Track inventory position (on hand plus on order) for the reorder trigger and on hand separately for the stockout test. Triggering off on hand alone re-orders while stock is already in transit and silently changes the policy you are measuring.
- Define the two outcomes precisely and separately: cycle service level is the fraction of replenishment cycles containing at least one unit of unmet demand, while fill rate is total units shipped from stock over total units demanded. They answer different questions and z targets only the first.
- Before running anything, bound the achievable service level by hand from the lead-time mixture. Each lead-time branch is a separate Poisson demand, so cycle service level is the probability-weighted sum of P(Poisson(40 x L) <= reorder point) across the three branches, and a reorder point that cannot cover the 14-day branch caps the policy at 0.9 no matter how the simulation is tuned.
- Run enough cycles that Monte Carlo error is small against the gap you are trying to see. The standard error of a service level is sqrt(p*(1-p)/n_cycles), about 0.005 at p = 0.7 with 10,000 cycles, so the 20-point gap between the two policies is unambiguous and a 1-point one is not.
- Let unmet demand be lost rather than backordered, state that choice, and note that switching to full backordering raises measured fill rate for the same policy.
Worked solution (35 min)
- Compute mean lead time 8.3 days, variance of lead time 5.01, sigma_D = sqrt(40) = 6.32, and mean demand 40 per day.
- Naive safety stock: 1.645 * 6.32 * sqrt(8.3) = 30 units, giving a reorder point of 332 + 30 = 362. Correct safety stock: 1.645 * sqrt(8.3 * 40 + 40^2 * 5.01) = 1.645 * 91.4 = 150 units, giving a reorder point of 482.
- Write one simulation loop parameterised by safety stock: draw daily Poisson demand, decrement on hand, record shortfall, trigger an order of 400 when position hits the reorder point, and schedule the receipt after a sampled lead time.
- Run 10,000 cycles per policy with a burn-in of at least two lead times discarded so the starting position does not flatter the first cycles.
- Report achieved cycle service level, achieved fill rate and the average on-hand position for each of the two safety stock settings.
Expected result: Naive safety stock is 30 units and the variable-lead-time figure is 150 units, a factor of five. At Q = 400 with lost sales, the naive policy achieves a cycle service level of about 0.70 and the correct policy about 0.90, so the naive policy misses the nominal 0.95 by roughly 25 points and the correct one by 5. The naive ceiling is arithmetic, not simulation noise: with a reorder point of 362 the 7-day branch is essentially always covered, the 10-day branch survives with probability P(Poisson(400) <= 362) = 0.029, and the 14-day branch never does, so cycle service level cannot exceed 0.7 + 0.20.029 + 0.10 = 0.706, and reorder-point undershoot of about 20 units under daily demand arrivals pulls it to roughly 0.70. The correct policy tops out near 0.90 for the same structural reason: a reorder point of 482 covers the 10-day branch almost completely (P = 0.99997) but not the 14-day one (P = 0.0004), so that 0.1 of probability mass is near-pure loss. Fill rate is higher than cycle service level under both, about 0.92 against 0.70 and about 0.98 against 0.90, because expected units short per cycle (roughly 33 and 10) are small against Q = 400.
Verify it yourself
- Set the lead time to a constant 8.3 days and confirm the two formulas return an identical safety stock, since the lead-time variance term vanishes.
- Compare the naive policy's simulated cycle service level against its analytic ceiling, 0.7 + 0.2P(Poisson(400) <= 362) + 0.1P(Poisson(560) <= 362) = 0.706 before undershoot. A simulated figure above that is a bug, usually triggering off on hand instead of inventory position, and three seeds should land a little below it and agree within about one point, consistent with a standard error of about 0.005 at 10,000 cycles.
- Confirm fill rate exceeds cycle service level at Q = 400 under both policies, and treat that ordering as a consequence of Q rather than a law: expected units short per cycle depends on the reorder point and the lead-time demand distribution, not on Q, so holding the reorder point fixed and halving Q leaves cycle service level roughly unchanged while fill rate falls by about the same shortfall over a smaller denominator.
- Confirm simulated demand over the lead time has mean near 332 units and standard deviation near 91.4, which is exactly the quantity the correct formula multiplies by z.
Follow-up
- With Q = 100 against lead-time demand of 332 units, three or four orders are open at once. What happens to the meaning of a replenishment cycle, and to each of the two service measures?
- Lead time and demand are correlated because the supplier is capacity constrained in peak weeks. What does that do to the formula you used?
- How would you convert the extra 120 units of safety stock into an annual cost the business can weigh against the service gain?
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.
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?
Cluster bootstrap for landed cost per delivered unit
Medium · bootstrap · clustering · ratio estimator · mix shift
You have delivered outbound legs from fct_shipment_leg: leg_id, shipment_id, leg_seq, carrier_id, origin_location_id, destination_location_id, shipped_units, cube_m3, linehaul_cost_cents, fuel_surcharge_cents, accessorial_cost_cents, expedite_premium_cents. In this extract the linehaul for a multi-stop load is booked entirely on leg_seq 1, so allocate it across the load's legs by cube first. Then estimate the difference in landed cost per delivered unit between two carriers on lanes both serve, with a 95 percent interval. Write the resampling yourself; no bootstrap library.
Approach
- Allocate the linehaul before anything else, in proportion to each leg's cube over the load's total cube, and keep the other three cost columns where they already sit. State the choice: allocating by weight instead reorders lanes whenever freight is bulky rather than dense.
- Restrict to the set of lanes both carriers actually serve in the window. Comparing over all lanes measures which carrier was assigned the cheap lanes, not which carrier is cheaper.
- Resample shipments, not legs. Legs of one load share a single allocated linehaul and a single dispatch decision, so treating them as independent draws understates the variance of the estimate.
- Recompute the statistic as a ratio of sums on every resample, total allocated cost over total shipped units. Taking the mean of leg-level cost-per-unit values instead gives a different estimand in which a one-unit leg counts as much as a full truckload.
- Report both the raw difference and a lane-standardised difference where lane weights are fixed at the pooled volume share, and say which one you would put in front of a decision maker.
Follow-up
- The interval crosses zero. What would you need in volume or in window length to resolve a difference of 2 cents per unit?
- One carrier's accessorials are rising while its linehaul is flat. What is that signature telling you about execution versus rates?
- How does your interval change if one shipment accounts for 15 percent of the units?
Landed cost per delivered unit by lane without fan-out
Medium · fan-out · many-to-many · cost per unit
fct_shipment_leg holds one row per leg: shipment_id, leg_seq, origin_location_id, destination_location_id (NULL when the stop is a customer address rather than a network node), direction, mode, shipped_units, stop_count, leg_status, and linehaul_cost_cents, fuel_surcharge_cents, accessorial_cost_cents and expedite_premium_cents. fct_order_line carries shipment_id, NULL until shipped. Compute monthly landed cost per delivered unit by lane for direction in ('outbound','interfacility'), keying the lane on origin and destination with customer stops bucketed. Do not route through fct_order_line to get units. State the multi-stop allocation basis and what shipped_units means on a leg.
Approach
- Keep the entire computation at leg grain. Numerator is the sum of the four cost columns, denominator is SUM(shipped_units), both over legs with leg_status = 'delivered' and the two directions. The order table is not needed and joining it can only damage the number.
- Bucket destination_location_id IS NULL into an explicit customer_stop lane key with COALESCE, because a NULL never equals anything and will otherwise drop the row from a join or split it silently in a GROUP BY comparison.
- Say out loud whether shipped_units is units on board or units dropped at that stop. Under units on board, summing the legs of a multi-stop load counts a unit once per leg it rides and understates cost per unit; under units dropped, the leg sum is a true shipment total.
- Declare whether multi-stop cost is allocated by weight or by cube, since dense and bulky freight rank lanes in opposite orders under the two bases and the lane league table is not readable without it.
- Roll months up by re-summing cost and units and dividing once. Averaging leg-level cost per unit weights a one-pallet LTL leg the same as a full truckload.
Worked solution (25 min)
- Filter fct_shipment_leg to leg_status = 'delivered', direction IN ('outbound','interfacility'), and the month range.
- Build lane_key from origin_location_id and COALESCE(destination_location_id::text, 'customer_stop').
- Aggregate per month and lane_key: total_cost_cents as the sum of the four columns, total_units as SUM(shipped_units), leg_count as COUNT(*).
- Divide once: cost_per_unit_cents = total_cost_cents / NULLIF(total_units, 0).
- Cross-check the grand total against an unaggregated SUM over the same filter, and record the allocation basis and shipped_units semantics in the output header.
Expected result: One row per month and lane with total_cost_cents, total_units, leg_count and cost_per_unit_cents, computed only from fct_shipment_leg, where leg_count equals a plain COUNT(*) over the identical filter.
Verify it yourself
- SUM(shipped_units) over the leg set equals a standalone aggregate with no joins. Routing through fct_order_line makes it larger, which is the fan-out showing itself.
- Lane cost totals sum to the unfiltered month total for those directions and statuses.
- Legs with destination_location_id NULL appear in the output under the customer bucket rather than going missing.
Follow-up
- Cost per unit rose 8 percent while contracted linehaul rates were flat. Which columns do you cut first, and what does a rise in accessorials rather than linehaul point to?
- How do you separate a rate change from a mix change across mode and service_level?
- If cost genuinely had to be attributed to an order line, how would you allocate a leg's cost, and what does your rule do to a line that shipped one heavy item on an otherwise full truck?
Stockout streaks of three days or more per cell
Medium · window functions · gaps and islands · stockout analysis
fct_inventory_daily carries one row per sku_id x location_id x inventory_date for every active pair, including days with nothing on hand, plus stockout_flag marking days where available_qty reached zero. Return every run of consecutive stockout days lasting three days or more: sku, location, start date, end date, run length, and demand_qty summed across the run. Return alongside it, per sku-location, the count of such runs in the window. Verify that the daily row series is contiguous for each pair before relying on date arithmetic, and say what you do about pairs that fail.
Approach
- Filter to stockout_flag = TRUE, then assign ROW_NUMBER() OVER (PARTITION BY sku_id, location_id ORDER BY inventory_date).
- Form the island key as inventory_date - rn * INTERVAL '1 day'. Two rows share a key only when their dates are exactly consecutive, so the key cannot weld separate runs together; the exposure runs the other way. A day with no snapshot row inside a true stockout breaks that run into fragments, and any fragment under three days is deleted by the threshold, so the run leaves the output rather than appearing with a wrong length.
- Prove contiguity before trusting the key: per sku-location compare COUNT(DISTINCT inventory_date) against (MAX(inventory_date) - MIN(inventory_date) + 1), and separately confirm no pair carries two rows on one date, since a duplicate date advances rn without advancing the calendar and shifts every key after it. Pairs that fail get a generated date spine: LEFT JOIN generate_series over the window so each day is labelled stockout, in stock, or no snapshot, then apply a stated rule for the no-snapshot days instead of letting the key make that call silently. The conservative rule is to break the run at the hole and mark both fragments gap_adjacent.
- Group by sku, location and island key for MIN and MAX date, COUNT(*) as length and SUM(demand_qty) as the demand exposed to the stockout, keeping runs of length >= 3.
- Aggregate once more per sku-location for the run count, and mark runs touching either window edge as censored, since their true length is unknown from this window alone.
Follow-up
- The longest runs cluster on X-class A items. What does that combination suggest to look at before touching the safety stock policy?
- How would you estimate lost demand across a run, given that demand_qty records the orders that were placed but not the orders nobody bothered to place?
- The snapshot is taken at each location's local end of day and the pairs span several timezones. What breaks, and what stays true?
Set a stopping rule and error budget for a daily-read test
Medium · peeking · sequential testing · multiple comparisons · alpha spending
A carrier allocation change is randomised at the shipment leg within lane. The primary metric is landed cost per delivered unit from fct_shipment_leg; the team also watches 14 guardrails, including on-time rate, accessorial share and refused legs. The dashboard refreshes daily and the team plans to stop as soon as the primary is significant. The run is planned at 20 business days. Give the type I error of that plan, specify a stopping rule that fixes it, and say how you treat the 14 guardrails.
Approach
- Quantify the peeking cost rather than describing it. Testing accumulating data at nominal two-sided 0.05 on each of 20 equally spaced looks carries a type I error of roughly 25%, against the 5% the team believes it is running; the inflation comes from taking the maximum of a correlated sequence of statistics instead of reading one.
- Match the correction to the behaviour they actually want. To stop early on a fixed schedule, use a group-sequential plan with an alpha-spending function: at five interim looks the O'Brien-Fleming boundaries are z = 4.56, 3.23, 2.63, 2.28, 2.04, which spends almost nothing early and leaves the final look near full alpha for about 3% more maximum sample; Pocock's constant z = 2.41 stops earlier but pays for it at the end. To look whenever they like with no schedule, use an anytime-valid confidence sequence, which is honest under continuous monitoring but needs more data for the same interval width.
- Do not adjust the primary for the guardrails. It is one hypothesis, and its alpha is spent entirely by the stopping rule.
- Treat the guardrails by what a miss costs. For the few that would actually block a launch, pre-register non-inferiority margins and test each at unadjusted 0.05, because a Bonferroni here buys a lower false-alarm rate by blinding the guardrail to the regression it exists to catch. Report the rest with Benjamini-Hochberg q-values at FDR 0.1 as triage, and state up front that 14 independent guardrails at 0.05 give a 1 - 0.95^14 = 51% chance of at least one spurious flag.
- Cluster the variance to match the dependence: legs on the same lane and day share weather, carrier capacity and fuel shocks, so leg-level iid standard errors are too small and make every boundary easier to cross than the design intends.
Worked solution (30 min)
- State the uncorrected error: about 25% at 20 looks, against a believed 5%.
- Fix five interim analyses at equal information fractions with an O'Brien-Fleming spending function and boundaries z = 4.56, 3.23, 2.63, 2.28, 2.04.
- Re-size for the plan: maximum sample rises about 3% over a fixed-horizon test at the same power, while expected sample falls when a real effect is present.
- Write the guardrail policy: three or four blocking guardrails with pre-registered non-inferiority margins at unadjusted 0.05, the remainder Benjamini-Hochberg at FDR 0.1 for triage only.
- Fit the primary with standard errors clustered at lane by day and re-check the boundary crossings under the clustered SEs.
Expected result: Daily testing for 20 days at nominal 0.05 runs a type I error near 25%. A five-look O'Brien-Fleming plan restores 5% overall while still permitting an early stop, at about 3% more maximum sample. The 14 guardrails carry a 51% chance of at least one spurious flag at unadjusted 0.05 and are handled with pre-registered blocking margins plus BH at FDR 0.1 for the rest, never by adjusting the primary.
Verify it yourself
- The final O'Brien-Fleming boundary, 2.04, should sit just above the fixed-horizon 1.96; a plan whose last boundary is far above that is front-loaded and is throwing away power.
- Simulate the null by replaying the daily rule on 1,000 A/A samples of historical legs: the uncorrected crossing rate should land near 25% and the corrected near 5%.
- Recompute 1 - 0.95^k for the number of guardrails you actually ship with; at 14 it is 0.51, and the figure should be quoted in the readout rather than assumed.
Follow-up
- Legs of one shipment share a truck and an allocated cost. Is the leg a legitimate randomisation unit here, and what breaks if it is not?
- The primary crosses the boundary on day 4. What do you need to see before you recommend stopping, and what would make you keep running anyway?
A carrier scorecard the carrier cannot move on its own
Medium · gameable metrics · mix shift · immutable promise
Design the quarterly scorecard that decides carrier award volume. You have fct_shipment_leg (carrier_id, mode, service_level, origin_location_id, destination_location_id, tendered_at_utc, picked_up_at_utc, promised_delivery_at_utc, delivered_at_utc, planned_transit_hours, actual_transit_hours, stop_count, shipped_units, linehaul_cost_cents, fuel_surcharge_cents, accessorial_cost_cents, expedite_premium_cents, leg_status, exception_code). promised_delivery_at_utc is mutable and exception_code is carrier-supplied. Specify the primary metric, the guardrails, the mix control, and what must change in the data model before the first scorecard is published.
Approach
- Name the data defect first. A mutable promise column lets the scored party move its own target, so require a tender-time snapshot of the promise, either an immutable column or a promise event log, and label any scorecard built before that exists as advisory.
- Split the clock by who controls it: shipper dwell from tendered_at_utc to picked_up_at_utc, carrier transit from picked_up_at_utc to delivered_at_utc. Score transit against planned_transit_hours and report dwell separately, so a late tender is neither laundered into a carrier miss nor available as a carrier excuse.
- Treat exception_code as a claim, not a fact. Publish on-time both with and without carrier-coded exceptions; excluding weather then has to be argued in the open, and a widening gap between the two series is itself the signal.
- Control the mix. On-time and cost per unit both move with lane, mode, stop_count and service_level, so compare carriers only within lane by mode by service level cells and roll up on a volume weighting frozen from a prior period.
- Pair service with delivered cost per unit including accessorials and expedite premium, because on-time bought by upgrading to guaranteed service or by paying expedite is a price increase rather than a carrier improvement.
- Publish the award rule before the quarter opens, including the minimum leg count per cell below which a carrier is unranked, so a carrier with nine lucky legs cannot outrank one with nine hundred.
Follow-up
- A carrier's on-time is flat but its accessorial charge per leg has doubled. What do you check first?
- How do you score a lane where one carrier holds all the volume and no comparator exists?
Forecast accuracy collapsed at nodes missing snapshot rows
Medium · pipeline failure · wmape · data completeness
WMAPE at lag 7 for one region jumped from 31 percent to 58 percent over three weeks with no model change and no retraining. fct_inventory_daily is specified to carry one row per active sku-location-date even when on_hand_qty is zero, and a release changed how the end-of-day cut-off is derived from dim_location.timezone. Using fct_inventory_daily (inventory_date, sku_id, location_id, demand_qty, forecast_qty_lag7, stockout_flag) and dim_location, decide whether demand changed or the table did, and quantify the contaminated share before anyone retrains.
Approach
- Run a completeness check before touching any accuracy number: expected rows per location per day from the active sku-location grid, against actual rows present. If the table is incomplete the accuracy question is not well posed, and this check costs one query.
- Separate the two failure modes a cut-off change produces. Rows missing entirely shrink the denominator, and the sku-days that vanish are not a random sample. Rows present but with demand pushed onto the adjacent date create a one-day shift that inflates absolute error on both days while leaving the weekly total intact.
- Test the shift hypothesis directly by comparing SUM(demand_qty) per ISO week per node before and after the release. If weekly totals match and only the daily split moved, the model is fine and the grain is broken.
- Recompute WMAPE on clean node-days only and report both figures with the excluded row count and the share of demand those rows represent, since the metric definition already requires the excluded count to travel with the number.
- Check whether the affected nodes share a timezone that crosses a daylight-saving boundary or sits far from the offset the pipeline assumed. That grouping is the discriminating evidence between a code defect and genuine demand movement.
Follow-up
- Weekly totals match and only the daily split moved. Does the ordering decision care? At what replenishment frequency does it stop mattering?
- How would you backfill the missing days, and which of them would you refuse to backfill?
- What assertion would you add to the snapshot job so this fails loudly instead of surfacing three weeks later as model drift?
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.
Getting an analysis acted on
Work that nobody used is a common and unflattering pattern in data careers, and interviewers probe for it. Have a story about an analysis that changed a decision, and be specific about how you got it in front of the person who could act. Also have one about work that went nowhere, with your reading of why.
Can you describe a time you had to explain a complex model to a non-technical stakeholder?
Medium · behavioural and stakeholder questions
Approach
- Close with what you would do differently, concretely.
- 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 would you do differently if you ran that project again?
- What did you decide not to do, and why?
How do you handle situations where you have conflicting priorities or tight deadlines?
Medium · behavioural and stakeholder questions
Approach
- State the situation in two sentences and spend the rest on your reasoning.
- 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.
Follow-up
- How did you know the outcome was caused by your change?
- What would you do differently if you ran that project again?
Why do you want to work at Arrive Logistics specifically?
Medium · behavioural and stakeholder questions
Approach
- Name the disagreement or constraint, and how you resolved it with evidence.
- Quantify the outcome, including what you would not claim credit for.
- Close with what you would do differently, concretely.
Follow-up
- What would you do differently if you ran that project again?
- What did you decide not to do, and why?
What is your preferred work process when starting a new data project from scratch?
Medium · behavioural and stakeholder questions
Approach
- Quantify the outcome, including what you would not claim credit for.
- State the situation in two sentences and spend the rest on your reasoning.
- Close with what you would do differently, concretely.
Follow-up
- What would you do differently if you ran that project again?
- How did you know the outcome was caused by your change?