Design an AI code assistant
Two products share one name. Inline completion suggests the rest of a line as a developer types, and is useless if it arrives after they've typed it themselves. The chat and agent mode reads the codebase, edits several files and runs the tests, and is dangerous if it runs the wrong command. Both depend on picking the right few thousand tokens of context out of millions of lines of code.
1. The prompt
"Design an AI coding assistant that works inside the editor: inline completions as you type, and a chat that can answer questions about the codebase, edit files and run tests. It's a product used by many companies."
What makes it hard:
- A latency budget of a few hundred milliseconds for completions, including a network round trip and a model.
- Context selection. The model sees a few thousand tokens; the repository has millions of lines.
- Trust. Customers' code is their most sensitive asset, and an agent that runs commands can damage a machine or leak secrets.
2. Frame the problem
How many users?
1 M daily active developers across ~20 K companies; editors on every desktop OS.
Completion traffic is enormous; chat is smaller but each request is much larger.
How fast must a completion appear?
Ideally under ~300 ms from the moment the developer pauses.
Response-time research puts "feels instant" around 100 ms and "flow is kept" up to about a second; a suggestion that arrives after the next keystroke is wasted.
What can the agent do?
Read and search files, propose multi-file edits, run tests and commands in the developer's environment, with approval.
A tool loop with permissions, not free shell access.
Where does the code live?
On the developer's machine and in their company's repositories; we never store it beyond the request unless the company opts in to indexing.
Privacy and retention rules shape caching and indexing.
Which models?
We host our own small completion model and use large hosted models for chat and agents.
Two serving paths with very different latency and cost profiles.
How do we know it's good?
Acceptance of suggestions, code that survives in the file, and agent tasks that pass tests.
Plan the telemetry and evaluation from the start.
3. What it must do
- Suggest completions at the cursor while typing, using the surrounding code and relevant code elsewhere in the project.
- Answer questions and make edits through chat, grounded in the codebase, shown as diffs the developer accepts.
- Run an agent loop: plan, read, edit, run tests, repeat, with the developer approving risky steps.
Not now: code review bots on pull requests, autonomous agents in the cloud working on tickets, training custom models per company.
4. How well it must do it
| Target | Why this number |
|---|---|
| Completions: p50 under 200 ms, p95 under 500 ms after the pause speed | Slower suggestions arrive after the developer has moved on. |
| Useful: suggestions accepted often enough to be worth showing, and accepted code mostly kept data quality | Bad suggestions distract more than no suggestions. |
| Safe agent: no file write or command without the configured approval; no secrets leave the machine security | One destructive command or leaked key outweighs all the saved time. |
| Private: customer code isn't retained or used for training without consent; tenants isolated security | The precondition for any company to adopt it. |
| Degrades quietly: if the service is slow or down, the editor works normally surviving failures | It's an assistant, never a blocker. |
5. Numbers that change the design
| Quantity | Working | → so we… |
|---|---|---|
| Completion requests | 1 M developers × ~4 h typing × a request every few seconds ≈ 3 B a day ≈ 35 K a second, ~100 K at peak | → so a small, fast model on our own GPUs, close to users, with cancellation and caching |
| Completion prompt | ~2 K tokens in (code around the cursor and snippets), ~20–50 out | Prefill dominates → so reuse cached prefixes and keep prompts tight |
| Wasted requests | Many requests are overtaken by the next keystroke | → so debounce, cancel in flight, and stop generating when the client disconnects |
| Chat and agent | ~20 M turns a day, often 20–100 K tokens of context each | Hundreds of billions of input tokens a day → so prompt caching and careful context selection decide the bill |
6. The nouns
- Completion request: file path, language, prefix and suffix around the cursor, plus context snippets.
- Suggestion and its outcome: shown, accepted, partially accepted, or dismissed, and whether the code survived.
- Repository index: code chunks (functions, classes) with symbols and embeddings, per repository and commit.
- Agent session: a goal, a sequence of tool calls (read, search, edit, run) and their approvals.
- Policy: per company: retention, indexing, allowed models, which commands auto-run.
completion_req req_id, user_id, org_id, lang, path, prefix (tail), suffix (head), snippets[], created_at (not stored by default) suggestion_event req_id, shown_ms, accepted (none | partial | full), chars_kept_after_5min (telemetry, no code) index_chunk org_id, repo_id, commit, path, symbol, kind (function | class | …), embedding, text (opt-in, encrypted) agent_step session_id, n, tool (read | search | edit | run), args, approval (auto | user | denied), result_summary org_policy org_id, retention, indexing_enabled, allowed_models[], auto_run_commands[]
7. The contract
POST /v1/completions {lang, path, prefix, suffix, snippets[]} → stream of tokens; client may cancel any time
POST /v1/completions/events {req_id, outcome, kept_chars} → 204 (telemetry, batched)
POST /v1/chat {messages, context: {files, selection, index_hits}} → stream: text · tool_call · diff
Tools run in the editor (client side), never on our servers:
read_file(path) · search(query) · edit(path, diff) · run(command) each checked against the org policy
The editor extension does most of the work that touches code: it gathers context, runs tools, and shows diffs. The server sees only what each request needs.
8. First working version
An editor extension, a completion service with a small model on GPUs in a few regions, and a chat service that calls a large hosted model, with tools executed by the extension.
8.1 A completion
After a short pause in typing, the extension sends the code before and after the cursor (the model is trained for "fill in the middle") plus a few snippets from open files. The model streams a suggestion; the editor shows it as grey "ghost text". Any keystroke cancels the request.
8.2 An agent task
The chat sends the goal and context to a large model. The model asks for tools; the extension runs them locally, subject to policy and approvals, and sends results back, until the tests pass or a step budget is reached. Edits appear as diffs to accept.
9. Make it hold up
9.1 The completion latency budget completions under 200 ms at p50
| Approach | Why | |
|---|---|---|
| ✗ Bad | A large model in one region, a request on every keystroke | Seconds per suggestion, and most requests are obsolete before they finish. |
| ~ Good | A small model, a debounce of ~100–200 ms, cancel on the next keystroke | Far fewer requests, but users far from the region still wait on the round trip. |
| ✓ Great | Plus GPUs in several regions, a persistent connection (no TLS handshake per request), prefix caching of the attention state for the unchanged part of the file, streaming the first line early, speculative decoding, and stopping generation when the client cancels | Each piece removes a slice of the budget. Try them in section 13. |
Also show cached suggestions instantly: if the developer types exactly what the previous suggestion predicted, keep showing the rest of it without a new request.
9.2 Choosing the context useful
- Around the cursor: the prefix and suffix, trimmed to a token budget, nearest lines first.
- From open and recently edited files: snippets most similar to the code near the cursor (a cheap token-overlap score is enough here).
- From the language's structure: signatures of functions and types used nearby, found through the editor's language server or a parser such as tree-sitter.
- A budget with priorities: when everything doesn't fit, keep the cursor neighbourhood first, then definitions, then similar snippets.
For chat, retrieval reaches further: search the repository index (9.3) and let the model ask for more with tools.
9.3 Indexing a repository useful · private
| Approach | Why | |
|---|---|---|
| ✗ Bad | Put whole files in the prompt | Runs out of context after a few files and costs a fortune. |
| ~ Good | Fixed-size chunks with embeddings | Chunks cut functions in half; exact symbol names are missed by vector search. |
| ✓ Great | Chunk by syntax (whole functions and classes), index symbols and keywords as well as embeddings, update incrementally from file changes and commits, and filter by the user's repository permissions at query time | Results are whole, findable by name or meaning, current, and never from code the user can't see. |
Indexing can run locally (small repos, maximum privacy) or on our servers for large monorepos, encrypted per company, only when the company opts in.
9.4 A safe agent loop safe agent
- Tools, not a shell: read, search, edit (as a diff) and run, each checked against policy.
- Approval levels: reads run freely; edits show diffs; commands run only if on the company's allowlist (tests, linters) or after the developer approves. Destructive commands and network access need explicit approval.
- Sandbox when possible: run in a container or dev environment, not on the bare laptop.
- Untrusted input: files, READMEs, issue text and web pages can contain instructions aimed at the model (prompt injection). Treat tool results as data, keep approvals in code, and never put secrets into context.
- Budgets: a maximum number of steps, tokens and wall-clock time per task, and a clear stop when tests keep failing.
9.5 Privacy and tenants private
By default prompts and suggestions are processed in memory and not stored; telemetry records outcomes, not code. Companies can choose retention, regions and which models may see their code. Indexes and caches are keyed by company and encrypted with per-company keys; prefix caches never cross company boundaries. A filter blocks suggestions that match public code verbatim if the company requires it, and a secret scanner strips keys from prompts before they leave the machine.
9.6 Serving and cost completions at 100 K a second
| Approach | Why | |
|---|---|---|
| ✗ Bad | One GPU pool for completions and chat | Long chat prompts block short completions, and completion latency spikes. |
| ~ Good | Separate pools: small model for completions, hosted large models for chat | Latency is protected, but completion GPUs idle at night and overflow at peak. |
| ✓ Great | Continuous batching with paged KV memory (as in vLLM), prefix caching per file, autoscaling per region by queue time, routing to the nearest healthy region, per-user rate limits, and turning completions off gracefully under overload | High GPU utilisation without letting a batch hurt latency; overload degrades to fewer suggestions, not slower editors (Module 08). |
9.7 Measuring quality useful
- Online: acceptance rate, how much accepted code is still there after 30 s and 5 minutes, latency percentiles, and A/B tests for every model or context change. A study of Copilot users found acceptance rate was the best predictor of how productive developers felt.
- Offline: completion benchmarks built by hiding lines in real repositories; for agents, tasks with tests that must pass (SWE-bench-style), run before every model or prompt change.
- Guardrails: rates of approval denials, reverted agent edits, and security filter hits.
9.8 The whole design after the deep dives
10. Interviewer follow-ups
A company with a 50-million-line monorepo complains chat doesn't find the right code.
Server-side indexing (opt-in) with syntax-aware chunks, a symbol index, and a code-graph of imports and calls; retrieval combines all three, filtered by permissions. Let the agent search iteratively rather than relying on one retrieval.
Completions get slow in Asia.
Measure the breakdown (network vs queue vs prefill vs decode). Add a region, or route to the nearest region with capacity; check prefix-cache hit rate there.
The agent ran a command that deleted files.
Commands outside the allowlist must be approved; run agents in containers; keep edits as diffs that can be reverted; log every step. Then add the command pattern to the deny list and an eval case.
A customer forbids sending code to third-party model providers.
Per-company policy routes their chat to models we host (or their own deployment), and the client refuses to send anywhere else.
How do you ship a new completion model safely?
Offline benchmark, then shadow traffic for latency, then an A/B test on acceptance and kept code, then a gradual rollout per region with automatic rollback on latency or acceptance drops.
Offline mode on a plane.
Optionally ship a tiny local model for completions; chat and agents wait for the network.
11. When things break
| Failure | What users see | How we detect it | What limits the damage |
|---|---|---|---|
| Completion region overloaded | Fewer or later suggestions | Queue time, p95 latency | Route to another region; shed load by raising the debounce |
| Hosted chat model down | Chat errors | Error rate | Fallback model; completions unaffected (separate path) |
| Bad model release | Worse suggestions | Acceptance and kept-code drop | Gradual rollout, automatic rollback |
| Index stale | Chat misses new code | Index lag per repo | Incremental updates; agent can read files directly |
| Prompt injection in a repo file | Agent proposes odd steps | Denied approvals, filter hits | Approvals in code, sandbox, no secrets in context |
| Our service unreachable | No suggestions | Client error rate | Editor works normally; extension backs off quietly |
12. Wrap-up and the clock
Trade-offs we made:
- A small model for completions: less clever, but fast enough to be used.
- Tools run in the editor, so code stays local and permissions are the user's own.
- Approvals that slow the agent down, for safety.
- Two serving paths instead of one, to protect completion latency.
What we'd watch: completion p50/p95 by region · acceptance and kept code · cancelled-request share · GPU utilisation · prefix-cache hit rate · agent task success and denied approvals · cost per active developer.
At 10×: more regions, smaller or distilled models, more aggressive caching, and per-company dedicated capacity for the largest customers.
A 60-minute interview
| 0–5 | Frame: two products, latency, privacy, agent powers |
| 5–10 | Must do, how well, numbers |
| 10–15 | Nouns, contract (tools run client side) |
| 15–25 | First version: completion and agent |
| 25–50 | Deep dives: latency (9.1), context (9.2), safe agent (9.4) |
| 50–60 | Serving or quality, follow-ups, wrap-up |
A 90-minute interview
| 0–8 | Frame, including enterprise policies |
| 8–15 | Must do, how well, numbers |
| 15–22 | Nouns, contract |
| 22–35 | First working version |
| 35–75 | Deep dives 9.1–9.7 and the final picture |
| 75–85 | Follow-ups: monorepo, model rollout, no third parties |
| 85–90 | Trade-offs, metrics, 10× |
| Level | What a strong answer shows |
|---|---|
| Mid-level | An editor extension, a completion API with a model behind it, chat with retrieval, debouncing. |
| Senior | A latency budget broken into parts, cancellation and prefix caching, syntax-aware indexing, tools run client side with approvals, separate serving pools. |
| Staff+ | Privacy and tenancy as architecture (what leaves the machine, per-company keys and policies), quality measured by kept code and controlled experiments, agent safety against injection, cost per developer as a design input. |
Common mistakes:
- Treating completions and chat as one service.
- A request per keystroke with no cancellation.
- Fixed-size chunks that split functions.
- Letting the agent run arbitrary commands on a laptop.
- Storing customer code "for debugging" without a policy.
13. Try it, check yourself, read more
Completion-latency lab. A developer pauses typing. How long until the grey suggestion appears, and how many requests does it cost? Change the model, the context size, the debounce and the distance to the GPUs. The target is under 300 ms from the pause.
Illustrative model: prefill at 50 K / 15 K / 5 K tokens a second and decode at 200 / 80 / 40 tokens a second for small / medium / large; a first line is ~15 tokens; a prefix-cache hit skips 80% of prefill. Requests a minute assume steady typing with pauses. GPU baseline = small model, 2,000 tokens, cache on, 200 ms debounce.
What should happen to a completion request when the developer types another character?
Where do agent tools such as run(command) execute?
How should code be chunked for retrieval?
Which metric best shows completions are useful?
| Resource | Why |
|---|---|
| Response times: the 3 important limits (Nielsen Norman Group) | 0.1 s feels instant, 1 s keeps the flow of thought: the budget behind completions. |
| Productivity Assessment of Neural Code Completion (Ziegler et al., 2022) | Which completion metrics track how productive developers feel. |
| Efficient Training of Language Models to Fill in the Middle (Bavarian et al., 2022) | How models learn to complete code between a prefix and a suffix. |
| Fast Inference from Transformers via Speculative Decoding (Leviathan et al., 2022) | A small model drafts tokens, the large one checks them. |
| Efficient Memory Management for LLM Serving with PagedAttention (Kwon et al., 2023) | The vLLM paper: continuous batching and paged KV memory. |
| SWE-bench | Real GitHub issues as agent tasks, checked by the repository's tests. |
| tree-sitter and the Language Server Protocol | Parsing code into functions and finding definitions, for chunking and context. |