← OpenAI Interview Insights

OpenAI·Frontend Engineer·Onsite - System Design / Architecture·Senior

SeniorPrefer not to say
Jun 2026

Summary

System design round for a frontend-leaning role at OpenAI, centered entirely on building an AI prompt playground with streaming, presets, and failure handling end to end. The scope was bigger than I expected and the interviewer wanted me to drive, not just answer.

Questions Asked (9)

Q1

Design a web-based AI prompt playground (similar to the ChatGPT Playground) where users can type a prompt, configure model parameters, run it, and watch the response stream back token by token.

System DesignTechnical Trade-offs
Author's notes

This was the core question and it ate the whole session.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying requirements and scope, then walk through a high-level architecture covering UI components, state management, and streaming data flow. Dive into key technical decisions like streaming transport (SSE vs WebSocket), state handling for partial responses, and performance optimizations, while discussing trade-offs and edge cases.

Pro tip: Emphasize how you'd handle streaming gracefully—e.g., using ReadableStream with incremental rendering and abort controls—and mention observability (logging token latency) to show production maturity.

1. Clarify requirements and scope

Ask about expected scale, supported models, parameter configurability, and whether features like conversation history or multi-turn are needed. Confirm the focus is on frontend architecture and user experience.

2. Outline high-level architecture

Describe the main UI components (prompt input, parameter panel, response area) and the data flow from user action to API call to streaming response. Mention state management (e.g., React context or Redux) and separation of concerns.

3. Detail streaming implementation

Explain how to consume a streaming API (e.g., using fetch with ReadableStream or EventSource) and incrementally update the UI. Discuss handling partial tokens, error recovery, and cancellation.

4. Discuss performance and UX considerations

Cover optimizations like debouncing parameter changes, virtualizing long responses, and showing loading states. Address accessibility and responsive design for a smooth user experience.

5. Highlight trade-offs and edge cases

Compare SSE vs WebSocket for streaming, discuss state management choices, and outline error handling (network failures, rate limits). Mention testing strategies for streaming behavior.

Key Points to Mention

  • Streaming transport: Server-Sent Events (SSE) vs WebSocket, and why SSE is often sufficient for one-way token streaming
  • State management: handling partial responses, optimistic updates, and avoiding unnecessary re-renders
  • Performance: debouncing parameter inputs, virtualizing long conversations, and memoization
  • Error handling and resilience: retry logic, abort controllers, and graceful degradation
  • Accessibility: ARIA live regions for streaming updates and keyboard navigation
  • Observability: logging token latency and error rates for monitoring

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q2

Walk through the full client-to-provider data flow for a single run, including how streaming tokens get from the model provider back to the browser.

System DesignAPI & Integrations
Author's notes

Pretty much the happy path version of the big question.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by framing the flow at a high level: browser sends a request to your backend, which calls the model provider's API with streaming enabled, then relays the streamed tokens back to the browser via SSE or WebSockets. Then walk through each hop in order, emphasizing where the frontend is involved and how streaming is handled end-to-end.

Pro tip: Mention that you'd use the Fetch API with ReadableStream to consume the stream on the client, and that you'd handle backpressure and cancellation (e.g., AbortController) to avoid memory leaks and ensure a smooth UX.

1. Client initiates request

The browser sends a user prompt to your backend via HTTPS, typically as a POST request. The frontend sets up an event listener or stream reader to handle the response incrementally.

2. Backend calls model provider

Your server authenticates with the model provider's API (e.g., OpenAI) and sends the prompt with streaming enabled (e.g., stream: true). The provider responds with a stream of server-sent events (SSE) or chunked transfer encoding.

3. Backend relays stream to client

The backend forwards the streamed chunks to the browser, either by proxying the SSE stream directly or by re-emitting events over WebSockets. It may also transform or buffer chunks as needed.

4. Client processes streamed tokens

The browser reads the stream incrementally using the Fetch API's ReadableStream or EventSource, parses each chunk (e.g., JSON lines), and updates the UI in real-time as tokens arrive.

5. Handle completion and errors

The client detects the end of the stream (e.g., [DONE] signal) and finalizes the UI. It also handles errors, retries, and cancellation (e.g., via AbortController) to ensure robustness.

Key Points to Mention

  • Use of Server-Sent Events (SSE) or WebSockets for streaming from backend to browser
  • Fetch API with ReadableStream for consuming streams in the browser
  • Backend as a proxy to hide API keys and manage rate limiting
  • Parsing streamed chunks (e.g., JSON lines with delta content)
  • Handling backpressure and cancellation with AbortController
  • Error handling and reconnection strategies for streaming

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q3

How would you design the model gateway to abstract away provider-specific details like API keys, parameter ranges, and streaming wire formats?

System DesignAPI & Integrations
Author's notes

