End-to-End ML System Design Questions
Designing a complete machine learning system from problem to production. Covers the components and architecture of a production ML system, data flow from ingestion to serving, scalability, and integration of models into a larger product. Emphasizes the whole-system design tradeoffs that appear in ML system-design interviews.
You need to serve a very large model at low latency and high availability across multiple regions. Walk through the major architectural decisions, and how they would change if you had to serve several such models behind a single request instead of one.
Sample Answer
Direct answer
Serving a very large model at low latency across regions is fundamentally a placement and replication problem: decide where model replicas live, how requests find the nearest healthy one, and how much the model itself has to shrink so a single region's inference fits the latency budget, then replicate that whole unit per region rather than routing every request to one "home" region. When a single request needs several models instead of one, the same placement decisions still apply to each model individually, but the design additionally has to decide whether those models run in parallel or in a sequence inside one request, how they share the latency budget, and how they are isolated from each other so one model's slowdown does not take the whole request down.
Structured elaboration
1. Fitting the model into the latency budget. In order of how invasive they are: quantization (representing weights with fewer bits, for example int8 instead of float32) and pruning (removing low-impact weights) both shrink a model with a small, bounded accuracy cost; distillation (training a materially smaller student model to imitate the large one) goes further. Model or tensor parallelism, splitting one model's layers or weight tensors across multiple accelerators, is a fit lever, not a latency lever: it lets a model that does not fit on one device serve at all, but the network hop between shards usually adds latency versus a model that fits on a single device, so it is reached for only when the model genuinely does not fit on one accelerator, not as a default optimization.
2. Regional placement and routing. Deploy the same, already-shrunk model as an independent, fully-loaded replica set in each region rather than routing distant users back to one region; a single cross-region network hop can cost tens of milliseconds before the model even runs, which can consume the entire latency budget by itself. Route with a latency-aware global load balancer that health-checks every region and fails over automatically, and keep warm standby capacity in at least one other region so a regional outage does not force a cold start under load. Replicate the versioned model artifact, not live state, from one registry to every region, so behavior is identical everywhere; a regional accuracy difference is its own outage.
3. Keeping replicas fast. Pre-warmed instances so no model-load path sits in the request's critical path, request batching where traffic allows a few milliseconds of queuing in exchange for accelerator throughput, and colocating the feature or context fetch with the model so a network round trip is not added on the hot path.
4. Extending to several models behind one request. Orchestration shape: fan-out (parallel calls to independent models, where the budget is the slowest single model) versus a pipeline (model A's output feeds model B, where the budget is the sum of both). Fan-out is strongly preferred whenever the models have no genuine data dependency, because it turns several latencies into their maximum rather than their sum. Isolation: run each model in its own resource pool with its own autoscaling and quota, the same principle as multi-tenant isolation on shared large language model (LLM) infrastructure, where each tenant, or here each model, gets hard resource and rate limits rather than best-effort sharing, so one model's traffic spike cannot starve another sharing the same request. Graceful degradation: define a fallback per sub-model (a cached prior result, a simpler heuristic, or omitting that model's contribution) so one slow or failed model degrades the response instead of failing the whole request. Cost compounds across an ensemble faster than across one model, so quantization and pruning matter more here: shrinking each sub-model by even a modest factor multiplies out across every model in the request.
flowchart LR
U[User request] --> LB[Latency-aware global router]
LB --> R1[Region A replica pool]
LB --> R2[Region B replica pool]
R1 --> F1[Model A]
R1 --> F2[Model B]
R1 --> F3[Model C]
F1 --> AG[Response aggregator]
F2 --> AG
F3 --> AG
AG --> U
Worked example
Assume a single-region, single-model request must land under a 150 ms end-user budget: 20 ms client-to-edge network, 10 ms routing and queuing, 100 ms model inference (already shrunk via quantization to fit one accelerator), and 15 ms response serialization.
20+10+100+15=145 ms, a 5 ms margin under the 150 ms budgetNow extend this to three independent models behind one request. Network and routing costs (20 + 10 + 15 = 45 ms) are paid once regardless of how many models run. Called in parallel, with individual inference times of 100 ms, 80 ms, and 60 ms, the model stage is the maximum of the three:
45+max(100,80,60)=45+100=145 ms, the same total as the single-model caseHad the same three models instead been chained sequentially, output of one feeding the next, the model stage becomes their sum:
45+(100+80+60)=45+240=285 ms, 135 ms over the 150 ms budgetThis is exactly why independent sub-models belong in a fan-out, not a chain, whenever the request allows it.
Trade-offs and pitfalls
Model or tensor parallelism trades serving cost for latency in the wrong direction if reached for prematurely; a frequent wrong turn is sharding a model that would have fit on a single accelerator, just to save on a larger instance, and eating the inter-shard network cost on every request going forward. Multi-region active-active replication of a very large model is expensive, since it means full weight storage and warm compute per region; a common cost-saving pitfall is running the large model in only one or two regions and routing distant users through it, which quietly reintroduces the cross-region latency the whole design exists to avoid. For multi-model requests, the fan-out-versus-pipeline choice is often made implicitly by whichever engineer wires up the second model rather than deliberately, so a design review should ask, for every pair of sub-models, whether one genuinely needs the other's output before accepting a sequential dependency.
A company with roughly 100 million users and thousands of models running in production asks you to design a shared ML platform that many teams can build on. How do you structure it so teams stay isolated from each other's failures and costs while still sharing the underlying infrastructure?
Sample Answer
Direct answer
Split the platform into a control plane and a data plane: a shared control plane owns identity, quotas, and policy, while the data plane runs each team's workloads inside an isolated compute and cost boundary, so one team's runaway job cannot exhaust shared capacity or budget. Standardize the artifact contract (model, environment, and metrics) so any team's output, regardless of training framework, is consumable by the rest of the company.
Structured elaboration
| Isolation concern | Mechanism | Why not settle for less |
|---|---|---|
| Compute (the noisy-neighbor problem: one tenant's workload degrading another's performance purely by sharing hardware) | Per-tenant resource quotas plus namespace or cluster boundaries with CPU/GPU limits and priority classes; a low-priority experimentation job is preempted before it can starve a production serving pod | Without a hard ceiling, one team's hyperparameter sweep can silently starve another team's live inference pods on the same node pool |
| Cost | A per-tenant budget meter on compute-hours and storage, with a hard alarm that throttles new job submissions rather than killing a job already serving live traffic | Shared infrastructure without cost isolation turns "shared" into "whoever spends first gets it" |
| Data and compliance (including GDPR-style tenancy) | Tenant-scoped data access enforced by a policy engine using attribute-based access control, plus residency and retention metadata on any dataset containing regulated personal data, enforced independent of which team owns the compute | A GDPR-relevant dataset must not leave its legal region even though the platform underneath is otherwise shared; this is a data-tenancy rule, not a compute-tenancy rule, and needs its own enforcement point |
| Multi-framework reproducibility | The model registry's contract is "load this artifact and reproduce this prediction" (artifact, pinned environment, training code commit), independent of whether a team trained in TensorFlow or PyTorch | Lets teams keep their own framework choice without one team's tooling blocking another team's pipeline from using the same shared registry |
| Downstream consumption | Model outputs are written to a shared, schema-registered output table so business-intelligence (BI) dashboards or reporting tools can consume predictions without any framework-specific integration | Decouples "how was this model built" from "how do people who don't work in ML consume its output" |
flowchart TB
CP[Control plane: identity, quota, policy]
PE[Policy engine: ABAC + residency rules]
TA[Team A namespace]
TB[Team B namespace]
GPU[Shared GPU pool]
MR[Model registry]
SV[Serving endpoints]
BI[BI / reporting consumers]
CP --> TA
CP --> TB
PE --> TA
PE --> TB
TA --> GPU
TB --> GPU
TA --> MR
TB --> MR
MR --> SV
SV --> BI
Worked example
Suppose the platform has a shared pool of 500 GPUs across roughly 40 teams. At parity, each team's fair share is 500/40=12.5 GPUs. Setting a hard per-team ceiling at, say, 5% of the pool (25 GPUs) gives about 2x headroom over parity for legitimate spiky workloads, while still bounding the worst case: even if one team claims its full quota, 500×0.95=475 GPUs (95% of the pool) remain available to everyone else. That single number, the fraction of the pool one tenant can claim before hitting a ceiling, is the concrete lever that turns "noisy neighbor" from an open-ended risk into a bounded one.
Trade-offs & pitfalls
- Stronger isolation (a fully dedicated cluster per tenant) costs more and is operationally heavier than namespace-plus-quota isolation; reserve dedicated clusters for tenants with an actual compliance requirement that data cannot share hardware with other tenants' workloads, not as a default.
- A live serving endpoint sharing a node pool with training jobs needs a stricter guarantee than "same team, same priority": priority classes should reflect production versus experimentation, not just team identity, or a batch job can still degrade a production endpoint belonging to the same team.
- Centralizing every policy decision in the control plane recreates the single point of contention the platform was built to avoid; the control plane should audit and set limits, not gate every individual action.
- Compute isolation and data-residency isolation are separate dimensions: a team can be perfectly isolated on compute and cost and still violate a data-residency rule if the policy engine doesn't independently enforce where regulated data is allowed to live.
What would you want on the monitoring dashboard for a model that's been live in production for a while? Be specific about what would go on it, and how you'd explain to a non-technical executive why one of those panels just turned red.
Sample Answer
Direct answer
A production model's dashboard needs three tiers of panels: system health (is it up and fast), input and prediction health (is what is arriving, and what the model is outputting, still what is expected), and outcome health (is it still right), because a model can look perfectly healthy on the first tier while being silently wrong on the third. When a panel turns red for a non-technical executive, the useful explanation skips the underlying statistic entirely and leads with the business consequence in one sentence, offers the mechanism only as a short "why" if it is known, and closes with what is already being done about it, because the executive's real question underneath "why is it red" is almost always "should I be worried, and is someone already on it."
Structured elaboration
| Panel | What it tracks | Tier | Example alert condition |
|---|---|---|---|
| Traffic and latency | Request volume, p50/p95/p99 (50th/95th/99th percentile) latency, error rate | System | p99 latency above target for 5 minutes |
| Input and feature health | Per-feature distribution shift, missing-value rate, schema violations | Input | A feature's distribution shift crosses a set threshold |
| Prediction distribution | Whether predicted classes or scores are shifting over time | Input | Predicted-positive rate moves sharply from its recent baseline |
| Outcome or quality proxy | Accuracy against delayed ground truth, or a fast proxy such as override or manual-review rate | Outcome | Proxy metric degrades beyond a set band |
| Business impact | The KPI (key performance indicator) the model exists to move, for example approval rate or conversion | Outcome | KPI moves beyond an agreed tolerance |
| Deploy markers | Every rollout overlaid as a vertical marker on every other panel | Correlation aid | N/A, always visible |
Explaining a red panel to a non-technical executive, a repeatable structure:
- Impact first, stated in business units, not model units: "we are likely approving more risky applications than intended," not "PSI (population stability index) crossed a threshold."
- One-line mechanism, in plain language, only if it is actually known: "the upstream sign-up form changed and is sending empty fields," or, if not yet known, "we are actively narrowing it down between a data problem and a real change in customer behavior."
- What is already being done and by when: "we have routed higher-risk cases to manual review while we confirm the cause; next update in two hours."
Applied variant, training-job health metrics: the same three-tier structure applies to the training pipeline itself, not only the serving path: a training-job dashboard needs its own system tier (job success rate, time-to-completion), its own input tier (dataset freshness, row-count anomalies at ingestion), and its own outcome tier (offline validation metrics on the freshly trained candidate before it is ever promoted), because a training pipeline that "succeeds" every run but silently trains on stale or malformed data is the training-side equivalent of a model that is fast and up but wrong.
Worked example
Suppose the outcome panel for a fraud model shows its fraud-catch rate has fallen to roughly half its usual level over the last two days, based on confirmed-fraud labels that just arrived. Framed for the model's own dashboard, an engineer would say "our recall on confirmed fraud has dropped sharply over the last 48 hours." Framed for the executive, the metric name disappears entirely: "in the last two days we caught roughly half as much fraud as usual before it went through; we do not yet know if this is a bug or a new fraud pattern, and we have temporarily tightened manual review on borderline cases while we find out."
Trade-offs and pitfalls
A common wrong turn is building only the system tier, because it is the easiest to instrument, which gives false confidence that "monitoring is in place" while the model can be silently wrong for days; this is exactly the failure mode behind a model whose quality quietly drops with nobody noticing. Translating a red panel for an executive by walking through the metric's definition reads as evasive at precisely the moment the executive wants a fast, confident answer. Omitting deploy markers from the outcome and quality panels is the single fastest way to rule out "did we just ship something" as the cause, and skipping it means every incident starts from scratch.
You're building the evaluation and rollout plan for a model used in a healthcare triage setting, where a wrong prediction has real consequences. What would that evaluation plan need to cover before you'd be comfortable putting the model in front of a clinician?
Sample Answer
Direct answer
The evaluation plan needs three layers before a clinician ever sees a prediction: rigorous offline validation on data that looks like the deployment population, a period where the model runs silently alongside clinicians so you measure real-world performance without it influencing care, and a staged, monitored rollout with pre-agreed stopping rules. The single organizing principle: in triage, a false negative (missed urgent case) and a false positive (unnecessary alarm) have very different costs, so the plan has to be built around that asymmetry rather than around a single aggregate accuracy number.
Structured elaboration
Evaluation-to-rollout pipeline
flowchart TD
A[Offline evaluation on multi-site holdout] --> B[Silent shadow deployment]
B --> C{Safety and fairness gates}
C -->|Fail| D[Return to model or data team]
C -->|Pass| E[Staged pilot: single site, assistive mode]
E --> F[Prospective RCT or stepped-wedge trial]
F --> G{Non-inferiority and safety met}
G -->|No| D
G -->|Yes| H[Full clinical rollout]
H --> I[Continuous drift and subgroup monitoring]
I --> J[Periodic re-validation]
J --> C
1. Offline evaluation, before anything touches a clinician
- Hold out data by time and by site (not a random split), so the estimate reflects performance on a hospital or population the model has not seen, catching cases where the model quietly learned a site-specific artifact instead of the clinical signal.
- Report sensitivity (recall) on the urgent-case class as the headline metric, not overall accuracy, because urgent cases are usually a small fraction of volume and accuracy can look excellent while missing most of them.
- Report calibration (does a predicted probability of 0.8 correspond to roughly 80% of those cases actually being urgent) separately from discrimination, because a clinician needs to trust the number, not just the ranking.
- Break every metric out by demographic subgroup (age, sex, site, language) rather than reporting one pooled number, since a pooled metric can hide a subgroup where the model is unsafe.
2. Silent (shadow) deployment
The model runs on live cases in real time, its predictions are logged, but clinicians never see them and never act on them. This is the step that catches "worked in offline eval, breaks in production" failures: label leakage in the historical data, a preprocessing mismatch between the training pipeline and the live feature pipeline, or a shift in the patient population since the training data was collected. Compare shadow-period sensitivity and calibration against the offline estimate before proceeding.
3. Staged, human-in-the-loop clinical validation
- Assistive mode first: the model's output (score plus a plain-language rationale) is shown to the clinician, who retains the decision; nothing is automated.
- A single site or unit before a multi-site rollout, so you learn how clinicians actually use the tool (do they defer to it, ignore it, use it only for edge cases) before that behavior is baked in everywhere.
- A prospective trial (randomized or stepped-wedge) with endpoints defined ahead of time: time-to-treatment for urgent cases, rate of missed critical events, and a pre-specified non-inferiority margin, so "did it help" is answered by a design that was agreed on before you saw the data, not by a post-hoc read of favorable-looking numbers.
- Stopping rules for harm: if the missed-urgent-case rate crosses a pre-agreed threshold during the trial, the trial halts. This has to be decided before the trial starts, not negotiated after a bad week.
4. Governance and monitoring that outlives the launch
- A model card and data sheet documenting training population, known limitations, and intended use, reviewed by a model risk or clinical safety committee before go-live.
- Post-deployment: real-time dashboards for sensitivity/FNR (false negative rate, the fraction of true urgent cases the model misses) by subgroup, calibration drift, and feature drift, with automatic alerts and a rollback path if any breach the pre-agreed threshold.
- A retraining and re-validation cadence: the plan doesn't end at launch, it specifies how often the model gets re-evaluated against fresh holdout data as the patient population and clinical practice shift.
Worked example
Say the target patient population has an urgent-case prevalence of 3%, and the triage system processes 5,000 cases a day.
Expected urgent cases/day=5000×0.03=150At a false-negative rate (FNR, the fraction of true urgent cases the model misses) of 3%, that is:
Missed cases/day=150×FNRwhich gives 4.5 missed cases/day at FNR = 3%, versus 15/day at FNR = 10%. That gap is why the acceptance threshold is negotiated on sensitivity, not overall accuracy: a model that is 97% accurate overall but has a 10% FNR on the urgent subgroup is missing three times as many critical cases as one with a 3% FNR.
Now suppose the shadow deployment observes 1,500 confirmed urgent cases over its run and the model misses 40 of them:
SE=np(1−p),p=150040=0.0267 95% CI=p±1.96⋅SE=0.0267±0.0081=[0.0185, 0.0348]So the observed FNR is about 2.67%, with a 95% confidence interval of roughly 1.85% to 3.48%. That interval is wide even with 1,500 urgent-case events, which is the practical argument for why shadow periods for rare, high-stakes subgroups need to run long enough (often across multiple sites, over weeks) to pin the estimate down tightly enough to make a go/no-go call with confidence, rather than reading too much into a single week of data.
Trade-offs & pitfalls
- The most common wrong turn: optimizing and reporting a single aggregate metric (AUC, accuracy) instead of the sensitivity/FNR split by subgroup that actually reflects clinical harm. A senior answer leads with the asymmetric-cost framing, not with a generic "we'll do an 80/20 train/test split."
- Skipping the silent/shadow stage and going straight from offline metrics to a clinician-facing pilot is the fastest way to discover a training-serving skew (systematic difference between how features were computed in training versus in the live serving path) as a patient-safety incident instead of a dashboard alert.
- Treating the prospective trial as a formality after the model is already "approved" internally, rather than as the actual gate: the stopping rules and non-inferiority margin must be able to kill the rollout, or they are theater.
- Over-indexing on model performance while under-specifying the human factors: if the UI doesn't clearly communicate uncertainty and the clinician silently starts rubber-stamping the model's suggestion (automation bias), the safety plan has a gap no amount of offline validation catches.
- Regulatory and compliance scope (for example FDA guidance on software as a medical device, or HIPAA for patient data handling) needs to be identified early, since it can change what evidence the trial has to produce, not bolted on after the pilot is already running.
A mid-sized team is deciding whether to build training and serving on managed cloud ML services or run it themselves on Kubernetes. What would push you toward one or the other, and what would make you revisit that decision later?
Sample Answer
Direct answer
Start with managed cloud ML services unless the team already has platform engineering capacity to spare or a workload that doesn't fit managed abstractions well. The two things that should make you revisit the decision later are a computable cost crossover (your usage grows past the point where the fixed cost of running it yourselves is cheaper than the managed premium) and a hard capability wall (a hardware, scheduling, or compliance need managed can't meet).
Structured elaboration
| Criterion | Managed (e.g. a vendor's hosted training/serving service) | Self-run on Kubernetes |
|---|---|---|
| Time-to-market | Fast: built-in pipelines, autoscaling, storage/identity integrations | Slower: you build CI/CD, operators, autoscaling yourself |
| Operational overhead | Low: vendor handles patching, scheduler tuning | Real: needs dedicated platform/SRE capacity for GPU drivers, storage, networking |
| Cost predictability | Predictable billing, but a management premium is baked in and fine-grained optimization is harder | Can be cheaper at scale (spot capacity, custom bin-packing) but needs engineering effort to realize |
| Vendor lock-in | Higher: proprietary endpoint formats, registries, feature stores | Lower: standard container images and portable model formats travel across clouds |
| Custom hardware / ops | Constrained to what the vendor exposes, though many now support bring-your-own-container | Full flexibility: custom kernels, newest accelerators, custom schedulers |
Decision criteria for a mid-sized team: if the priority is fast delivery with limited dedicated ops headcount, using mostly standard frameworks, choose managed. If the priority is custom kernels, bleeding-edge hardware, strict cost control at real scale, or multi-cloud portability, choose Kubernetes. A common middle path is to start managed for experimentation and the standard model lifecycle, and move only the production-critical or genuinely specialized workloads to Kubernetes once the cost/complexity trade-off is clearly favorable, which is exactly the computation below.
Worked example
The break-even isn't a philosophical call, it's a computable crossover. Using illustrative rates (explicitly hypothetical inputs, not vendor list prices, since real prices change and shouldn't be asserted as fact) to show the shape of the calculation:
Assume the managed service charges an all-in rate of $4.00 per GPU-hour (covers autoscaling, orchestration, monitoring). Assume the raw GPU-hour cost when self-managed is $3.00, but running it yourselves requires roughly half a platform engineer's time at this scale, at a fully-loaded cost of $180,000/year:
F=12180,000×0.5=7,500 per month (fixed platform overhead)Define monthly cost as a function of GPU-hours used per month, h:
costmanaged(h)=4.00h costself(h)=3.00h+7,500Set them equal to find the crossover point:
4.00h=3.00h+7,500 h=7,500 GPU-hours/monthCheck: at h=7,500, managed costs 4.00×7,500=30,000, and self-hosted costs 3.00×7,500+7,500=22,500+7,500=30,000. They match, confirming the crossover.
Below 7,500 GPU-hours/month, managed is cheaper because the fixed platform-engineering cost isn't yet justified by volume; above it, self-hosted is cheaper. The specific number is only as good as the rates and headcount assumption you plug in, but the shape of the calculation, a fixed cost against a per-unit rate difference, is what you should actually recompute with your own numbers rather than deciding by gut feel.
Trade-offs & pitfalls
Comparing only the sticker GPU-hour price ignores engineering opportunity cost on one side and ignores real operational risk (patching, scaling failures) on the other; both belong in the model, not just the rate. Don't forget to amortize the one-time migration cost into the crossover: a move that pays off at steady state can still be a net loss for the first several months. Treat this as a decision to revisit on a schedule (say, quarterly), not a one-time fork in the road, since usage volume, team headcount cost, and vendor pricing all move independently of each other.
Unlock Full Question Bank
Get access to all End-to-End ML System Design interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.