← Openai Interview Insights

Openai·Software Engineer·Onsite - System Design / Architecture·Senior

SeniorPrefer not to say
Jun 2026

Summary

System design round at OpenAI for a software engineering role, centered entirely on designing a extensible chat app with bot routing. Two-part problem that escalated from a single-channel design to multi-channel state isolation. Pretty involved for what sounded like a straightforward OOP question at first.

Questions Asked (7)

Q1

Design a ChatApp class that dispatches user messages to multiple registered bots, where adding a new bot type requires zero changes to the core ChatApp code. Show the bot interface, the dispatch loop, and at least one concrete bot.

System DesignTechnical Trade-offs
Author's notes

The core trap I almost fell into was putting any bot-specific logic inside the dispatch loop.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by defining a clean Bot interface with a method like `handleMessage(message)`, then design ChatApp to maintain a list of Bot instances and dispatch messages by iterating over them. Emphasize that new bots only need to implement the interface and be registered, requiring zero changes to ChatApp. Provide a concrete bot example and discuss trade-offs like ordering, error handling, and extensibility.

Pro tip: Mention that the Open/Closed Principle is key here, and highlight that you'd use dependency injection to register bots, making the system testable and decoupled. Also, briefly discuss how you'd handle bot failures to avoid one bot breaking the dispatch loop.

1. Define the Bot interface

Create an interface with a method that takes a message and returns a response or performs an action. Keep it minimal and focused on the single responsibility of handling a message.

2. Design ChatApp with bot registration

ChatApp should have a collection of bots and methods to register/unregister bots. The dispatch loop iterates over registered bots and calls their handleMessage method.

3. Implement a concrete bot

Provide at least one bot, such as EchoBot or GreetBot, that implements the Bot interface. Show how it processes a message and returns a response.

4. Demonstrate extensibility

Explain that adding a new bot type only requires creating a new class that implements Bot and registering it with ChatApp, with no modifications to ChatApp's code.

5. Discuss trade-offs and edge cases

Address potential issues like bot ordering, error handling, asynchronous dispatch, and performance considerations. Mention how the design supports testing and scalability.

Key Points to Mention

  • Open/Closed Principle: ChatApp is open for extension but closed for modification.
  • Dependency injection: bots are passed to ChatApp, promoting loose coupling.
  • Interface segregation: Bot interface is small and focused.
  • Error handling: isolate bot failures so one bot doesn't break the dispatch loop.
  • Ordering and prioritization: consider if bots need to be invoked in a specific order.
  • Asynchronous dispatch: discuss if bots should be called asynchronously for scalability.

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

Q2

Where should per-conversation bot state live if you want a single bot instance to be reusable across many conversations?

System DesignTechnical Trade-offs
Author's notes

Fumbled this initially.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the requirements: the bot instance should be stateless and reusable, so per-conversation state must be externalized. Then propose storing state in a dedicated store (e.g., in-memory cache, database, or distributed cache) keyed by conversation ID, and discuss trade-offs like scalability, persistence, and latency.

Pro tip: Emphasize that the bot instance should be stateless to enable horizontal scaling and reuse; this is a common pattern in production systems and shows you understand separation of concerns.

1. Clarify requirements

Ask about scale, persistence needs, latency requirements, and whether conversations are short-lived or long-running. This ensures your solution fits the context.

2. Define state boundaries

Identify what constitutes per-conversation state (e.g., context, user preferences, session data) and what can be shared across conversations (e.g., bot logic, configuration).

3. Choose a storage solution

Propose an external store such as Redis, a database, or a distributed cache, keyed by conversation ID. Discuss trade-offs between in-memory, persistent, and distributed options.

4. Address lifecycle and consistency

Explain how state is created, updated, and expired (e.g., TTL, cleanup jobs). Consider consistency models and how to handle concurrent access.

5. Discuss scalability and resilience

Highlight how this design enables horizontal scaling, fault tolerance, and reuse of the bot instance across many conversations without state leakage.

Key Points to Mention

  • Stateless bot instance for reusability and scalability
  • External state store keyed by conversation ID (e.g., Redis, DynamoDB)
  • Trade-offs: latency vs. persistence, in-memory vs. distributed
  • State lifecycle management: TTL, cleanup, versioning
  • Concurrency control and consistency guarantees
  • Security and isolation of conversation data

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

Q3

Extend the design to support multiple independent channels, each with its own message log and per-bot state, without changing the dispatch logic.

System DesignData Modeling
Author's notes

This is where the earlier state decision pays off or bites you.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the current design's dispatch logic and identifying the minimal changes needed to support multiple channels. Propose a data model where each channel has its own message log and per-bot state, and ensure the dispatch logic remains channel-agnostic by routing based on channel ID. Emphasize scalability, isolation, and backward compatibility.

