xAI will hand you a blank file and ask you to build an in-memory key-value store with TTL expiry. Forty minutes later, once you have set, get, and expiry working, the interviewer adds a requirement: back the whole thing up, restore it into a fresh process, and make sure a key that had eight seconds left still has roughly eight seconds left after the restore, measured against the new wall clock. That second half is where most candidates stall, and it tells you what this loop is about.
The engineering interview at xAI is almost entirely code you type and run. There is no dedicated behavioral round and no values panel; the “tell me about a conflict” theater simply is not part of the loop. The people reviewing you care about the code you would actually ship, so the questions look like small pieces of real infrastructure rather than LeetCode set pieces. If you have interviewed at OpenAI or Anthropic, the pace and the coding weight will feel familiar. The missing culture rounds will not.
The first call is with an engineer
Recruiters exist, but the first substantive conversation is usually a 15 to 30 minute call with an engineer who asks what you have built and what you want to work on. Treat it as a filter for signal, not a warmup. The strongest thing you can bring is a specific, hard piece of work you did personally: the distributed job scheduler you wrote, the CUDA kernel you profiled, the data pipeline that moved a real number. Vague ownership (“I led the effort to modernize our stack”) reads as a red flag. Name the bug, the constraint, the thing that made it hard.
From that call to an onsite can be under two weeks. xAI schedules fast and expects you to move fast too, which is worth knowing before you ask for a month to prepare.
The TTL key-value store and the backup twist
The in-memory KV store shows up often enough that you should walk in with a clean version in your head. The base problem is easy: a dictionary, plus an expiry timestamp per key.
class Store:
def __init__(self, clock):
self.clock = clock # injected, returns current time
self.data = {} # key -> (value, expire_at or None)
def set(self, key, value, ttl=None):
expire = self.clock() + ttl if ttl is not None else None
self.data[key] = (value, expire)
def get(self, key):
if key not in self.data:
return None
value, expire = self.data[key]
if expire is not None and self.clock() >= expire:
del self.data[key]
return None
return value
Injecting the clock instead of calling time.time() directly is the first thing a sharp interviewer looks for, because it makes the store testable and it sets up the twist. Backup has to store remaining TTL, not the absolute expiry, since the restore happens in a new process at a different wall-clock time. On restore you add the remaining TTL back to the current clock.
def backup(self):
now = self.clock()
out = {}
for key, (value, expire) in self.data.items():
if expire is None:
out[key] = (value, None)
elif expire > now:
out[key] = (value, expire - now) # remaining ttl
return out
def restore(self, snapshot):
now = self.clock()
self.data = {}
for key, (value, remaining) in snapshot.items():
expire = now + remaining if remaining is not None else None
self.data[key] = (value, expire)
Backup silently drops already-expired keys and never writes an absolute timestamp. If you serialize expire_at straight to disk and reload it, every key’s clock is wrong the moment the machine’s time differs, which it always does. Interviewers probe this on purpose, so handle it before they ask.
Rate limiting without hand-waving
API rate limiting is the other problem you should be able to produce cold. The usual ask is a per-client limiter, and the usual mistake is a fixed window that lets a client fire double the limit across a window boundary. A token bucket avoids that and is short to write.
class TokenBucket:
def __init__(self, rate, capacity, clock):
self.rate = rate # tokens added per second
self.capacity = capacity
self.tokens = capacity
self.clock = clock
self.last = clock()
def allow(self, cost=1):
now = self.clock()
self.tokens = min(self.capacity,
self.tokens + (now - self.last) * self.rate)
self.last = now
if self.tokens >= cost:
self.tokens -= cost
return True
return False
Be ready to talk through what changes when the limiter runs on many machines. A single in-process bucket does not survive horizontal scaling, so the follow-up is usually “now put this behind Redis,” and you should know the atomicity problem. A read, then a modify, then a write on the token count races unless you push the whole update into a Lua script or use an atomic operation with a refill scheme. Say that out loud even if you do not write it.
kth element, then “make it parallel”
Selection questions here rarely stop at the textbook answer. Finding the kth largest element with a heap or quickselect is table stakes. The real question is the follow-up: the input is 400 GB and does not fit in memory, or you have 64 cores and want the work spread across them. Now you are talking about partitioning, a bounded min-heap of size k so memory stays at O(k), or an external merge sort with sorted runs written to disk. Quickselect gives you average O(n); the interviewer wants to hear you name the O(n^2) worst case and how a random or median-of-medians pivot bounds it.
The parallel angle is where xAI’s actual work leaks into the interview. This is a company training models on a cluster with more than 200,000 GPUs, so “how does this scale across cores and machines” maps directly onto the day job. Reason about the merge step, contention on shared state, and where coordination cost eats your speedup.
Iterators, streams, and flattening nested data
A cluster of problems tests whether you can process data you cannot hold all at once. Common ones: merge k sorted streams lazily, build an iterator that flattens an arbitrarily nested list without recursion blowing the stack, or write flatten and unflatten for nested dicts so that {"a": {"b": 1}} round-trips to {"a.b": 1} and back. The unflatten direction is the one people botch, because you have to rebuild intermediate dicts key by key and decide what happens on a collision.
Short example prompts that have shown up:
- Implement a trie-based tokenizer that matches the longest known token at each position in the input.
- Write an iterator over a nested list that supports
next()andhas_next()in true iterator style, not by flattening everything up front. - Merge k sorted iterators into one sorted stream using a heap of size k.
What the loop looks like end to end
| Stage | Format | What it tests | Typical length |
|---|---|---|---|
| Engineer screen | Call with an engineer, no coding | Depth of your hardest personal project and technical interests | 15 to 30 min |
| Technical screen | Timed coding in a shared editor | One infra-flavored problem, run and passing tests | 45 to 60 min |
| Onsite coding (2 to 4 rounds) | Engineer-led, build and debug live | TTL store, rate limiter, kth element, iterators, correctness under edge cases | 45 to 60 min each |
| Systems or ML design | Whiteboard or shared doc | Throughput and latency targets, batching, caching, failure and rollback plans | 45 to 60 min |
| Hiring manager | Conversation | Fit for the specific team, scope of the role, how you work | 30 to 45 min |
Notice what is absent: no take-home that eats your weekend, no separate values interview, no puzzle round about manhole covers. The whole loop points at the same question, which is whether you can build and debug real code at speed while an engineer watches.
What “code you would ship” means in practice
Grading leans on whether your code would survive a real system, not on whether you found the optimal solution. Inject your clock. Validate inputs. Say what happens on the empty case, the duplicate key, the negative TTL, the concurrent writer. Before you type, state your complexity and memory budget, then hold yourself to it. When a function is done, run it against a case you expect to break and fix it in front of them, because debugging live is a signal they want to see rather than a slip to cover.
A useful drill before your onsite: take the TTL store and the token bucket, write them from memory, then try to break your own code with a negative TTL, a zero-capacity bucket, a clock that jumps backward. If you catch the bug before an interviewer points at it, you are working the way xAI grades. The people who clear this loop tend to treat a 45-minute prompt like a small pull request they would sign their name to, right down to the one line that pages someone at 3 a.m. if it is wrong.
Practice the behavioral round:
