Implement an Extensible Chatbot App
OpenAISoftware Engineer · Technical ScreenLast updated
This question evaluates a candidate's ability to design extensible object-oriented systems, covering interface-based architecture, dependency injection, per-conversation state management, and decoupled dispatch logic.
Implement a ChatApp class that processes user messages and routes them to bot responders.
The system must support multiple bot types, and the design must be extensible: adding a brand-new bot type should require zero changes to existing ChatApp code. You are designing the in-memory application objects and their interfaces — not a distributed system — so focus on clean class design, the right abstractions, and clear extension points.
Constraints & Assumptions
- Single-process, in-memory implementation (no database, network, or RPC layer required).
- A message has at minimum a sender identity and text content.
- Multiple bots may be registered at once; each bot independently decides whether and how to respond to a given message.
- A bot may need to remember context across messages (e.g. a multi-turn flow), so each bot needs somewhere to keep per-conversation state.
- Bots are added over time by other developers — you control the
ChatAppcore, but not the bot implementations. - You may use any mainstream language; the reference answer uses Python.
Clarifying Questions to Ask
- Should every registered bot receive every message, or should bots subscribe to specific message types / channels?
- Do bot responses themselves get re-dispatched to other bots (can a bot reply to a bot), or do only user messages trigger dispatch?
- Is ordering of bot responses significant — must bots run in registration order, or by some priority?
- Are bots registered globally for the whole app, or can each channel have a different set of bots?
- What should happen if one bot raises an exception — fail the whole send, or skip that bot?
- Do we need concurrency safety (multiple users sending into the same channel at once), or is single-threaded acceptable?
Part 1 — Extensible single-channel ChatApp
Design and implement ChatApp so that:
- A user can send a message to the app.
- The app dispatches that message to every registered bot.
- Each bot decides whether and how to respond (it may produce a response, or stay silent).
- The app records a message log containing both user messages and bot responses.
- New bot implementations are added purely through registration / dependency injection — never by editing
ChatApp.
Show the bot interface, the ChatApp core, and at least one concrete example bot demonstrating that adding a bot needs no change to ChatApp.
What This Part Should Cover
- A clean bot abstraction (one contract every bot implements) so the app talks to bots through a single interface.
- A
ChatAppdispatch loop with no knowledge of concrete bot types — noisinstance/type switch (Open/Closed Principle). - A registration / DI mechanism for adding bots, demonstrated with at least one concrete bot added with zero
ChatAppedits. - Correct per-conversation state ownership — the app hands each bot its state slice, so a single bot instance stays reusable and stateless.
- A message log that captures both inbound user messages and outbound bot responses.
Part 2 — Multi-channel support (follow-up)
Extend the design so the app supports multiple channels (think Slack channels or DMs). Each channel must have fully independent state, including:
- Its own message log.
- Its own per-bot state, so the same bot can hold different context in different channels.
Decide and justify whether bots are registered globally (shared across channels) or per-channel, and make sure adding a channel does not require touching the dispatch logic.
What This Part Should Cover
- Channel-isolated state — each channel keeps its own message log and its own per-bot state, so the same bot holds different context per channel.
- A clean extraction of the per-channel pieces (e.g. a
ChannelStatevalue object keyed bychannel_id) so the dispatch loop is unchanged. - A clear, justified stance on global-vs-per-channel bot registration (and what would change if the other choice were required).
- Explicit handling of the lifecycle orderings — a message arriving for an unknown channel, and a bot registered after channels already exist.
Hints
Hint 1
Design principle: "A new bot type must not modify existing code" points squarely at one of the SOLID principles. Which one? Once you name it, ask what language feature normally lets you satisfy it instead of a type switch.
Hint 2
Where state lives: You want one bot *instance* to be reusable across many conversations, yet each conversation needs its own memory. So where can per-conversation state live such that it is *not* a field on the bot object? Think about who hands the bot its memory on each call.
Hint 3
Decoupling the dispatch: Picture the core send path. What is the *most* `ChatApp` should be allowed to know about the bots it loops over? If the answer is "nothing but the interface," what does that imply the loop can and cannot mention?
Hint 4
Where to start: Look at what `ChatApp` currently owns. Which of those things are inherently per-channel, and which (if any) could stay shared across channels? If you grouped the per-channel pieces together and looked them up by channel, would the dispatch loop have to change at all?
Hint 5
Edge cases worth surfacing: Walk through the lifecycle of channels and bot registrations and ask which orderings could leave a channel or a bot without the state it expects. Two orderings in particular need explicit handling — name them to the interviewer before you code.
Follow-up questions
- How would you let bots **subscribe** to only certain messages (e.g. by command prefix or message type) instead of receiving everything, without bloating `ChatApp`?
- How would you add **error isolation** so a single misbehaving bot cannot break the message flow for the channel?
- If bot responses were **asynchronous** (a bot calls an external API), how would the dispatch contract and the message log change?
- How would you make this **thread-safe** if many users send into the same channel concurrently, and where exactly are the race conditions?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.