This one I actually liked.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the gateway's role as a unified interface that normalizes provider differences, then walk through key abstraction layers: configuration, request/response transformation, and streaming. Emphasize how the frontend benefits from a consistent API and how to handle provider-specific quirks without leaking them.

Pro tip: Highlight the importance of a capability-based provider registry and runtime validation to gracefully handle parameter range differences, showing you think about robustness and developer experience.

1. Define a unified API contract

Specify a provider-agnostic request/response schema that covers common parameters (model, messages, temperature, max_tokens) and streaming events, using TypeScript types or JSON Schema for clarity.

2. Abstract provider configuration and credentials

Design a provider registry that stores API keys securely (e.g., via environment variables or a secrets manager) and maps logical model names to provider-specific endpoints and auth methods.

3. Normalize parameters and handle ranges

Implement per-provider adapters that validate and clamp parameter values (e.g., temperature ranges) and translate unified parameters into provider-specific ones, with clear error messages for out-of-range values.

4. Unify streaming and response formats

Create a streaming adapter that converts provider-specific wire formats (SSE, WebSocket, JSON lines) into a consistent event stream (e.g., token deltas, completion events) and normalizes non-streaming responses.

5. Add observability and fallback mechanisms

Include logging, metrics, and error handling that abstract provider failures, and consider fallback strategies (e.g., retry with another provider) while keeping the frontend unaware of provider specifics.

Key Points to Mention

  • Provider registry with capability metadata (supported models, parameter ranges, streaming support)
  • Secure credential management (never expose API keys to the frontend; use server-side proxy)
  • Parameter validation and normalization with clear error messages
  • Streaming abstraction: converting SSE/WebSocket/JSON lines into a unified event stream
  • Adapter pattern for request/response transformation
  • Observability: logging, metrics, and error handling that hide provider details

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q4

What failure modes do you need to handle across the run lifecycle, covering both your own server and the model provider, and what should the client do in each case?

System DesignRoot Cause Analysis
Author's notes

The interviewer explicitly said they wanted me to enumerate these without being prompted for each one.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Structure your answer by walking through the run lifecycle chronologically, identifying failure modes at each stage from both your server and the model provider. For each failure, specify the client's responsibility (e.g., retry, surface error, degrade gracefully) and emphasize idempotency, observability, and user experience.

Pro tip: Demonstrate maturity by distinguishing between retryable and non-retryable errors, and by designing client behavior that avoids thundering herds (e.g., exponential backoff with jitter). Also mention how you'd surface actionable errors to users without exposing internal details.

1. Map the run lifecycle stages

Outline the key stages: request initiation, authentication, model inference, streaming response, and completion. This sets the scope for failure analysis.

2. Identify server-side failure modes

List failures like rate limiting, timeouts, validation errors, internal server errors, and partial failures. Explain how each manifests to the client (e.g., HTTP status codes, error payloads).

3. Identify model provider failure modes

Consider provider-specific issues: model overload, content filtering, token limits, latency spikes, and provider outages. Note how these propagate through your server to the client.

4. Define client behavior for each failure

For each failure mode, specify the client's action: retry with backoff, fallback to a simpler model, show a user-friendly error, or degrade features. Emphasize idempotency and avoiding duplicate requests.

5. Highlight observability and resilience patterns

Mention logging, metrics, and tracing to detect failures, and patterns like circuit breakers, timeouts, and graceful degradation to maintain UX.

Key Points to Mention

  • Retry strategies with exponential backoff and jitter for transient errors (e.g., 429, 5xx).
  • Idempotency keys to prevent duplicate runs on retries.
  • Circuit breakers to avoid cascading failures when the model provider is down.
  • User-facing error messages that are actionable but don't leak sensitive info.
  • Fallback mechanisms (e.g., switching to a smaller model or cached response) for degraded service.
  • Observability: logging, metrics, and tracing to diagnose failures across the stack.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q5

Is it always safe to retry a failed run? What changes if the first token has already been streamed to the user before the failure?

Technical Trade-offsAPI & Integrations
Author's notes

Knew this was coming after the failure modes question.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying that retrying is not always safe—it depends on idempotency and side effects. Then explain the streaming complication: once tokens are sent, you can't simply retry without risking duplicate or inconsistent output, so you need strategies like resuming or fallback. Emphasize user experience and correctness trade-offs.

Pro tip: Mention that you'd design the system to be idempotent from the start (e.g., using request IDs) and handle streaming failures gracefully by informing the user and offering a retry that continues from the last token, rather than restarting.

1. Define retry safety

Explain that retrying is safe only if the operation is idempotent and has no side effects. For non-idempotent operations, retrying can cause duplicates or inconsistencies.

2. Consider streaming context

Describe how streaming changes the situation: once the first token is sent, the user has seen partial output. A naive retry would restart and duplicate content, confusing the user.

