An xAI software engineer application should make your work inspectable. A claim such as “made search much faster” leaves obvious questions unanswered: faster than what, on which input, with what extra cost, and what did you personally change? Prepare the answers before polishing the claim.
This page covers the broad hiring description and how to build a project evidence packet. The search experiment below is original PracHub teaching material, with actual local code checks and timing results. It is not an xAI interview question, a private scoring rubric or a candidate’s employment history.
![]()
What the current official process description establishes
As checked October 9,2026, the public careers page at x.ai/careers uses SpaceXAI branding. It describes technical-team review of the CV and exceptional-work statement, a short screening conversation with technical/background questions, and deeper technical interviews. It says recruiters generally are not used for assessments. That supports preparing concrete work and technical explanation; it does not establish one global interview count or deadline.
The current London page specifically describes a 5–15-minute engineer screen and practical live coding paired with an engineer rather than LeetCode-style interviews. Keep that location-specific context attached. It is not a promise for every role or office. Ask your contact about the exact role, language, format, project presentation, permitted tools and scheduling.
PracHub’s bank labels and automatic four-round/preparation header organize reported practice material. They are not the official sequence. Do not treat a bank’s percentages as calibrated odds that a question will appear. Likewise, a fixed one-week decision promise or universal leadership round is not established by the general careers page reviewed here.
For an additional coding and memory rehearsal, read PracHub’s xAI technical guide with tested top-k examples. That resource develops counting, tie rules and external-memory choices. Here the different task is to make a project claim reviewable from contract through evidence. Topic overlap alone does not prove search cannibalization.
Build the evidence packet before writing the statement
A useful packet has six small parts: a one-paragraph problem, the exact contract, a baseline, the personal change, verification/measurement records and a limitation with a next decision. It can be a repository, notebook, diagram or sanitized technical note; public source code is not a universal requirement established by the pages above.
Choose real work you are allowed to discuss. A personal project can support a technical conversation, but do not imply an employer deployed it. For collaborative work, identify the component you owned and the dependencies others supplied. If a confidential metric cannot be shared, explain the measurement design and an approved relative result instead of inventing a public figure.
For this teaching packet, the problem is exact AND search over a fixed collection of documents. Repeated scans visit every document for every query. The proposed change builds an inverted index once, so a query can intersect only the matching document-ID sets. This is deliberately narrower than semantic retrieval, ranking or a complete search service.
Packet part 1: contract and hard constraint
Input documents are an immutable-by-contract tuple of frozensets of string terms. Document ID is its zero-based position. A query returns ascending IDs of documents containing all distinct requested terms. Repeated query terms do not change the answer. An empty query returns an empty list, and any unknown term yields no matches.
Pretokenization is given: the exercise does not implement Unicode normalization, stemming, punctuation rules, phrase matching or text ingestion. The contract also excludes live updates, authorization, tenant isolation, network access and durability. That boundary makes the experiment checkable; it does not make the omitted responsibilities unnecessary in a product.
The hard constraint is exact output equivalence with the baseline. A faster result that drops a document is a failed change. The chosen experiment also needs to report one-time construction separately from query execution, because a caller issuing only a few queries may be better served by scanning.
Tiny fixture: predict the IDs first
| Document ID | Terms |
|---|---|
| 0 | red, blue |
| 1 | red |
| 2 | blue, green |
| 3 | red, blue |
Query red AND blue returns [0,3]. Query red AND red returns [0,1,3]; duplication is not frequency filtering. Green AND red returns []. The fixture catches an accidental OR implementation, repeated-term mistakes and unstable output order before any benchmark is meaningful.
Packet part 2: baseline and complete change
def build_index(documents):
# documents is an immutable-by-contract tuple of frozensets of strings.
postings = {}
for doc_id, terms in enumerate(documents):
for term in terms:
postings.setdefault(term, set()).add(doc_id)
return {term: frozenset(ids) for term, ids in postings.items()}
def scan(documents, terms):
wanted = frozenset(terms)
if not wanted:
return []
return [i for i, words in enumerate(documents) if wanted <= words]
def search(index, terms):
wanted = frozenset(terms)
if not wanted or any(term not in index for term in wanted):
return []
lists = sorted((index[term] for term in wanted), key=len)
matches = set(lists[0])
for ids in lists[1:]:
matches.intersection_update(ids)
if not matches:
break
return sorted(matches)
The scan is a fairer baseline than repeatedly parsing raw text: both paths use the same pretokenized corpus. The index maps a term to every document containing it. A query starts with the smallest posting set, intersects the others and sorts the surviving IDs. Starting small reduces intermediate work for selective terms, although it does not eliminate a large result’s materialization cost.
A tempting shortcut is to return the set directly. That changes the ascending-ID contract. Another is to treat an empty query as matching every document because the empty set is a subset of all sets. The explicit guard chooses the exercise’s stated behavior instead of inheriting that mathematical convention accidentally.
The code assumes well-formed, trusted in-process structures and a static snapshot. It is not an input-validation or security boundary. A production API needs its own bounded request validation and current access checks; filtering with a stale or unauthorized index would be a different correctness problem.
Packet part 3: correctness evidence
The local run passed 4060 checks. Ten fixture checks compare both paths against explicit expected outputs. Across 40 seeds, 2000 generated queries over 100-document corpora compare both implementations with an independent oracle that checks each requested term per document. Another 50 queries compare the two paths on the benchmark corpus.
The independent oracle uses per-term membership rather than the implementation’s set-subset/intersection expression. That reduces the chance of simply repeating the same mistaken expression in a test. It still covers a bounded input space; no finite check count proves every possible input or a concurrent service.
Retain the failing input when a check fails, not just a green count. A reviewer should be able to see whether the issue is tokenization, query semantics, ID ordering or index construction. For a real project, include the regression that motivated the change and explain what the previous test suite missed.

