As a Data Scientist at Outfit7, you sit at the intersection of creative game design and rigorous analytical engineering. Your work directly impacts the experiences of millions of global users by driving data-informed decisions that shape the mechanics, monetization, and engagement strategies of our world-renowned mobile gaming portfolio.
You will tackle complex challenges involving massive datasets, ranging from user behavior modeling to predictive analytics for game balance. This role is not just about crunching numbers; it is about translating abstract data patterns into actionable insights that help our teams iterate on products and optimize the player journey. If you thrive in an environment where technical precision meets high-scale consumer entertainment, this role offers a significant opportunity to influence the future of our digital franchises.
Initial Screening
reportedMost candidates lose this call inside the first two minutes, during the walkthrough of their own background. The account runs chronologically, sits at the level of tools and titles, and never arrives at a decision anyone could have disagreed with. Anchor on a problem instead of a timeline: what the team could not answer, what you did about it, what happened next. Ninety seconds is enough, and stopping on time leaves room for the half of the call that belongs to you. What you ask about how work gets prioritised signals your level more reliably than the walkthrough does.
What to demonstrate
- Whether your background summary has a shape (problem, decision, consequence) or is a chronological list of tools and employers
- Whether you can account for gaps, short stints and the reason you are looking, unprompted and without hedging
- The substance of the questions you ask back, which an experienced screener reads as a level signal
How to prepare
- Time your opening walkthrough against a clock. If it runs past two minutes, compress the earliest role into a single clause and spend the recovered time on the most recent one
- Write one honest sentence for every gap or short stint visible on your resume and offer it before being asked about it
- Prepare questions about how work arrives and gets prioritised: who writes the request, how often priorities change, and what happens to an analysis after it is delivered
Take-Home Test
reportedThe clock is part of the test. Three to six hours is not enough to do everything the dataset supports, so the submission mostly reveals how you spend a fixed budget against an open question. A reviewer sees which paths you took and, by absence, which you abandoned. Work that runs out of time inside the analysis ships a thin conclusion, while work that cuts scope early protects the last hour for writing. The most reliable way to lose here is to leave the scoping decision implicit, so it reads as something you missed rather than something you chose.
What to demonstrate
- Whether the scope you settled on is presented as a decision with a reason, rather than left for the reader to infer from what is missing
- Whether the depth of the work is consistent with the stated time budget, instead of several half-finished directions left open
- Whether the closing section reads as something written on purpose rather than assembled from whichever cells survived
How to prepare
- Run a timed rehearsal on a public dataset with a hard stop, holding the final sixty minutes for writing no matter where the analysis has got to
- Before opening the data, list the questions it could plausibly answer, pick one, and keep the discarded ones as a short note on what you did not attempt and why
- Commit a one-line finding after each analysis step so the writeup is assembled from recorded results rather than from memory at midnight
Technical Discussions
reportedA 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
Final Team Meetings
reportedWhere a loop ends with a senior leader, that conversation is rarely another skills test. The technical signal already exists by then, so the questions tend to open up: what you would look at first, where a metric you have heard about could mislead, what you would push back on. The decision being made is scope, which in practice means level and how much you would be trusted to own unsupervised. Treating it as a formality is the usual mistake. An open question late in the day is still being scored, and a vague answer reads as someone who has not run anything themselves.
What to demonstrate
- Whether your view of the business has anything specific behind it, given that you are working only from what is public and are expected to say so
- Whether the scope of work you describe owning matches the scope of the role, instead of sitting a level below it
- Whether you can disagree with something concrete and stay useful about it, rather than agreeing with everything said in the room
- Whether your questions are ones only this person could answer, as opposed to ones the recruiter already covered
How to prepare
- Build one view you could defend for two minutes using only public information: what the funnel probably looks like, which metric likely drives decisions, and where that metric could mislead. Being wrong for a stated reason survives this round; having no view does not
- Write down the largest piece of work you have owned from question to decision, who else touched it, and what you decided alone, then check that it reads at the level you are interviewing for
- Prepare one thing you would want changed if you joined and phrase it as a question rather than a verdict, so it opens a conversation instead of closing one
PracHub editorial advice for the preparation topics above.
Comparing retention or spend across progression stages reached.
Reaching level 20 requires having survived long enough to reach level 20, so grouping by max level attained conditions on the outcome you are trying to explain. Every correlate of playing longer then looks causal, and 'players who join a guild retain better' is the canonical version of this error. The defensible framing fixes a cohort at a common age or a common exposure point and measures forward from there, or models the hazard with progression as a time-varying covariate.
Treating the account as the person, and the calendar week as neutral time.
Reinstalls, multi-platform play and shared accounts split or merge humans relative to player_id, which inflates install counts and deflates retention denominators in ways that correlate with platform and campaign. Meanwhile daily actives spike on release day and decay over the following week, so a week-over-week read aliases the content calendar. Anchor comparisons to time since release or to matched days in the release cycle, and report identity-sensitive metrics with an explicit statement of which identity key was used.
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.
Naming a model class before naming the deployment constraints
Set out the latency budget, the label delay, the retraining cadence, the interpretability requirement and the number of labelled examples, then pick the model that fits them. A boosted-tree answer to a problem where each decision must be explained to the affected user is a well-executed answer to the wrong question.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
What statistical methods would you use to determine if a change in gam…
What statistical methods would you use to determine if a change in game difficulty is statistically significant?
Approach
- Sanity-check the answer against a simple bound or a simulated case.
- Write down the assumption the method needs before you use the method.
- Translate the result into the decision it informs, in one plain sentence.
Follow-up
- How would you explain this result to someone who does not know statistics?
- What sample size would you need to detect an effect half this size?
Explain the difference between correlation and causation in the contex…
Explain the difference between correlation and causation in the context of user churn.
Approach
- Translate the result into the decision it informs, in one plain sentence.
- Quantify uncertainty explicitly rather than reporting a point estimate alone.
- Write down the assumption the method needs before you use the method.
Follow-up
- How would you explain this result to someone who does not know statistics?
- What sample size would you need to detect an effect half this size?
How do you approach debugging a model that is producing unexpected res…
How do you approach debugging a model that is producing unexpected results?
Approach
- Pick an evaluation metric that matches the cost of each error type, not a default.
- Check what information would not exist at prediction time, and exclude it.
- Frame the prediction: the label, the moment of prediction, and the action it triggers.
Follow-up
- Where could label leakage enter this setup?
- What would you monitor after launch to know the model is still valid?
Sink to faucet ratio per currency from a signed ledger
You are given ledger, a pandas DataFrame with one row per signed currency movement: ledger_id, player_id, occurred_at (UTC timestamp), currency_code, delta_amount (signed int64), flow_type in {faucet, sink, transfer, correction}, source_system, is_reversal (bool). Implement sink_to_faucet(ledger) returning one row per (currency_code, week_start) with faucet_units, sink_units and ratio = sink_units / faucet_units. Sink units are reported as positive magnitudes. Rows with flow_type in {transfer, correction} and rows with is_reversal true are excluded from both sides. A currency-week with zero faucet volume must return a null ratio rather than raise or return infinity.
Approach
- Filter first, aggregate second: build one boolean mask for flow_type in ('faucet','sink') and is_reversal == False, apply it once, so the exclusion cannot be applied to one side of the ratio and forgotten on the other.
- Derive week_start by flooring occurred_at to the Monday of its ISO week: occurred_at.dt.normalize() minus pd.to_timedelta(occurred_at.dt.weekday, unit='D'). Avoid dt.isocalendar().week on its own, which drops the year and collapses week 1 of two different years into one bucket.
- Split the aggregate by sign rather than by abs(): faucet_units = sum of delta_amount where flow_type == 'faucet', sink_units = sum of -delta_amount where flow_type == 'sink'. Taking abs() of the whole column hides a faucet row carrying a negative delta, which is a real data bug you want surfaced.
- Pivot to one row per (currency_code, week_start) and divide under a guard so faucet_units == 0 yields NaN, not inf; downstream charts silently drop NaN and silently rescale on inf.
- Run the same aggregate once with the is_reversal filter removed and diff the two results. Any currency whose ratio moves materially is telling you that clawbacks or customer-service corrections are being read as genuine economy flow.
Worked solution 20 min
- Apply the single inclusion mask and assert that the filtered frame contains only the two flow types you intend.
- Add the week_start column by weekday subtraction and confirm every value is a Monday at 00:00 UTC.
- Aggregate faucet and sink magnitudes separately with a groupby on (currency_code, week_start), then join the two results on that key with an outer join so a currency-week with sinks but no faucets survives.
- Divide under a where() guard and return the frame sorted by currency_code then week_start.
Follow-up
- The definition drops the reversal row but leaves the original entry it undid inside the aggregate. Defend that choice or change it, and say which direction the ratio moves either way.
- The soft currency has held a ratio near 0.8 for six weeks. What do you look at next, and what evidence would let you say the economy is nevertheless fine?
- How would you report this when a single live-ops event contributed 60 percent of the week's faucet volume?
How do you ensure your code is scalable enough to handle millions of d…
How do you ensure your code is scalable enough to handle millions of daily active users?
Approach
- 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.
- 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 would you verify this result without re-running the same query?
Describe your process for optimizing a slow-running data pipeline.
Describe your process for optimizing a slow-running data pipeline.
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.
- 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?
What libraries or tools do you prefer for data manipulation and visual…
What libraries or tools do you prefer for data manipulation and visualization, and why?
Approach
- Compute rates by summing numerator and denominator separately, never by averaging rates.
- Check whether any join is one-to-many before aggregating, or the sums inflate.
- Handle the rows that do not match: a LEFT JOIN with a NULL check is usually the question.
Follow-up
- What breaks if events arrive late or out of order?
- How would you verify this result without re-running the same query?
Stitch crash-fragmented sessions into play blocks with gaps and islands
fct_session has session_id, player_id, session_start_ts, session_end_ts (NULL when the client crashed), duration_seconds (NULL alongside it), platform and ended_reason. A crash splits one sitting into two rows seconds apart. Group consecutive sessions for the same player and platform into play blocks, starting a new block when the gap from the previous end to the next start is 120 seconds or more. For NULL ends, impute session_start_ts plus that player's median duration_seconds on the same platform. Return one row per block.
Approach
- Build the imputed end in its own CTE: COALESCE(session_end_ts, session_start_ts + the median duration as an interval). Compute the median with PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY duration_seconds) grouped by player_id and platform over the non-NULL rows, then join it back. Players with no non-NULL session have no median, so decide explicitly whether to drop them and report how many.
- Get the previous end with LAG(imputed_end) OVER (PARTITION BY player_id, platform ORDER BY session_start_ts, session_id). The session_id tiebreak matters because two sessions can share a start timestamp on a device that relaunched instantly.
- Flag a block boundary where prev_end IS NULL (the first session in the partition) OR session_start_ts - prev_end >= interval '120 seconds', and cast that flag to an integer.
- Turn flags into block ids with SUM(flag) OVER (PARTITION BY player_id, platform ORDER BY session_start_ts, session_id ROWS UNBOUNDED PRECEDING). This is the gaps-and-islands step. The ROWS frame here is explicit, not load-bearing: session_id is unique, so every row is its own peer group and the default RANGE frame would produce the same block ids. What actually keeps two sittings apart is the session_id tiebreak, which is why it has to appear in all three window clauses.
- Drop that tiebreak and the ordering stops being total, which breaks the step in one of two ways depending on the frame. Under the default RANGE, sessions sharing a start timestamp are peers, so each receives the tie group's closing cumulative total, they share a block id, and two genuinely separate sittings merge. Under ROWS they get distinct ids, but the order inside the tie is unspecified, so which session opens the block, and the LAG that fed the boundary flag, can differ between runs on identical data.
- Aggregate by (player_id, platform, block_id) for MIN(session_start_ts), MAX(imputed_end), COUNT(*), the summed in-session seconds and BOOL_OR(ended_reason = 'crash') to mark blocks stitched across a crash.
- State the direction of the imputation bias: if the imputed end lands earlier than the true end, the gap is overstated and the query under-stitches, so the crash-stitch rate under this rule is a lower bound.
Worked solution 35 min
- Compute per-(player_id, platform) median duration_seconds over rows where it is NOT NULL, and count how many players have no median at all.
- Build the imputed_end CTE and confirm no NULL ends remain among the rows you kept.
- Add LAG, the boundary flag and the cumulative-sum block id in one CTE, all three carrying the same PARTITION BY (player_id, platform) and the same ORDER BY (session_start_ts, session_id). If the three orderings disagree, the flags and the ids are computed over different sequences and the block boundaries land on the wrong rows.
- Aggregate to block grain and spot-check a player known to have crashed: their two adjacent rows should share one block id.
- Recompute with the threshold at 30 and at 600 seconds and watch the block count move. A threshold that changes nothing means crashes are not in fact producing adjacent rows.
Follow-up
- The 120-second threshold is an assertion. How would you pick it from the distribution of inter-session gaps instead?
- Under your imputation, does the block count over-count or under-count real sittings, and which direction would you rather err in for a crash-impact readout?
- A player is on two platforms at once. Should that be one block or two, and what does your partition key currently decide for you?
How do you prioritize your tasks when working on multiple high-impact …
How do you prioritize your tasks when working on multiple high-impact projects simultaneously?
Approach
- Name one primary metric, then the guardrail that stops it being gamed.
- Restate the decision this analysis has to support, and who acts on the answer.
- 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?
- How would you detect that the metric is being gamed rather than genuinely improving?
Given a specific dataset, how would you structure a script to extract …
Given a specific dataset, how would you structure a script to extract actionable engagement metrics?
Approach
- State what result would change your recommendation, so the answer is falsifiable.
- Restate the decision this analysis has to support, and who acts on the answer.
- Decompose the metric into the rates that drive it, and say which one you would check first.
Follow-up
- Which segment would you cut first, and what would that rule out?
- What would you do if the primary metric and the guardrail moved in opposite directions?
How would you approach A/B testing for a new feature in one of our gam…
How would you approach A/B testing for a new feature in one of our games?
Approach
- Decide the analysis before seeing data, including how long it runs and when you look.
- Say whether units interfere with each other, and switch design if they do.
- State the primary metric and the minimum effect worth shipping, then size the test.
Follow-up
- What would you conclude if the result is positive but the test is underpowered?
- What would you do if you could not randomise at all?
Build the scorecard for a season pass without overstating young cohorts
A season pass (item_class = 'battle_pass') sells for real money and pays premium currency back across its tiers. Two weeks after launch, a deck reports gross bookings per install for the launch-week cohort against last quarter's cohort and claims a 40% lift. Using fct_iap_transaction (price_usd_gross, platform_fee_usd, price_usd_net, status, refund_ts, purchase_ts), fct_currency_ledger and dim_player, rewrite the scorecard: give the primary metric with its maturity rule, two guardrails, and the reason the reported lift is not evidence.
Approach
- Kill the comparison first. A two-week-old cohort has two weeks of purchases and almost no refund exposure, while last quarter's cohort has a full quarter of both. Gross bookings also ignores platform_fee_usd, which is a percentage haircut that differs by store_channel. The two numbers are not one quantity measured twice, so their ratio means nothing.
- Primary: net revenue per install at a fixed cohort age of 90 days, computed only for install cohorts that have reached that age. Fixing the age fixes refund exposure as well: a reversed purchase leaves status = 'completed' whenever the reversal lands, so a cohort's net revenue keeps falling for weeks after purchase and only equal-age reads compare equal amounts of that decay. The launch cohort has no primary number yet, and saying so plainly is the answer rather than a dodge.
- Give the team something honest to read at two weeks: net revenue per install at 14 days of cohort age, compared only against the 14-day value of earlier cohorts. Fixed-age against fixed-age, never as-of-today against as-of-today.
- Guardrail one: refund and chargeback rate at 60-day maturity on pass purchases, restricted to purchase-date cohorts at least 60 days old. This is precisely the exposure the two-week read is structurally blind to.
- Guardrail two: premium-currency faucet volume with source_system = 'pass_tier', reported next to the premium sink-to-faucet ratio. A pass that pays back most of its price in currency is partly a discount on future purchases, and that cost lands in the economy rather than on the pass's own revenue line.
- Decompose so any movement is attributable: net revenue per install equals paying conversion multiplied by net revenue per payer. State which factor moved before recommending anything, because the two imply opposite follow-ups.
Worked solution 30 min
- Write the net revenue definition once, in full: sum of price_usd_net over rows with purchase_ts inside the cohort window and status = 'completed'. There is no second refund term. status is current state, so a reversed purchase has already left that filter, and subtracting it as well deducts the refund twice. If your warehouse instead freezes status at purchase time, the reversal term keyed on refund_ts is required, so confirm which column carries the current state before writing the SQL.
- Write the maturity rule: fixed cohort age, with younger cohorts excluded rather than extrapolated or annualised.
- Build the fixed-age comparison table, every cohort at 14, 60 and 90 days, so a young cohort shows a blank cell rather than a misleading one.
- Add both guardrails with their windows and their cohort restrictions.
- Write the decomposition identity and the rule for which factor you report first.
Follow-up
- The pass pays back 900 premium currency against a 1,000-currency equivalent price. What does that do to next quarter's currency-pack revenue, and which metric in your scorecard would show it first?
- Last quarter's cohort is now 100 days old. Which of its numbers can you compare against the launch cohort today, and which cannot be compared at all?
Sessions per player dropped the week a client version shipped
Sessions per active player fell 31% the week a client version rolled out, while matches per active player and total play seconds are flat. fct_session has session_id, player_id, session_start_ts, session_end_ts, duration_seconds, app_version, matches_started, ended_reason. A growth lead has filed this as an engagement regression. Determine whether player behaviour changed or the session boundary did, using only these columns, and state the evidence that would settle it either way.
Approach
- Start from the conservation check that is already in the prompt: if matches per active player and total play seconds are both flat while session count fell, the same activity is being partitioned into fewer containers. Genuine disengagement would move at least one of those two.
- Verify the implied arithmetic. If sessions per player fell 31%, matches per session must have risen by 1/0.69 minus 1, about 44.9%, for matches per player to stay flat. Compute it. A match that lands close to that figure is strong evidence of re-partitioning rather than behaviour.
- Split the same calendar day by app_version. Holding the date fixed removes weekday, release-calendar and server-side effects, so a step difference between versions on one day localises the change to the client. Note the caveat aloud: version adoption is not random, since fast updaters differ from slow ones, so this is corroboration, not proof.
- Get mechanism evidence from the gap distribution. For each player, compute session_start_ts minus the lagged session_end_ts, and histogram those gaps by app_version. If the client's idle timeout lengthened, the old version has mass in the band between the old and new timeouts and the new version has none, because those gaps now sit inside a single session. The cut point names the new timeout.
- Handle the null trap explicitly: duration_seconds is null whenever session_end_ts is null, which is what a crashed client leaves behind. An average over duration_seconds silently drops those rows, so report the null rate by app_version and by ended_reason alongside any duration statistic.
- Deliver the verdict with a restatement plan: which historical series must be recomputed on a consistent boundary, and which metrics, such as matches per active player, were never affected and can be compared across the change.
Follow-up
- Which downstream metrics in this product are defined on sessions rather than on players or matches, and what does each of them now mis-state?
- How would you backfill a comparable series across the boundary change, and what would you refuse to backfill?
- If ended_reason='unknown' also rose on the new version, does that strengthen or weaken your conclusion?
For someone who can already write the query and train the model but stalls when asked what to measure or whether a change is worth making. Metric definition and case structure come first; the technical work is kept as maintenance rather than the centre of the week.
Prepare, practise & reflect
One practical outcome each day. Spend longer where you need it.
0 / 7 done01Metric anatomy
- For three products you use daily, write one primary metric, two input metrics that plausibly move it, and one guardrail that would catch a cheap way of moving the primary at the cost of the product.
- For one of them, specify the metric precisely enough that two analysts would return the same number: numerator, denominator, unit of observation, time window, and how returning and deleted accounts are treated.
- Pick a ratio metric and write what happens to it when the denominator shrinks for reasons unrelated to the numerator, with a concrete example of that happening.
Deliverable: A one-page metric tree for one product, with the primary metric written as an unambiguous spec.
Practice prompt ↗Practice prompt ↗Practice prompt ↗Worked solution ↗02Diagnosing a drop without guessing
- Take the prompt "weekly active users fell 8 percent week over week" and write the segmentation plan before proposing any cause: platform, region, tenure cohort, acquisition channel, and whether the movement sits in the numerator or in a changed denominator.
- List the instrumentation failures that manufacture fake drops (a client release that stopped firing an event, a bot filter change, a shifted date boundary or timezone) and write the query that rules out each one.
- Rehearse stating the boring explanations first, seasonality and day-of-week composition, before reaching for a product cause.
Deliverable: A drop-diagnosis checklist short enough to recite from memory in under a minute.
Practice prompt ↗Practice prompt ↗Practice prompt ↗03Should we build it
- Take a feature idea and write it as a bet: what you believe is true, what would have to be true for it to pay off, the metric that would confirm it, and the effect size that would justify the engineering cost.
- Size the opportunity top-down and bottom-up, then reconcile the two numbers in writing instead of quoting whichever is friendlier.
- Write the counter-metric that would make you kill the feature even if it wins on the primary metric.
Deliverable: A one-page product memo ending in a decision rather than a list of considerations.
Practice prompt ↗Practice prompt ↗04The places aggregate numbers lie
- Construct a Simpson's paradox numerically: two segments where the treatment wins within each segment yet loses overall, and identify the shift in segment weights that causes it.
- Take a heavy right-tailed quantity such as revenue per user and write why the mean is the wrong summary, which percentile you would report instead, and what a moving mean with a stable median tells you.
- Write your definition of a session for the product from day one, then name two real behaviours it misclassifies.
Deliverable: One page holding a worked Simpson's paradox table and a session definition with its two known failure cases.
Practice prompt ↗Practice prompt ↗Worked solution ↗05Technical maintenance, aimed at metrics
- Solve four timed SQL prompts that all end in a ratio metric, so the question of grain stays live in every answer.
- Compute a 95 percent confidence interval for a proportion on a small sample, and state why the normal approximation is unreliable when either np or n(1 minus p) falls below roughly 10, along with which interval you would use instead.
- Take one metric from your day-one tree, write the query that computes it correctly, then write the query that computes it wrong in the most plausible way and explain how you would notice.
Deliverable: Four solved prompts plus a matched correct and plausible-wrong query for one metric.
Practice prompt ↗Practice prompt ↗06Turning engineering work into data science stories
- Write three project stories as situation, decision, trade-off, outcome, each carrying one number and one thing you got wrong.
- For the story you will lead with, prepare an answer to "what would you do differently" that names a decision you made, not a constraint you were handed.
- Practise the sentence that reframes a systems project as a question project: the question the work answered, ahead of the pipeline it shipped.
Deliverable: Three written stories with the lead story delivered aloud and timed under four minutes.
Practice prompt ↗Practice prompt ↗07Mock case and gap list
- Run a 40-minute mock case with someone playing a product manager who pushes back on your metric choice, and record it.
- Listen back and mark every moment you proposed a solution before the success metric existed.
- Rewrite those moments as the question you should have asked, and rehearse the first 90 seconds of the case until scoping comes before solving.
Deliverable: A recorded case plus a rewritten opening 90 seconds.
Practice prompt ↗Practice prompt ↗Worked solution ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
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 time you had to explain a complex technical insight to a no…
Describe a time you had to explain a complex technical insight to a non-technical stakeholder.
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
- 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 escalated requests
Three requests land in the same week and you have five working days. The economy team wants a sink-to-faucet audit per currency after a faucet change shipped ten days ago. Live-ops wants a readout on an event that ends Friday, because the next event is configured from it. Acquisition wants 90-day net revenue per install by channel for a budget meeting in three weeks, and two of the channels launched six weeks ago. All three owners have escalated. Give your allocation, the reasoning you give each owner, and what you refuse or defer.
Approach
- Sort by decision deadline and by reversibility rather than by escalation volume. The event readout is perishable because the population and the live-ops configuration that produced it stop existing on Friday and the next event's config depends on it. The budget meeting is three weeks out. The economy audit has no external deadline but a compounding cost.
- Kill the part that cannot be done correctly at any effort level, and kill it in a ten-minute conversation rather than four days of work. Net revenue per install at 90 days requires cohorts that have reached 90 days of maturity; channels that launched six weeks ago have not, and extrapolating them produces a number that will slope with cohort age. The honest deliverable is matured channels only, with the immature ones listed as excluded and dated for when they qualify.
- Split the economy request into the decision-relevant core and the rest. One day gets the sink-to-faucet ratio per currency_code for the weeks before and after the faucet change, with reversals, transfers and cs_grant excluded, plus the balance percentile curve. A ratio below 1 sustained means balances are accumulating and premium shortcuts will stop selling, which is worth knowing this week. The full per-source audit can wait.
- Give the event readout the largest block, because it is the one with a hard expiry and a downstream configuration decision. Scope it to a decision memo, not a dashboard.
- Publish the allocation in one place with a one-line reason per item, so any escalation argues with the reasoning rather than with you, and the owners can see each other's deadlines.
- Hold back roughly one day. Something breaks most weeks, and an allocation with no slack fails in a way that damages all three commitments instead of one.
Follow-up
- The acquisition owner says a rough number is better than nothing for a budget meeting. What exactly do you give them?
- How would you decide whether the economy audit is genuinely urgent rather than merely important?
- Two weeks of this pattern in a row. What structural change do you propose, and to whom?
Disagree with a queue-tolerance change using guardrail data
A product manager proposes widening matchmaking skill tolerance in one region because p95 matchmaking wait there is 71 seconds against 34 seconds elsewhere. You have fct_match_participant with matchmaking_wait_seconds, skill_rating_before, result, disconnected, server_region and game_mode. Your concern is that the wait percentile is computed only over matches that actually formed, and that wider tolerance raises mismatch and abandon rate. The PM has a deadline and reads your concern as blocking. Make the disagreement in a way that keeps the decision moving, and state the evidence that would change your mind.
Approach
- Agree on the problem in the first sentence. The wait gap is real and worth fixing; the disagreement is about the price of this particular fix, which is a far easier conversation than one that sounds like a veto.
- Name the measurement defect concretely rather than generally. p95 matchmaking wait is an order statistic over matches that formed, so players who gave up in queue are absent from it entirely. A queue that fails outright reports an excellent p95. Pair it with a queue-abandonment rate measured on queue entries, and with match abandon rate defined over result = 'abandon' or disconnected = true.
- Bring a quantity instead of an objection. From current data, bucket matches by absolute skill gap between teams using skill_rating_before and plot abandon rate and disconnect rate per bucket, cut by game_mode and server_region. That converts 'mismatch is bad' into a rate per unit of tolerance, which the PM can trade against seconds of wait.
- Propose the design that settles it, and explain why the obvious one is invalid: queue population is shared, so a treated player is matched against a control player and the treatment leaks across arms. Randomise by region and mode cluster, or switchback on time slices with a burn-in long enough for the queue to clear, and analyse at the randomisation unit.
- State your falsifier explicitly, for example that if abandon rate is flat across the top two skill-gap buckets in this region, you withdraw the objection.
- Give the PM something shippable inside the deadline, such as a bounded tolerance increase in the worst hours only, with the guardrail thresholds pre-registered before launch.
Follow-up
- How long a burn-in would you use for a switchback here, and what in the data tells you it is long enough?
- Queue-abandonment is not in the tables listed. What would you instrument, and what would you do for this decision in the meantime?
- The PM ships it globally without a test. What do you measure afterwards, and what can that measurement honestly support?
- 01
Describe a time you had to explain a complex technical insight to a non-technical stakeholder.
- 02
Three requests land in the same week and you have five working days. The economy team wants a sink-to-faucet audit per currency after a faucet change shipped ten days ago. Live-ops wants a readout on an event that ends Friday, because the next event is configured from it. Acquisition wants 90-day net revenue per install by channel for a budget meeting in three weeks, and two of the channels launched six weeks ago. All three owners have escalated. Give your allocation, the reasoning you give each owner, and what you refuse or defer.
- 03
A product manager proposes widening matchmaking skill tolerance in one region because p95 matchmaking wait there is 71 seconds against 34 seconds elsewhere. You have fct_match_participant with matchmaking_wait_seconds, skill_rating_before, result, disconnected, server_region and game_mode. Your concern is that the wait percentile is computed only over matches that actually formed, and that wider tolerance raises mismatch and abandon rate. The PM has a deadline and reads your concern as blocking. Make the disagreement in a way that keeps the decision moving, and state the evidence that would change your mind.
Is this an official Outfit7 interview guide?
No. It is PracHub's own research and practice material for the Data Scientist role at Outfit7. Rounds and questions reflect what candidates have reported, not a process Outfit7 has published, and they change over time. Confirm the current format and scope with your recruiter.
PracHub interview research ↗How difficult is the interview process?
The difficulty is generally considered average, though it requires significant preparation. We focus on fair, realistic assessments rather than "gotcha" questions.
PracHub interview research ↗How long does the entire process take?
While it varies, you should expect the process to span several weeks from the initial screening to the final offer, including time for the expertise test.
PracHub interview research ↗What differentiates successful candidates?
Successful candidates are those who can explain the "why" behind their technical decisions and show a genuine interest in how their work influences the player experience.
PracHub interview research ↗Will I receive feedback if I am not selected?
Yes, we strive to provide constructive feedback to candidates who have reached the technical interview stages, as we believe in the value of the learning process.
PracHub interview research ↗Sources & methodology 3 sources ↗
No official company page is cited. Rounds and questions come from candidate reports and PracHub editorial material; each source shows the date it was read.
- 01PracHub interview research ↗
PracHub editorial research into this company and role, maintained with this guide. Candidate-reported, not an employer publication.
PracHub page · Accessed 2026-09-22 - 02PracHub Data Scientist practice ↗
Cross-company practice questions for this role.
PracHub page · Accessed 2026-09-22 - 03PracHub interview preparation framework ↗
The framework the preparation plan follows.
PracHub page · Accessed 2026-09-22