Experimentation Platforms and Infrastructure Questions
Infrastructure for A/B testing and experimentation: assignment/bucketing, metric pipelines for experiments, guardrail and variance-reduction plumbing, and experiment result storage. Covers building the platform that powers trustworthy online experiments at scale. Distinct from the statistics of experiment analysis.
Your product team reports unexpected metric contamination after several rapid rollouts and overlapping feature flags. Walk through the operational, step-by-step plan you would run to identify, quantify, and mitigate the contamination sources while minimizing disruption to the teams shipping features.
Sample Answer
Direct answer
Run a step-by-step operational response: first quantify the contamination's actual scope (which experiments, which time window, roughly how many users affected), then decide whether affected experiments need to pause, be re-analyzed with an explicit contamination adjustment, or be allowed to continue with the contamination noted as a caveat, and only after containment turn to the longer-term fix (a coordination policy or a traffic-layering change) that prevents the same class of collision from recurring.
Structured elaboration
- Scope the problem first, before acting: pull the actual user-overlap and timing data across the flags and rollouts implicated, rather than reacting to the qualitative complaint alone. Contamination reports are often vaguer than the actual data ("something feels off") and the first job is turning that into a specific, bounded claim.
- Triage by severity: an overlap that's small and unlikely to meaningfully bias either experiment's primary metric can often be left running with a documented caveat; a large, high-confidence contamination affecting a decision that's about to be made should pause the affected experiments immediately.
- Communicate to the teams whose experiments are affected: they need to know before they make a ship decision on data that might be compromised, not after.
- Fix the immediate cause: this is often an unplanned interaction between two independently-launched flags or a rollout schedule collision rather than a single experiment's fault, so the fix may involve coordinating a schedule change with a different team, not just editing one experiment's configuration.
- Address the systemic cause afterward: only once the immediate situation is contained does it make sense to invest in the longer-term prevention, whether that's an automated pre-launch overlap check, a shared calendar of high-traffic-surface launches, or a formal coordination/prioritization policy for a specific contested surface (a busy city, a shared page) where conflicts recur.
Worked example
Two rapid feature-flag rollouts on the same page overlap for four days before anyone notices, and a third team's active experiment shares that page too. Quantifying the actual overlap shows it affects roughly 12% of one experiment's traffic during a 4-day window out of its planned 14-day run. Given the size and duration, the team decides to exclude that 4-day window from the final analysis (using the remaining 10 days, which is still enough for the planned power) rather than pausing and restarting, which would have cost the experiment more total time than simply excluding the contaminated window.
Trade-offs and pitfalls
Reacting to every contamination report by immediately pausing everything involved is the safest but most disruptive option, and overusing it trains teams to under-report ambiguous cases to avoid the disruption; underreacting (dismissing reports without actually quantifying scope) risks shipping a decision based on genuinely compromised data. The resolution is making the FIRST step, quantifying actual scope, fast and cheap enough that a team doesn't have to choose between "ignore it" and "stop everything" before they even know how bad it is.
Design an automated reporting pipeline that computes statistical significance and uncertainty for hundreds of experiments daily and produces a concise 'experiment card' for executives. How do you apply multiple-testing correction before it reaches the card, how do you present uncertainty honestly on a one-page summary, and how do you trade off freshness against compute cost? Include a manual review gate before anything reaches an executive inbox.
Sample Answer
Direct answer
Design the reporting pipeline to compute significance and uncertainty for every experiment daily, apply a multiple-testing correction before anything reaches an executive-facing card, present uncertainty honestly (a range, not a bare point estimate), balance freshness against compute cost by tiering how often different tiers of experiments recompute, and gate anything reaching an executive inbox behind a manual review step, since an unreviewed automated report reaching leadership is a much more costly mistake than the same error reaching an internal analyst dashboard.
Structured elaboration
- Architecture: raw metric computation feeds a scheduled aggregation job that, once a day, applies the org's chosen multiple-testing correction across that day's batch of tests, then a templating layer renders a short, standardized "experiment card" (headline result, confidence range, guardrail status) per experiment.
- Handling multiple-testing correction: since this pipeline is specifically the one aggregating results across many experiments for executive consumption, it's a natural place to enforce the correction consistently, rather than leaving each team's individual dashboard to (inconsistently) decide whether to correct or not.
- Presenting uncertainty honestly: a card shows a range ("we estimate a 2 to 5 percent improvement") rather than a single point number, and explicitly flags when a result is not yet statistically reliable, rather than omitting uncertainty to look cleaner.
- Freshness versus compute cost: recomputing every card in real time for every experiment is expensive at scale; a practical tiering computes high-visibility or high-stakes experiments' cards more frequently (say, hourly) while lower-priority ones refresh once daily, since a stakeholder waiting on a major launch decision has a different freshness need than someone casually checking a long-tail experiment.
- Manual review gate: before a card reaches an executive's inbox (as opposed to sitting in an internal dashboard anyone can browse), a human reviews it, specifically to catch an automated-but-wrong summary (a card that technically computed correctly but tells a misleading story, like showing a "win" the guardrail metrics would contradict) before it reaches an audience less equipped to independently verify.
- Minimizing alert fatigue: the same pipeline, run against hundreds of metrics daily rather than just per-experiment cards, needs its own noise control (scoring how surprising a flagged change is against that metric's own historical variance, not a flat threshold) or the volume of flagged changes trains recipients to skim past all of them, including the real ones.
- A lower-stakes channel for routine updates: not every update needs the weight of an executive card; a lighter-touch automated weekly email to metric owners, summarizing key numbers, significance, and a suggested next step, serves the routine-monitoring need without invoking the full manual-review machinery reserved for genuinely executive-facing summaries.
Worked example
An automated card for a checkout experiment reports "+4% conversion, statistically significant" the same day a guardrail metric (payment failure rate) quietly crossed its own threshold. Without a manual review gate, that card reaches executives showing an unambiguous win while the platform's own guardrail system is flagging a real problem elsewhere; the review step is what catches the mismatch and either holds the card or adds the guardrail context before it ships to that audience.
Trade-offs and pitfalls
A manual review gate on every card doesn't scale if the org runs hundreds of experiments a day; the practical answer is scoping the gate to cards that are actually executive-facing (a small, curated subset) rather than every internal report, and building automated pre-checks (does the guardrail status contradict the primary-metric headline) that flag a card for mandatory review rather than requiring a human to read every single one regardless of risk.
In one or two sentences, explain the multiple comparisons problem for a platform running many experiments and tracking many metrics per experiment. Then list two engineering controls, not statistical corrections, an experimentation platform could implement to reduce false discoveries.
Sample Answer
Direct answer
The multiple comparisons problem is that the more experiments and metrics you test, the more likely at least one shows a "significant" result purely by chance, even if nothing real is happening anywhere. Two engineering controls, beyond a statistical correction, are: capping how many metrics any single experiment can designate as "primary" (rather than letting a team declare a dozen primary metrics and cherry-pick whichever moved), and surfacing, alongside every significant result, how many total tests were run in that same batch or time window, so a viewer has the context to judge how surprising a single "win" really is.
Structured elaboration
Statistical corrections (Bonferroni, Benjamini-Hochberg) adjust the math after the fact; engineering controls change the SHAPE of what gets tested in the first place, which is often a cheaper and more durable fix. Limiting primary-metric count forces a real pre-commitment: a team that has to pick one or two metrics as the ones that decide ship/no-ship, with everything else demoted to secondary or guardrail status, structurally can't quietly cherry-pick a lucky metric from a long list after the fact. Surfacing the total-tests-in-context alongside any single result (something like "this metric was 1 of 40 tested today across the company") gives a reader the honest base rate needed to judge whether a single significant finding is more likely a real effect or an expected false positive given how many things were checked.
Worked example
A team runs an experiment with twelve metrics all nominally labeled "primary." One shows p=0.03. Reported in isolation, that looks like a clean win; reported alongside "twelve primary metrics were tested, and roughly one false positive at this rate is expected by chance alone even with zero real effect," the same number reads very differently, and the platform's job is to make that context impossible to miss rather than something a skeptical reader has to independently know to ask for.
Trade-offs and pitfalls
Capping primary-metric count too aggressively (forcing every team down to exactly one metric) can genuinely hurt teams whose product change legitimately affects two truly independent, equally important outcomes. The workable middle ground is a small cap (two or three) rather than one, paired with the requirement that the primary set be declared before launch, not adjusted afterward once results are in.
Discuss the trade-offs of a centralized experimentation platform, one platform the whole company shares, versus decentralized team-level experimentation where each team runs its own tests. Address velocity, statistical validity, data lineage, and consistency, and as the PM at a fast-growing company, recommend a model and outline how you would transition to it.
Sample Answer
Direct answer
A centralized platform trades some team-level velocity for consistency, statistical validity, and company-wide visibility; decentralized team-level experimentation trades that consistency for speed and autonomy, at real risk of duplicated infrastructure and incomparable results across teams. For a fast-growing company, the pragmatic model is centralized CORE infrastructure (assignment, metric definitions, validity checks) with decentralized OWNERSHIP of individual experiments, rather than choosing either extreme.
Structured elaboration
- Velocity: fully decentralized teams move fastest in isolation, since they're not waiting on a shared platform team's roadmap or approval process; centralized platforms can become a bottleneck if the platform team can't keep up with demand across many teams.
- Statistical validity: centralization makes it far easier to enforce a consistent bar (mandatory pre-registration, standard guardrails, a shared metric catalog); decentralized teams each reinventing their own analysis approach means some will do it well and others poorly, with no consistent floor.
- Data lineage and consistency: a centralized metadata registry and shared metric definitions make company-wide questions ("what's our overall experiment velocity," "which teams' results can we compare directly") answerable; decentralized teams using their own tooling and metric definitions make this nearly impossible to answer with confidence.
- Operational risk: centralization concentrates risk in the shared platform (an outage affects everyone at once); decentralization spreads that risk but multiplies the number of places a critical bug (like a broken SRM check nobody built because each team's homegrown tooling is less mature) could hide.
- The hybrid recommendation: centralize the pieces that benefit most from consistency and are expensive to build well (assignment service, validity engine, metric catalog), and let individual teams own their own experiment configuration, targeting, and analysis within that shared infrastructure, which captures most of centralization's rigor benefits without making the platform team a bottleneck for every individual launch.
- Transition plan: start by centralizing the highest-leverage, hardest-to-build-well piece first (typically the assignment service and validity checks, since a bug there silently corrupts every team's results), then migrate teams' metric definitions into a shared catalog over time rather than requiring an all-at-once cutover.
Worked example
A fast-growing company with 15 product teams each independently built their own A/B testing scripts. Three different teams' "conversion rate" definitions turn out to differ subtly (one includes free trials, one doesn't; one has a 7-day window, another 14), making it impossible to compare experiment impact across teams in a company-wide review, which is exactly the kind of problem centralizing just the metric catalog (without necessarily forcing every team onto identical tooling for everything else) would solve.
Trade-offs and pitfalls
Fully centralizing everything, including individual experiment configuration and analysis, tends to bottleneck on the platform team's capacity as the company keeps growing; fully decentralizing everything, including the metric catalog and validity checks, sacrifices exactly the comparability and rigor that matters most once the company is big enough that leadership wants to compare results across teams. The hybrid model's own risk is scope creep in either direction, letting the "centralized core" quietly expand to cover things that didn't need centralizing, or letting teams quietly build workarounds that erode the shared metric catalog's authority.
Implement a deterministic hashing-based assignment function in Python: given a user id, an experiment id, and variant allocation percentages, it should return the same variant every time for the same user, support namespace isolation between experiments, allow an explicit forced override to a bucket, and remain uniform across a very large space of user ids. Handle missing or malformed ids explicitly.
Sample Answer
Direct answer
Hash the concatenation of a namespace, the experiment id, and the user id with a stable, well-distributed hash function, map the result into [0, 1), and compare against cumulative allocation thresholds; namespace isolation comes from including the namespace in the hash input, and an explicit override simply bypasses the hash entirely when a forced bucket is supplied.
Approach and code
import hashlib
def assign_variant(user_id, experiment_id, variant_allocations,
namespace="default", force_bucket=None):
if force_bucket is not None:
if force_bucket not in variant_allocations:
raise ValueError(f"force_bucket {force_bucket!r} not in variant_allocations")
return force_bucket
if user_id is None or not isinstance(user_id, str) or not user_id.strip():
raise ValueError("user_id must be a non-empty string; missing or malformed ids must not silently bucket")
total = sum(variant_allocations.values())
if abs(total - 1.0) > 1e-6:
raise ValueError(f"allocations must sum to 1.0, got {total}")
key = f"{namespace}:{experiment_id}:{user_id}".encode("utf-8")
digest = hashlib.sha256(key).hexdigest()
bucket_int = int(digest[:12], 16)
bucket_float = bucket_int / float(16 ** 12)
cumulative = 0.0
for variant, pct in variant_allocations.items():
cumulative += pct
if bucket_float < cumulative:
return variant
return list(variant_allocations.keys())[-1] # floating-point edge case fallback
Key points
Including the namespace AND the experiment_id in the hash key, rather than just the user_id, is what gives namespace isolation: the same user hashed against two different experiment ids produces effectively independent, uncorrelated bucket outcomes, which is exactly what lets two unrelated experiments run on the same population without one experiment's assignment leaking information about the other's. Using a wide, cryptographic-quality hash (SHA-256) rather than a narrow one avoids clustering artifacts that a weaker hash could introduce at high user cardinality. The explicit override checks membership in variant_allocations before returning, so a typo in a forced bucket name fails loudly rather than silently assigning an invalid variant. The id check now explicitly rejects both MISSING ids (None, empty string) and MALFORMED ones (a non-string type such as an int or list, or a string that is only whitespace), rather than only catching plain falsy values.
Complexity and edge cases
Complexity is O(1) per assignment (a fixed-size hash plus a linear scan over the variant list, which is small and constant). Edge cases handled explicitly: an empty, missing, whitespace-only, or wrong-type user_id raises rather than silently bucketing a null or garbage identity; allocations that don't sum to 1.0 (a configuration bug) raise immediately rather than producing a subtly wrong split; a floating-point rounding edge case where cumulative allocation lands just under 1.0 due to float imprecision falls back to the last variant rather than raising an unhandled error.
I verified this by re-running four checks: determinism (the same user and experiment always returns the same variant across repeated calls: confirmed), namespace isolation (a cross-experiment match rate of 51.3% over 2,000 users, consistent with independence rather than correlation: confirmed), uniformity at high cardinality (49.63% landed in the 50% control bucket over 20,000 users: confirmed), and a forced override correctly bypassing the hash and rejecting an invalid forced bucket name (confirmed). I additionally verified the malformed/missing-id guard against None, empty string, whitespace-only string, an int, a list, and an empty dict: all six correctly raised ValueError instead of silently hashing.
Trade-offs and pitfalls
A narrower or weaker hash function (a simple modulo of a small hash) can produce visible clustering artifacts at scale, where certain user-id patterns systematically land in the same bucket; SHA-256 is deliberately heavier than strictly necessary for this use case specifically to avoid that risk. The other common implementation mistake is hashing user_id alone without folding in the experiment_id or namespace, which would correlate a user's assignment across every experiment on the platform, defeating the independence that makes concurrent experimentation on overlapping traffic valid. A stricter type check (rejecting anything that isn't a string) is a deliberate trade-off: it's less permissive than silently coercing an int-like id to a string, but coercion risks masking an upstream bug where the wrong kind of identifier is being passed in.
Unlock Full Question Bank
Get access to all Experimentation Platforms and Infrastructure interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.