Packet part 4: actual measurements, with setup exposed
The recorded run used Python 3.12.14 on Windows, one process, a synthetic fixed corpus of 10000 documents and seed 7601. Each document contains the common term plus 40 distinct terms sampled from a 1000-term vocabulary. The index contains 1001 terms and 410000 term-document memberships. Membership count is not a byte or process-memory measurement.
Each query batch contains 50 queries: forty two-term queries, five common-term queries and five unknown-term queries. Both query paths were warmed. Seven repeats timed a complete batch, alternating which path was measured first. Index construction was timed separately in each repeat. Python timeit disables garbage collection by default during a timed call; these are microbenchmark conditions, not service traffic.
| Timed operation | Best of seven | Full recorded range |
|---|---|---|
| Scan, 50-query batch | 83.63 ms | 83.63–119.34 ms |
| Index, same 50-query batch | 2.10 ms | 2.10–3.98 ms |
| Construct index | 142.45 ms | 142.45–194.29 ms |
The best batch-time ratio is about 39.8. Report it as this local experiment’s ratio, not “production search is 40× faster.” The full timing vectors remain in the backup beside the seed, corpus hash and source hash. Python’s timeit documentation describes the minimum as a useful lower-bound indicator and advises inspecting the vector; the range here is not a confidence interval.
Construction changes the first-use result. Using the recorded minima, build plus one indexed batch costs about 144.55 ms, which exceeds the 83.63 ms scan batch. With the same query mixture and an average per-query saving of (83.6281-2.1019)/50, the simple estimated break-even is 88 queries. That arithmetic is an amortization estimate, not a measured threshold for every query mix.
Five common-term queries return all 10000 IDs, while missing-term queries terminate early. Change the mixture and the ratio changes. Rebuilding after each query can erase the benefit. A strong project account identifies those boundaries before presenting its headline result.
Packet part 5: an annotated statement
The following is an example description of this teaching experiment, not text to submit as your own achievement:
“In a synthetic static-document search experiment, I replaced repeated exact-AND scans with an inverted index while keeping ascending document IDs and empty-query behavior unchanged. Both paths passed 4060 local checks, including independently computed answers. On one Windows/Python 3.12.14 run, the best 50-query batch fell from 83.63 ms to 2.10 ms, while index construction cost 142.45 ms. That makes repeated querying promising under this workload; it does not establish a production SLO or an advantage for one-off queries. My next check would vary query selectivity and measure memory.”
The problem and change are concrete. The check count has a defined scope. The result includes the cost that weakens the headline. The next experiment addresses a real uncertainty. For your application, replace all of it with your actual contribution and evidence; adopting the structure is useful, adopting invented ownership is not.
For a collaborative project, add an ownership line such as “I changed the query path and wrote the equivalence checks; another engineer owned ingestion.” Use it only if true. Keep team impact and personal implementation distinct without diminishing collaborators.

Defend the packet under technical follow-ups
Why not cache every query? A cache can help repeated identical queries, but it needs an invalidation policy and has a different workload dependency. This experiment varies queries and keeps a static document snapshot. Compare caching under a stated repetition rate rather than assuming it replaces indexing.
What is the complexity? A scan checks each of n documents against q distinct terms, with expected set-membership costs and possible early failure. Index construction visits term-document memberships. Query work depends on posting-set sizes, intersections and sorting r output IDs in O(r log r); it is not universally O(q). The index stores those memberships in addition to the pretokenized documents.
What breaks with updates? The static code never changes a posting. A document replacement needs consistent removal of old memberships and addition of new ones. One approach publishes a newly built immutable snapshot and lets existing readers finish on the old one. Rebuild time, retained snapshots and freshness become explicit costs. This design extension was not implemented or benchmarked here.
Does exact keyword search establish answer quality for an LLM? No. It proves a particular retrieval contract. Relevance, supported answers, access control, prompt injection and evaluation data are additional tasks. Avoid turning this narrow search improvement into an unsupported model-quality claim.
What would invalidate the performance decision? A tiny corpus, rare queries, frequent rebuilds, memory pressure or mostly common-term queries could change the trade-off. Measure those conditions and choose the design from the requirements. The most informative answer is often the condition under which your original choice stops being appropriate.
Rehearse the broad loop with two explanation lengths
Prepare a 60-second project account: problem, constraint, personal change, one result and one limit. Then prepare a three-minute version with the baseline and verification method. Keep a deeper trace ready for questions; do not fill the opening with every tool in the repository.
For coding practice, begin with a contract and a small counterexample, then implement and test. For systems discussion, name the state boundary and a failure path before drawing components. For your project, be able to explain one alternative you rejected and what new evidence would make you revisit it. These are editorial preparation habits, not a private xAI scorecard.
A focused week can produce one reviewed packet, two timed implementation sessions, one role-relevant design mock and one skeptical project Q&A. Choose the next session from an observed gap. If the role is frontend, mobile, networking or ML infrastructure, substitute a project from that domain rather than forcing this search exercise into the conversation.
Sources and scope
Checked October 9,2026. Hiring details remain role-specific; the code, fixture, experiment and preparation plan are original editorial work.
- Current x.ai careers page — technical-team assessment, exceptional-work statement and broad process.
- Current London careers page — location-specific screen and paired practical coding description.
- Python timeit documentation — repeated timing, setup and measurement limitations. The experiment itself ran Python 3.12.14.