Pro tip: Highlight that keeping dispatch logic unchanged means abstracting channel-specific details behind a common interface, so the dispatcher only deals with channel identifiers and message payloads. This demonstrates foresight in decoupling and avoids over-engineering.

1. Clarify requirements and constraints

Ask about expected number of channels, message volume, and whether channels are created dynamically. Confirm that dispatch logic must remain untouched, implying a stable interface.

2. Design data model for channels

Introduce a Channel entity with a unique ID, its own message log (e.g., a partitioned table or separate queue), and per-bot state storage (e.g., a key-value store keyed by channel ID and bot ID).

3. Adapt dispatch logic minimally

Ensure the dispatcher receives a channel ID as part of the message context and routes to the appropriate channel's resources without changing its core algorithm. Use a registry or factory to resolve channel-specific components.

4. Address isolation and scalability

Discuss how to isolate channels to prevent cross-channel interference, and how to scale horizontally by sharding channels across nodes. Mention consistency and fault tolerance.

5. Summarize and validate

Recap how the design meets the requirement of unchanged dispatch logic, and propose testing strategies to ensure correctness and performance.

Key Points to Mention

  • Channel abstraction with unique identifiers and isolated resources
  • Per-bot state storage keyed by channel and bot IDs
  • Message log partitioning or separate queues per channel
  • Dispatcher remains channel-agnostic, using channel ID for routing
  • Scalability via sharding and load balancing across channels
  • Backward compatibility and migration strategy for existing single-channel data

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

Q4

How would you let bots subscribe to only certain messages, like by command prefix, without adding that filtering logic to ChatApp?

System DesignTechnical Trade-offs
Author's notes

Short answer: push the filtering into the bot.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Propose a decoupled architecture where ChatApp remains agnostic of bot filtering logic by introducing an intermediary subscription service or message broker. Bots register their filtering criteria (e.g., command prefix) with this service, which then routes only matching messages to them. This keeps ChatApp simple and extensible.

Pro tip: Emphasize that this design follows the Open/Closed Principle: ChatApp is open for extension (new bots with new filters) but closed for modification. Also mention that the filtering service can be scaled independently and might support complex filters beyond prefixes.

1. Clarify requirements and constraints

Ask about the expected scale, types of filters (prefix, regex, etc.), and whether bots can be trusted. Confirm that ChatApp should not be modified to include filtering logic.

2. Introduce a subscription service

Propose a separate service (e.g., Bot Subscription Service) that bots use to register their filtering criteria. This service maintains a mapping of filters to bot endpoints.

3. Modify ChatApp minimally to publish messages

ChatApp publishes all messages to a message bus or directly to the subscription service. This is a minimal change that doesn't embed filtering logic.

4. Implement filtering and routing in the subscription service

The subscription service evaluates each message against registered filters and forwards matching messages to the appropriate bots. This centralizes filtering logic outside ChatApp.

5. Discuss trade-offs and alternatives

Compare with alternatives like client-side filtering (bots receive all messages) or ChatApp plugins. Highlight scalability, latency, and complexity trade-offs.

Key Points to Mention

  • Decoupling via a message broker or pub/sub system (e.g., Kafka, RabbitMQ)
  • Registration API for bots to specify filters (e.g., command prefix)
  • Centralized filtering service that routes messages based on filters
  • Scalability: filtering service can be scaled independently
  • Extensibility: supports adding new filter types without changing ChatApp
  • Trade-offs: increased latency, potential single point of failure, operational overhead

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

Q5

How would you isolate errors so a single misbehaving bot doesn't break message delivery for the rest of the channel?

System DesignTechnical Trade-offs
Author's notes

Wrap each bot call in a try/except inside the dispatch loop, log the failure, and move on.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the system architecture and failure modes, then propose isolation mechanisms like per-bot queues, circuit breakers, and bulkheads. Emphasize trade-offs between isolation and resource overhead, and how you'd monitor and test the solution.

Pro tip: Mention that isolation should be combined with graceful degradation and backpressure to prevent cascading failures. Also, highlight the importance of observability to quickly identify and quarantine misbehaving bots.

1. Clarify requirements and constraints

Ask about the message delivery system, bot behavior, and what 'misbehaving' means (e.g., slow, crashing, spamming). Understand scale, latency requirements, and existing infrastructure.

2. Identify failure modes and impact

Discuss how a single bot can affect others: resource exhaustion (CPU, memory, threads), blocking shared queues, or causing retries that amplify load.

3. Design isolation mechanisms

Propose per-bot queues or processes, resource quotas, circuit breakers, and bulkheads. Consider asynchronous processing and timeouts to prevent one bot from monopolizing resources.

4. Address trade-offs and scalability

Evaluate overhead of isolation (e.g., more queues/processes), complexity, and how to scale. Discuss dynamic isolation based on bot behavior and fallback strategies.

5. Plan monitoring, testing, and iteration