3. Evaluate options after partial stream

Discuss alternatives: resume from the last token (if the API supports it), fall back to a non-streaming response, or inform the user and let them decide. Highlight the need for a request ID to correlate retries.

4. Design for idempotency and UX

Propose design patterns: use idempotency keys, track stream state, and implement graceful error handling that preserves user trust. Mention the trade-off between automatic retry and user control.

5. Conclude with best practices

Summarize that retries should be conditional, and streaming requires special handling to avoid duplication. Emphasize testing failure scenarios and monitoring.

Key Points to Mention

  • Idempotency and side effects: retrying non-idempotent operations can cause duplicates.
  • Streaming complicates retries because partial output is already visible to the user.
  • Use request IDs or idempotency keys to deduplicate retries.
  • Resuming a stream from the last token is ideal but may not be supported; fallback strategies are needed.
  • User experience: inform the user and offer control rather than silently retrying.
  • Design for failure: implement retry logic with exponential backoff and circuit breakers, but only for safe operations.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q6

A user reloads the page mid-generation. How do you let them reconnect to an in-flight run instead of losing it?

System DesignTechnical Trade-offs
Author's notes

Blanked for a second on this one.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the requirements: the run is server-side and identified by a run ID, and the client must persist that ID across reloads. Then describe a reconnection flow: on page load, check for a stored run ID, call an endpoint to fetch the run's current state, and resume streaming from the last received event using a cursor or sequence number. Finally, discuss trade-offs like storage choice, idempotency, and handling expired or completed runs.

Pro tip: Emphasize that the server should be the source of truth and the client should be stateless—this avoids complex client-side state recovery and aligns with scalable, resilient system design.

1. Clarify the run lifecycle and requirements

Define what an 'in-flight run' means: a server-side process with a unique ID that produces a stream of events. Establish that the client needs to survive reloads, network drops, and multiple tabs.

2. Persist run identity on the client

Store the run ID (and optionally a last-event cursor) in a durable client-side store like localStorage or sessionStorage, keyed by conversation or user context.

3. Implement a reconnection endpoint

On page load, read the stored run ID and call a server endpoint (e.g., GET /runs/{id}) that returns the run's status and a stream of events from a given cursor, or a snapshot if completed.

4. Resume streaming with cursor-based replay

Use a sequence number or event ID to request only events after the last received one, ensuring no duplicates or gaps. Handle cases where the run finished, failed, or expired.

5. Discuss trade-offs and edge cases

Compare storage options (localStorage vs. IndexedDB vs. cookies), consider multi-tab coordination, idempotency of reconnection, and cleanup of stale run IDs.

Key Points to Mention

  • Server-side run ID as the single source of truth
  • Client-side persistence using localStorage or sessionStorage
  • Cursor or sequence number for resuming streams without duplication
  • Idempotent reconnection endpoint that returns current state and events
  • Handling of completed, failed, or expired runs gracefully
  • Multi-tab and cleanup strategies for stale run IDs

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q7

You're adding a second model provider with a different streaming format and parameter limits. What changes in your architecture and what stays the same?

System DesignAdaptability & Ambiguity
Author's notes

Good question to end on.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the current architecture and the requirements of the new provider, then systematically separate provider-specific concerns from shared logic. Emphasize abstraction layers (e.g., adapter pattern) and configuration-driven design to handle differences in streaming and parameter limits while keeping core components unchanged.

Pro tip: Highlight the importance of a unified interface and thorough testing with mocked streams to ensure seamless integration and graceful degradation. Mention that you'd design for observability from the start to catch format mismatches early.

1. Clarify current architecture and new provider requirements

Ask questions to understand the existing system's components, data flow, and the new provider's streaming format and parameter constraints. This ensures you address the right pain points.

2. Identify what stays the same

List core components that should remain provider-agnostic, such as UI components, state management, and business logic. Emphasize that these should not change to avoid regressions.

3. Design abstraction for provider differences

Propose an adapter or strategy pattern to encapsulate provider-specific streaming parsing and parameter mapping. This isolates changes and allows adding future providers easily.

4. Handle streaming format differences

Describe how you would normalize streaming data (e.g., using a common event format) and manage backpressure or chunking differences. Mention fallbacks for unsupported features.

5. Address parameter limits and configuration

Explain how you would enforce parameter limits via validation and configuration, possibly with a capability registry per provider. Ensure the UI adapts dynamically to provider constraints.

Key Points to Mention

  • Adapter pattern or provider abstraction layer to isolate differences
  • Configuration-driven design for parameter limits and feature flags
  • Normalization of streaming events into a unified format
  • Graceful degradation and error handling for unsupported features
  • Testing strategy with mocked streams and contract tests
  • Observability and logging to detect format mismatches

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q8

