Interview guides · Hatch It
Hatch It Data Scientist Interview Questions & Guide 2026
Hatch It data scientist prep on data pitfalls: selection bias, target leakage, and checking an outlier's provenance before you drop it.
The 60-second brief
As a Data Scientist at Hatch It, you are not just building models; you are acting as a bridge between complex, high-stakes data and actionable product strategy. You will be embedded within the Product team, tasked with designing and launching robust data pipelines that transform raw, "arcane" datasets into intuitive, mission-critical analytics. Your work directly impacts security, energy, and healthcare sectors, meaning your contributions have real-world consequences that extend far beyond simple metrics.
Ask early whether the loop includes an asynchronous take-home or a timed live case, because the two are graded on different things. A take-home is read as an artifact: the question you decided to answer, what you did about missing or malformed records, and a conclusion stated plainly enough for someone to act on. A reviewer who cannot rerun your notebook discounts the result whatever score is printed in it. Hold to the stated time box and write down what you would have done with more of it, since the follow-up round is usually a live defence of the same work.
Focus areas
- Measure churn only on renewal-eligible accounts
- Analyse at the account grain, cluster errors
- Read NRR on a fixed account cohort
Overview
This role requires a unique balance of rigorous engineering and inquisitive data analysis. You will be expected to construct elegant system-level architectures while maintaining a relentless focus on data quality, reliability, and efficiency. Success here is defined by your ability to move at the speed of innovation, take clear ownership of your projects, and communicate technical insights to non-technical stakeholders effectively. If you are someone who thrives on solving large-scale, cross-disciplinary challenges, this role offers the perfect environment to apply your skills to high-impact, purpose-driven work.
Because this role requires an active Secret clearance, ensure your security documentation is up to date and ready for verification early in the process.
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
Treating raw request or usage volume as engagement
Most traffic in this domain is emitted by machines. Continuous-integration pipelines, scheduled batch jobs, synthetic monitors, backfills and client retries can all grow by an order of magnitude from one configuration change made by one engineer, and none of it represents a new decision to use the product. The inversion is what makes it dangerous: when the platform degrades, clients retry, so error-driven retry volume rises at the exact moment the customer is most likely to leave, and an engagement dashboard built on raw counts shows growth immediately before a churn. Filter on traffic_class and on successful status before anything else, and keep failed-request volume as its own separate series.
Reporting a mean over accounts when account revenue is heavy-tailed
When a small number of accounts hold most of the revenue, the sample mean is dominated by whichever of them happens to be in the sample, and the sample variance keeps growing as more data arrives instead of stabilising. In that regime the usual central-limit-based confidence interval understates uncertainty, and a single renewal or a single large account's batch job can flip the sign of a measured effect. The fixes are to pre-register a winsorisation or capping rule before looking at the outcome, to report account counts crossing a threshold alongside the revenue figure, or to define the estimand on a bounded transform. Choosing the cap after seeing the result is a separate and worse problem, because the cap then encodes the answer.
Extrapolating a first-week lift inflated by novelty effects
Plot the treatment effect by days since first exposure instead of quoting one pooled average. A lift that decays toward zero across the test window is behaviour that will not persist, and annualising it produces a forecast that misses by an order of magnitude.
Reading experiment results before checking the arm split
Compare observed arm counts against the intended allocation ratio, not an assumed even split, and set the alarm far below the conventional 0.05: at 0.05 roughly one healthy experiment in twenty trips it, which is why sample-ratio checks usually run at p < 0.001 or stricter. The test's power scales with sample size, so it misses a real diversion on a small experiment and fires on an imbalance too small to move the estimate on a very large one. A flag means go find the assignment or logging fault before reading any outcome, not report a mismatch.
Questions & practice
Explain your process for validating the quality and integrity of a new data source before integrating it into a production model.
Medium · machine learning and modelling
Approach
- 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.
- Set a baseline first, so any model has something honest to beat.
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?
Bootstrap a confidence interval for net revenue retention
Medium · bootstrap · retention · resampling
You have one row per account with arr_start_cents (ARR twelve months ago) and arr_end_cents (ARR today, zero if churned), covering the fixed cohort of accounts that had ARR twelve months ago. Net revenue retention is sum(arr_end_cents) / sum(arr_start_cents). Write a nonparametric bootstrap from scratch, without scipy.stats.bootstrap: resample accounts with replacement, recompute the ratio of sums on each resample, and return the point estimate with a 95% percentile interval from 10,000 resamples. Also report the interval you would get from the mean of per-account ratios, and explain the difference.
Approach
- Resample the account, because the account is the unit the estimand is defined over. One bootstrap draw is a vector of account indices and both numerator and denominator are recomputed from that same draw; resampling the two sides independently destroys the within-account correlation that makes a ratio estimator stable.
- Vectorise the draws: idx = rng.integers(0, n, size=(B, n)), then end[idx].sum(axis=1) / start[idx].sum(axis=1). A 10,000 by n index matrix is usually far cheaper than a Python loop; if the matrix is too large for memory, chunk over B rather than reverting to a loop.
- Take the interval from np.quantile(ratios, [0.025, 0.975]). The percentile interval differs from estimate +/- 1.96 * bootstrap SE whenever the resample distribution is skewed, which it will be here, and the skew is the thing you want represented.
- Compute the mean-of-ratios version on the same resamples, and state the exact relationship rather than guessing which of the two is larger. With r_i = arr_end_i / arr_start_i, the ratio of sums is the arr_start-weighted mean of exactly those r_i, so sum(end)/sum(start) - mean(r) = Cov(arr_start, r) / mean(arr_start) using the population covariance. The gap is positive when larger accounts retain and expand better than smaller ones, and negative when they do not; a cohort whose small accounts churn at a higher rate has positive covariance, which puts the mean of per-account ratios BELOW the ratio of sums. Requires arr_start_i > 0 for every account, which the fixed-cohort definition guarantees; r_i is floored at 0 and unbounded above, so a handful of 4x expansions among small accounts can flip the sign. Compute the covariance and report it instead of asserting a direction.
- Report the interval width beside the concentration of the cohort. If the largest account is 12% of starting ARR, a narrow interval is evidence that the resampling unit is wrong rather than evidence that the estimate is precise.
Worked solution (30 min)
- start = df.arr_start_cents.to_numpy(float); end = df.arr_end_cents.to_numpy(float); n = len(start); point = end.sum() / start.sum()
- rng = np.random.default_rng(7); idx = rng.integers(0, n, size=(10_000, n)); ratios = end[idx].sum(1) / start[idx].sum(1)
- lo, hi = np.quantile(ratios, [0.025, 0.975]); return point, lo, hi
- per_acct = end / start; mean_point = per_acct.mean(); mean_boot = per_acct[idx].mean(1); compare np.quantile(mean_boot, [0.025, 0.975]) against (lo, hi), and report np.cov(start, per_acct, ddof=0)[0,1] / start.mean() as the quantity that accounts for the gap between the two centres.
Expected result: A point estimate equal to sum(end)/sum(start) on the unresampled data — the bootstrap mean is close but must not be substituted for it — and a percentile interval that is asymmetric around the point when expansion is skewed. The mean-of-ratios interval is centred on a different estimand, and which side it falls on is decided by Cov(arr_start, r): it sits below the ratio of sums in the common case where small accounts churn at a higher rate than large ones, and above it when the large accounts are the ones shrinking. Its width is governed by the spread of per-account growth, while the ratio-of-sums width is governed by how concentrated starting ARR is, so neither interval is reliably the wider one.
Verify it yourself
- point equals sum(end)/sum(start) computed directly on the original arrays, and point - per_acct.mean() equals np.cov(start, per_acct, ddof=0)[0,1] / start.mean() to floating-point tolerance. That is an algebraic identity, not an empirical claim: a mismatch means the per-account ratios or the weights are wrong, and the sign of it tells you which of the two numbers is larger before you look.
- np.median(ratios) sits within a small fraction of a point of the point estimate; a large gap indicates a wrong resampling unit or a transposed index matrix.
- Raising B from 10,000 to 20,000 moves the endpoints by far less than the interval width.
- Setting every arr_end equal to its arr_start gives point == 1.0, per_acct.mean() == 1.0, a zero covariance term and a zero-width interval.
Follow-up
- The cohort has 800 accounts and the largest is 12% of starting ARR. How much do you trust a percentile interval here?
- How would you extend this to an interval on the year-over-year change in NRR?
- Two accounts merged mid-window and one contract was co-termed into the other. How do you keep the cohort fixed?
Quantify billable volume created by retries after server errors
Hard · telemetry-hygiene · pandas · unit-economics
From fct_api_request (request_id, account_id, endpoint, idempotency_key, is_retry, http_status, request_at, billable_units), measure how much billable volume in a 28-day window is retry traffic that followed a 5xx. Group requests into attempt chains by (account_id, endpoint, idempotency_key) ordered by request_at; a request is error-driven if any earlier attempt in its chain returned 5xx. Requests with a null idempotency_key cannot be chained, so report them as their own class rather than assuming each is unique. Return billable_units split into first-attempt, error-driven retry, other retry and unchainable, per account.
Approach
- Split the population before measuring anything. A null idempotency_key is not a chain of one, it is an unknown; report its share of billable_units first, because if it is 40% of volume then the headline estimate is a lower bound and the deliverable has to say so.
- Within chainable rows, sort by (account_id, endpoint, idempotency_key, request_at) and derive 'any earlier attempt failed' with arithmetic rather than a per-group lambda: with is5 = (http_status >= 500), the per-chain cumsum minus the row's own value is positive exactly when an earlier attempt in that chain returned 5xx. A groupby-apply gives the same answer and is unusable at five million rows.
- Do not take is_retry as the definition. It is set by the client whenever an idempotency_key is resent, which covers retries after client-side timeouts and after 4xx as well; compute the flag yourself and then cross-tabulate it against is_retry, because the disagreement is a finding in its own right.
- Aggregate billable_units by (account_id, class) and assert the classes sum to each account's total. The spine sets billable_units to zero on 5xx responses, so the failed attempt contributes nothing and the whole inflation sits in the successful retry that follows it.
- Report the per-account share and look at its distribution, not the fleet total. One account in a retry storm dominates any blended figure, which is the same failure that makes a fleet-wide error rate useless.
Follow-up
- An account's error-driven share is 22%. Is that the platform's fault or the client's, and what do you look at next?
- How would you define a consumption-based north-star metric that an outage cannot inflate?
- Chains straddle the 28-day boundary. How large is that bias and in which direction?
What are the trade-offs between using a SQL-based database versus a NoSQL solution for the specific architectures you have built?
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.
- Compute rates by summing numerator and denominator separately, never by averaging rates.
Follow-up
- How does the query change if the join becomes one-to-many?
- What breaks if events arrive late or out of order?
Collapse retried requests into logical operations per account
Medium · window functions · deduplication · null semantics
fct_api_request carries request_id, account_id, api_key_id, idempotency_key (null when the caller supplied none), is_retry, request_at and http_status. For one ISO week, count logical operations rather than HTTP calls: requests sharing (account_id, api_key_id, idempotency_key) collapse to the earliest one, while every request with a null idempotency_key is its own operation. Return per account the raw request count, the logical operation count and the ratio between them, and state which direction a rising 5xx rate pushes that ratio.
Approach
- Split the stream before deduplicating. GROUP BY and PARTITION BY treat NULLs as equal to one another, while the = operator does not, so a single grouping pass folds every null-key request in an account into one 'operation' and can cut the count by orders of magnitude.
- Deduplicate the keyed rows with ROW_NUMBER() OVER (PARTITION BY account_id, api_key_id, idempotency_key ORDER BY request_at, request_id) and keep rn = 1. Including request_id in the ORDER BY makes the result deterministic when two rows share a millisecond, which they will.
- UNION ALL the null-key rows back in unchanged; they need no dedup and must not pass through the partition.
- Aggregate per account: count() over the raw week, count() over the union, and the ratio of the two. Report the ratio, not the difference, so accounts of very different sizes are comparable.
- Cross-check against is_retry, which flags a resend of the same idempotency key: raw minus logical should sit close to the count of is_retry rows, and a large gap means clients are resending without a key and the dedup is under-counting duplicates.
- Name the inversion explicitly: 5xx responses provoke client retries, so amplification rises exactly when reliability falls, and any engagement metric built on raw request counts will show growth during an outage.
Worked solution (25 min)
- Filter to the ISO week with request_at >= :week_start AND request_at < :week_start + interval '7 days', and record the raw count per account as the baseline.
- Build keyed_dedup: SELECT ... ROW_NUMBER() OVER (PARTITION BY account_id, api_key_id, idempotency_key ORDER BY request_at, request_id) AS rn FROM the week WHERE idempotency_key IS NOT NULL, then keep rn = 1.
- Build unkeyed: the same week's rows WHERE idempotency_key IS NULL, taken as-is.
- UNION ALL the two, then GROUP BY account_id selecting the logical count; join back to the raw counts and compute raw::numeric / logical.
- Validate on one account by comparing raw - logical against COUNT(*) FILTER (WHERE is_retry) for that account and explaining any gap.
Expected result: One row per account with raw_requests, logical_operations and an amplification ratio at or above 1.0, where logical_operations counts each distinct (account_id, api_key_id, idempotency_key) once and each null-key request once.
Verify it yourself
- logical_operations is never greater than raw_requests, and the two are equal for accounts whose keys are all distinct.
- For accounts with no null idempotency_key, raw_requests - logical_operations is close to the count of is_retry rows.
- Deliberately grouping on the key without the null split reproduces a visibly lower count, confirming the NULL collapse is the mechanism.
- Re-running the dedup returns the same surviving request_id set, proving the ORDER BY tiebreak made it deterministic.
Follow-up
- Which of these two counts belongs in the billable-units metric, and which in an engagement metric?
- An account's amplification ratio jumps from 1.05 to 3.0 in a day. Name three causes and the query that separates them.
How do you optimize a Pandas workflow when dealing with datasets that exceed available memory?
Medium · product sense and metrics
Approach
- State what result would change your recommendation, so the answer is falsifiable.
- Name one primary metric, then the guardrail that stops it being gamed.
- Fix the population and the time window before naming any metric.
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?
Stop metering retried requests: design the metric that decides it
Medium · guardrail conflict · billing integrity · retries · expected value
The platform meters accepted requests. A proposal carries three clauses: stop metering fct_api_request rows where is_retry = true, stop metering rows with a 4xx status, and stop metering rows with http_status >= 500. Its author has attached one figure to all three together, roughly 4% of requests_thousands volume. You have fct_api_request (account_id, is_retry, idempotency_key, http_status, traffic_class, billable_units, request_at) and fct_usage_daily (billable_quantity, net_amount_cents, cogs_cents). Size each clause separately before arguing about any of them, then define the primary metric, the guardrail that genuinely conflicts with it, and how you resolve that conflict for a decision that has to be made this quarter. Revenue falls this quarter with certainty; any benefit appears at renewals up to twelve months out.
Approach
- Size the three clauses before accepting the headline 4%, because one of them is a no-op. billable_units is defined as zero for requests that failed with a 5xx, so the third clause removes no metered volume at all. Confirm that in the data rather than trusting the column comment: if sum(billable_units) over rows with http_status >= 500 is not zero, the metering pipeline contradicts its own definition and that is a billing defect to file before any pricing conversation happens. The two live clauses are 4xx failures, which are metered in full, and retries that did not themselves end in a 5xx.
- Size the two live clauses as a union, not a sum. A retry can return 4xx, so the clauses overlap and adding their volumes counts that intersection twice. Partition the trailing 90 days into four mutually exclusive buckets instead: clean (is_retry = false, http_status < 400), non-retry 4xx, retry with http_status < 500, and http_status >= 500. Report the removable share per account as a distribution; if the mass sits in a handful of accounts this is a commercial conversation with those accounts rather than a platform-wide pricing change.
- State the conflict rather than dissolving it. The primary metric, net metered revenue per paying account, and the integrity guardrail, the share of metered volume that is retried or failed traffic, move in opposite directions by construction. No redefinition removes that. The job is to price the trade-off, not to make it disappear.
- Show the perverse coupling with data, and be exact about its mechanism. Because a 5xx already carries zero billable_units, the platform is not paid directly for its own failures; it is paid for the retries and the client-side 4xx traffic those failures provoke, which is one step removed and therefore easy to miss. Cross-tab each account's trailing 28-day 5xx rate against its metered volume in the same window. If metered volume rises with error rate, that indirect coupling is the actual argument for the change.
- Resolve on expected value with the uncertainty stated. The revenue loss is computable and near-certain; the renewal benefit is not, so invert it and state the break-even: how many basis points of gross logo retention on the renewal-eligible base would offset the loss. That converts an argument about values into an argument about one number. Then propose the measurement that would settle it instead of claiming a readout you do not have: stage the rollout by renewal cohort so accounts whose terms end soonest are treated first, read out on gross logo retention on the renewal-eligible base, and say honestly whether the number of annual renewals in the window can support that estimate at all.
Worked solution (30 min)
- Test the third clause first: over the trailing 90 days compute count(*) and sum(billable_units) from fct_api_request where http_status >= 500. The sum must be zero, because billable_units is defined as zero for 5xx failures. If it is zero the clause removes nothing and drops out of the analysis; if it is not, stop and raise a metering defect, because every volume figure downstream of that column is then suspect.
- Compute trailing-90-day metered volume per account in the four mutually exclusive buckets: clean, non-retry 4xx, retry with http_status < 500, and http_status >= 500. Roll the total up to the requests_thousands SKU and reconcile it against fct_usage_daily billable_quantity for the same window.
- Convert the two removable buckets to money using each account's realised rate, net_amount_cents / billable_quantity from fct_usage_daily, because list rate overstates revenue for every discounted account.
- Annualise the revenue at risk and divide it by the ARR of the renewal-eligible base to express the break-even as an improvement in gross logo retention, in basis points.
- Cross-tab account 28-day 5xx rate deciles against metered volume per account to establish whether the error-to-revenue coupling, which can only run through retries and 4xx rather than through the failed requests themselves, is real or a story.
Expected result: Three clause sizes rather than one blended figure: zero for the 5xx clause, since billable_units is already zero there, and separate volumes for 4xx failures and for retries that did not 5xx, combined as a union so the retry-and-4xx intersection is counted once. On top of that, a single break-even figure stated as the improvement in gross logo retention on the renewal-eligible base needed to offset the metered revenue actually removed, and the per-account concentration of that removable volume. The answer says that the primary metric and the integrity guardrail conflict by construction, and resolves it with the break-even number and a staged rollout rather than by moving a definition.
Verify it yourself
- sum(billable_units) over rows with http_status >= 500 must be exactly zero; any other result contradicts the documented definition of the column and invalidates the volume figures derived from it.
- The four buckets must be mutually exclusive and must sum to total billable_units, which must in turn reconcile to fct_usage_daily billable_quantity within the known restatement tolerance; a large gap means the request-to-SKU mapping is wrong.
- The two removable buckets must be reported as a union; their arithmetic sum will exceed the union by exactly the retry-and-4xx intersection, and a write-up that quotes the sum has overstated the revenue at risk.
- Requests sent with no idempotency_key cannot carry is_retry = true, so the measured retry share is a lower bound and the write-up must say so.
Follow-up
- Suppose the two live clauses turn out to remove 2.6% of consumption revenue. How much improvement in gross logo retention on an annual-contract base pays that back, and over what horizon does the payback land?
- A retry sent without an idempotency_key cannot be flagged as a retry. Which direction does that bias your estimate of the removable volume, and how can you bound it?
Monthly logo churn triples with no change in satisfaction
Hard · renewal-eligible base · censoring · timestamp semantics
Monthly logo churn, computed as churned accounts divided by all paying accounts, tripled last month. Contracts are annual. From fct_subscription_period (account_id, term_start_date, term_end_date, amendment_type, auto_renew, booked_at, superseded_by_id, is_current) and dim_account (account_id, churned_at, account_status, employee_band, acquisition_channel), rebuild churn on the renewal-eligible base, separate calendar effects from customer behaviour, and state whether retention actually changed. Note that churned_at is sometimes set when the record was updated rather than at term end.
Approach
- Rebuild the denominator as accounts whose term_end_date falls in the month. An account eleven months from renewal sits in the current denominator while being structurally incapable of entering the numerator, so on annual contracts the published rate understates the truth by roughly the reciprocal of the annual renewal fraction and moves with the signing calendar.
- Plot the renewal-eligible base by month across two years. A signing surge twelve months earlier reproduces itself as an eligibility surge now, and a rate whose denominator ignores that tracks the sales calendar rather than customer sentiment.
- Date each churn by term_end_date, never by churned_at or an update timestamp. Inspect the distribution of churned_at minus term_end_date: a backlog cleared in one batch appears as a mass at a single date and shifts losses into whichever month the operations team did its paperwork.
- Apply the 45-day grace for late renewal paperwork so the most recent month and a half is marked not reportable, rather than printing a number that will rise once the paperwork lands.
- Compare corrected gross logo retention against its own trailing distribution, and if a real change survives, cut it by employee_band, acquisition_channel and plan_tier before proposing any cause.
Follow-up
- Some contracts in the window have not reached their renewal date. When does this require a survival estimator rather than a simple rate, and which one would you use?
- How do you report churn to an audience that wants a monthly number when the underlying event is annual and lumpy?
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.
Ambiguous requests are the test
Most of the questions in this section reduce to one thing: can you be handed a vague request and come back with something useful? Prepare an example where the ask was underspecified, you chose an interpretation, and you said out loud which interpretation you chose. Describing how you narrowed the question matters more than the technique you eventually used.
Describe a time you had to troubleshoot an issue in a batch processing pipeline within a cloud environment.
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.
- Close with what you would do differently, concretely.
Follow-up
- How did you know the outcome was caused by your change?
- What would you do differently if you ran that project again?
How do you handle missing or noisy data in a high-stakes, security-focused dataset?
Medium · behavioural and stakeholder questions
Approach
- Name the disagreement or constraint, and how you resolved it with evidence.
- State the situation in two sentences and spend the rest on your reasoning.
- 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?
Allocate one analyst week across three competing requests
Easy · prioritisation · stakeholders · scoping
Three requests arrive the same morning and you have one week. Finance wants per-account gross margin from fct_usage_daily for a pricing review in three weeks. Sales wants a renewal-risk list for accounts with term_end_date inside 60 days. A product manager wants an experiment readout for a decision being taken on Thursday. Produce your allocation with hours attached, what you say to whoever receives less, and one thing you refuse to do this week, with the reason each decision is defensible to the person it costs.
Approach
- The interviewer is probing whether you prioritise on decision timing and reversibility or on who asked most forcefully. Sort by the date each decision is actually taken and by what the default outcome is if nothing arrives.
- Apply that sort concretely. The Thursday readout has a hard irreversible deadline and no value afterwards. The pricing review has three weeks of slack. The renewal list has a rolling deadline set by term_end_date, so part of it is urgent this week and the rest is not, which means it can be split rather than deferred whole.
- Find the cheapest sufficient version of each request rather than the full version. The readout goes in full. The renewal list ships as a filtered query over renewal-eligible accounts ranked by two inspectable signals rather than as a model. The margin work is scoped to the accounts that dominate the pricing decision, since revenue is heavily skewed and the tail will not change the conclusion.
- Make the trade visible in one written note to all three at once, with dates. Telling each person separately that they are the priority is how an allocation becomes a credibility problem.
- Refuse something explicitly and say why. The model version of the renewal list is the usual candidate, because it cannot be evaluated without a holdout nobody has agreed to yet, and building it this week forecloses that.
- Leave slack. A plan with none is a plan to miss the one deadline that cannot move.
Follow-up
- The sales leader escalates to your manager. What did you already do that makes that a short conversation?
- Which of the three deadlines would you push back on, and what exactly would you ask for?
- What would you change about how these requests reach you so next week is not the same?