Describe how to detect misbehaving bots (metrics, logs, alerts), test isolation (chaos engineering), and iteratively improve based on production feedback.

Key Points to Mention

  • Bulkhead pattern: isolate components to prevent failures from spreading.
  • Circuit breaker: stop sending messages to a failing bot after threshold.
  • Per-bot queues or dedicated worker pools to contain resource usage.
  • Backpressure and rate limiting to protect the system from overload.
  • Observability: metrics, tracing, and logging to identify misbehaving bots.
  • Graceful degradation: ensure other bots continue functioning even if one fails.

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

Q6

If bots were asynchronous and made external API calls, how would the dispatch contract and message log need to change?

System DesignAPI & Integrations
Author's notes

Went async/await on the bot interface, made the dispatch loop await each bot or gather them concurrently.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the current synchronous dispatch contract and message log assumptions, then systematically identify how asynchronous external API calls break those assumptions. Propose concrete changes to the contract (e.g., correlation IDs, callbacks, timeouts) and to the log (e.g., state transitions, idempotency keys) while addressing failure modes and consistency.

Pro tip: Emphasize idempotency and exactly-once semantics—interviewers at OpenAI care deeply about reliability and correctness in distributed systems, so showing you think about duplicate deliveries and partial failures sets you apart.

1. Clarify current assumptions

Briefly restate how the dispatch contract and message log work synchronously: blocking calls, immediate responses, and simple append-only logs. This grounds the discussion and shows you understand the baseline.

2. Identify asynchronous challenges

List the new issues: non-blocking dispatch, delayed responses, partial failures, retries, and out-of-order events. Explain how these break the synchronous contract and log consistency.

3. Redesign the dispatch contract

Propose changes like adding correlation IDs, callback URLs or webhooks, timeouts, and idempotency keys. Discuss how the contract shifts from request-response to event-driven or polling-based.

4. Evolve the message log

Describe how the log must capture state transitions (e.g., pending, in-flight, completed, failed), support idempotent writes, and handle out-of-order or duplicate messages. Mention compaction or TTL for long-running async operations.

5. Address consistency and failure modes

Explain how to ensure exactly-once or at-least-once processing, handle retries and dead-letter queues, and reconcile state between the log and external systems. Highlight trade-offs between consistency and availability.

Key Points to Mention

  • Idempotency keys to deduplicate external API calls and log entries
  • Correlation IDs to track asynchronous requests across services
  • State machine for message log entries (e.g., pending, in-flight, succeeded, failed)
  • Timeouts, retries, and exponential backoff for external API calls
  • Exactly-once vs at-least-once delivery semantics and their implications
  • Dead-letter queues and compensating transactions for failure handling

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

Q7

Where are the race conditions if multiple users send into the same channel concurrently, and how would you make the design thread-safe?

System DesignTechnical Trade-offs
Author's notes

The log append and the state lookup/update are both obvious races.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the system's architecture and the shared mutable state involved when multiple users send to the same channel. Identify specific race conditions such as lost updates, duplicate messages, or inconsistent ordering, then propose thread-safety mechanisms like locks, atomic operations, or message queues. Conclude by discussing trade-offs between consistency, latency, and scalability.

Pro tip: Emphasize that thread safety must be designed at the right granularity—over-locking kills throughput, while under-locking causes data corruption. Mention that in distributed systems, you often need both local synchronization and distributed coordination (e.g., consensus or idempotency keys).

1. Clarify the system and shared state

Ask questions to understand the architecture: Is the channel a shared data structure in memory, a database row, or a distributed log? Identify all mutable state (e.g., message list, user list, sequence counter) that concurrent sends could corrupt.

2. Identify specific race conditions

Enumerate concrete races: lost updates when two threads read-modify-write the message list, duplicate messages due to non-atomic check-then-act, out-of-order delivery, and inconsistent fan-out to subscribers.

3. Propose thread-safety mechanisms

Suggest appropriate synchronization: fine-grained locks (e.g., per-channel mutex), lock-free data structures (e.g., concurrent queues), atomic operations for counters, or serializing writes through a single-threaded event loop or message queue.

4. Address distributed and scalability concerns

If the system is distributed, discuss distributed locks, consensus protocols (e.g., Raft), idempotency keys to deduplicate, and partitioning channels to reduce contention. Mention trade-offs between strong consistency and availability.

5. Summarize trade-offs and testing

Conclude by weighing performance vs. correctness, and mention how you would test for race conditions (e.g., stress tests, formal verification, or tools like ThreadSanitizer).

Key Points to Mention

  • Lost updates and read-modify-write races on shared channel state
  • Atomic operations and compare-and-swap for counters or flags
  • Fine-grained locking (e.g., per-channel mutex) vs. global locks
  • Lock-free data structures and concurrent queues
  • Distributed coordination: consensus, distributed locks, idempotency keys
  • Trade-offs: consistency vs. latency, throughput vs. safety, and testing strategies

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