How do you enforce per-user and per-org cost and rate limits without adding latency to time-to-first-token on the hot path?

System DesignProduct Analytics & Metrics
Author's notes

Rate limit checks need to happen before the run starts, not inline with the stream.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying that the hot path is the streaming response pipeline, and that enforcement must happen before the first token is generated. Propose a two-tier design: a fast, local, eventually-consistent check (e.g., token bucket in memory or edge cache) for the hot path, backed by an asynchronous, authoritative reconciliation service that updates limits without blocking requests. Emphasize that the frontend can help by pre-fetching quota status and gracefully degrading UI when limits are near, but the core enforcement is backend.

Pro tip: Mention that you would use a 'fail-open with alerting' strategy for the fast path to avoid latency spikes, while the async reconciler ensures eventual correctness—this shows you understand trade-offs between availability and strict enforcement.

1. Clarify the hot path and constraints

Define what 'hot path' means (time from request to first token) and confirm that any synchronous check must be sub-millisecond. State that the frontend cannot enforce limits but can surface quota status to reduce failed requests.

2. Design a fast local enforcement layer

Propose an in-memory or edge-cached token bucket per user/org, updated asynchronously from a central service. This check adds negligible latency and can be done in the same process or at the API gateway.

3. Implement asynchronous reconciliation

Describe a background service that aggregates usage and periodically pushes updated limits to the fast layer. Use eventual consistency and handle race conditions with idempotent updates.

4. Handle failures and edge cases

Explain fallback strategies: if the fast layer is unavailable, fail-open with logging and alerting, or fall back to a slightly stale cached value. Also discuss how to handle sudden bursts and distributed rate limiting.

5. Frontend integration and UX

Mention how the frontend can fetch quota status periodically and show warnings or disable actions when limits are near, reducing the chance of hitting limits on the hot path.

Key Points to Mention

  • Token bucket or leaky bucket algorithm for rate limiting
  • Edge caching or in-memory data stores (e.g., Redis) for low-latency checks
  • Asynchronous reconciliation with eventual consistency
  • Fail-open vs fail-closed trade-offs and alerting
  • Distributed rate limiting challenges (e.g., clock skew, network partitions)
  • Frontend pre-fetching of quota status and graceful degradation

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q9

The model output is rendered as markdown or HTML in the browser. How do you defend against prompt-injected content causing XSS, especially while streaming partial markup?

Technical Trade-offsSystem Design
Author's notes

Security question I half-expected.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by acknowledging the dual challenge: rendering model output safely while streaming partial markup. Then outline a defense-in-depth strategy: sanitize with a strict allowlist, use a safe rendering pipeline (e.g., virtual DOM with escaping), and handle streaming by buffering and sanitizing incrementally. Emphasize that no single mitigation is foolproof, so combine sanitization, CSP, and trust boundaries.

Pro tip: Mention that streaming requires a stateful sanitizer that can handle incomplete tags and attributes, and that you'd test with fuzzing and known XSS vectors to ensure robustness. Also, highlight the importance of keeping the sanitizer library up-to-date and using Trusted Types where available.

1. Understand the threat model

Explain that prompt-injected content can contain malicious HTML/JS, and streaming partial markup increases risk because incomplete tags might bypass naive sanitization. Clarify that the model output is untrusted and must be treated as such.

2. Choose a safe rendering approach

Describe using a robust sanitization library (e.g., DOMPurify) with a strict allowlist of tags and attributes, and rendering via a framework that escapes by default (e.g., React's dangerouslySetInnerHTML only after sanitization). Avoid innerHTML with unsanitized content.

3. Handle streaming safely

Explain that you need to buffer partial markup and only sanitize and render when you have complete tags, or use a streaming-aware sanitizer that maintains state across chunks. Alternatively, render as plain text until a safe boundary is reached.

4. Add defense-in-depth

Mention additional layers: Content Security Policy (CSP) to restrict inline scripts, Trusted Types to enforce sanitization, and sandboxing in an iframe if full HTML is needed. Also, consider server-side sanitization as a backup.

5. Test and monitor

Describe testing with known XSS payloads, fuzzing partial inputs, and monitoring for CSP violations. Emphasize continuous updates to sanitization libraries and staying informed about new bypass techniques.

Key Points to Mention

  • Use a well-maintained sanitization library like DOMPurify with a strict allowlist.
  • Streaming requires buffering or stateful sanitization to handle incomplete tags.
  • Content Security Policy (CSP) and Trusted Types as defense-in-depth.
  • Avoid dangerous sinks like innerHTML; prefer safe frameworks or textContent.
  • Consider sandboxing untrusted HTML in an iframe with restricted permissions.
  • Regularly test with XSS payloads and fuzz testing, and keep dependencies updated.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.