Interview guides · Anthropic
Anthropic Software Engineer Interview Guide 2026
Prepare for Anthropic SWE interviews with a tested rate limiter, tier changes, concurrency, distributed-system trade-offs and a focused four-week plan.
Interviewing at Anthropic
- Practice bank: 213+ questions
- Rounds: 4
- Typical prep: 2–4 weeks
- Interview reports: 36
Interview rounds
| Round | Questions |
|---|---|
| HR Screen | 13 |
| Online Assessment | 1 |
| Technical Screen | 60 |
| Onsite | 139 |
How hard is the Anthropic Software Engineer interview?
| Difficulty | Share | Questions |
|---|---|---|
| Easy | 1% | 2 |
| Medium | 61% | 129 |
| Hard | 38% | 82 |
What Anthropic actually tests for
| Topic | Share | Questions |
|---|---|---|
| Coding & Algorithms | 35% | 75 |
| System Design | 22% | 46 |
| Behavioral & Leadership | 20% | 43 |
| Software Engineering Fundamentals | 11% | 23 |
| ML System Design | 9% | 20 |
| Machine Learning | 2% | 4 |
| Analytics & Experimentation | 1% | 2 |
A rate limiter is easy to sketch until the requirements change. Two requests can race for the last slot. A tier downgrade can leave more recent requests than the new limit allows. A network timeout can make it unclear whether an admission was recorded. Those are useful engineering discussions to rehearse for an Anthropic Software Engineer interview.
This revision keeps the original guide's focus on adaptable implementation, systems reasoning, project ownership and mission discussion. It replaces the short rate-limiter sketch with an original runnable exercise, exact boundary examples and checked follow-ups. The mock is preparation material, not Anthropic's implementation or a prediction of your interview.
Use the Anthropic Software Engineer question bank to choose a practice task relevant to your recruiter instructions. Attempt it before opening the solution and record the first requirement change that breaks your design.
What current official guidance confirms
Anthropic's careers page describes remote interviews on Google Meet, live technical environments such as Colab and CodeSignal, and discussion of experience and motivation. It allows looking things up while recommending fluency with syntax and standard libraries. The page does not establish one universal SWE stage count, final-loop length or decision deadline.
The candidate AI policy makes a separate distinction. Preparation is encouraged, and AI can refine an application draft you wrote yourself. Take-home assessments and live interviews do not permit AI unless Anthropic explicitly says otherwise. Permission to consult documentation is not blanket permission for an AI-generated solution.
Confirm your assessment format, language, permitted tools, design scope and any project presentation with recruiting. References and team discussions may be relevant to your process; ask whether and when they apply rather than assuming the earlier guide's sequence is fixed.
The public careers material also emphasizes simple effective solutions, honest communication and responsibility for consequences. Prepare actual examples of a decision, an uncertainty, the evidence you gathered and what you changed. These themes support a useful preparation approach; they are not access to an unpublished scoring rubric.
Original coding exercise: a sliding-window limiter
Allow at most L accepted requests for a user in a window of W integer ticks. At time t, the window is (t − W, t]: an event exactly W ticks old has expired. Rejected requests do not consume capacity. Users have independent limits, and a configured limit of zero blocks admission.
Stage one handles one user. Stage two adds separate users. Stage three changes limits without resetting history. Stage four makes the check safe for concurrent calls in one process. The distributed follow-up is a design discussion beyond the local implementation.
Clarify the following choices before typing:
| Question | Contract chosen for this mock |
|---|---|
| Clock | Nonnegative integer ticks, globally nondecreasing across calls |
| Duplicate timestamps | Valid; each accepted call counts separately |
| Unknown user | Reject with KeyError until configured |
| Upgrade | More capacity immediately, with existing history retained |
| Downgrade | Preserve accepted history; block until count is below new limit |
| Denial result | False and earliest retry tick under unchanged limit and no future admissions |
| Zero limit | False and no calculable retry tick |
This request-count sliding window differs from Anthropic's documented API token-bucket rate limits. The real API has organization and workspace rules and multiple usage dimensions. Do not use this exercise's timestamps, thresholds or retry result as API guidance.
Trace a tier upgrade and downgrade
Set W to 10 and a user's limit to 2. Requests at ticks 0 and 1 are accepted. Tick 2 is denied with retry tick 10; it does not enter the queue. Upgrade the limit to 3, then accept tick 3. The accepted history is [0, 1, 3].
Downgrade to 1. Tick 4 is denied until 13. Waiting until the oldest event expires at 10 is insufficient: [1, 3] still exceeds the capacity. At 11, [3] remains and fills the one allowed slot. At 13 it expires, so a new request is accepted.
Now set the limit to zero and deny tick 14 without clearing the accepted tick 13. Restore limit 1 and deny tick 15 until 23. A tier change must not accidentally manufacture an empty history or a fresh burst.
Runnable local implementation
The invariant is that each user's queue contains accepted timestamps in nondecreasing order. Before an admission decision, remove expired timestamps. Under the same lock, read the current limit, check the remaining count and append only if capacity is available.
from collections import deque
from threading import Lock
class SlidingLimiter:
def __init__(self, window):
if type(window) is not int or window <= 0:
raise ValueError("window must be a positive integer")
self._window = window
self._limits = {}
self._events = {}
self._last_now = -1
self._lock = Lock()
@staticmethod
def _user(user):
if not isinstance(user, str) or not user:
raise ValueError("user must be nonempty text")
def set_limit(self, user, limit):
self._user(user)
if type(limit) is not int or limit < 0:
raise ValueError("limit must be a nonnegative integer")
with self._lock:
self._limits[user] = limit
self._events.setdefault(user, deque())
def allow(self, user, now):
self._user(user)
if type(now) is not int or now < 0:
raise ValueError("now must be a nonnegative integer tick")
with self._lock:
limit = self._limits[user] # Unknown users raise KeyError.
if now < self._last_now:
raise ValueError("clock must not go backwards")
self._last_now = now
events = self._events[user]
while events and events[0] <= now - self._window:
events.popleft()
if limit == 0:
return False, None
if len(events) >= limit:
retry_at = events[len(events) - limit] + self._window
return False, retry_at
events.append(now)
return True, None
For a denied request with H retained events and positive limit L, the next possible admission requires H − L + 1 events to expire. Their zero-based final index is H − L. That is why the retry timestamp is not always the oldest event plus the window, especially after a downgrade.
The return value assumes the limit stays fixed and no intervening request takes the freed slot. It is a useful hint, not a reservation. Two clients can retry at the same tick and only one may be admitted.
The global clock constraint keeps this exercise deterministic. Real callers should not supply arbitrary client wall-clock times to an authoritative limiter. A service needs a trusted time source and a policy for clock behavior; a distributed design cannot compare unrelated process-monotonic origins as if they were one shared timestamp.
Demonstrate the exact boundary
s = SlidingLimiter(10)
s.set_limit("user", 2)
assert s.allow("user", 0) == (True, None)
assert s.allow("user", 1) == (True, None)
assert s.allow("user", 2) == (False, 10)
s.set_limit("user", 3)
assert s.allow("user", 3) == (True, None)
s.set_limit("user", 1)
assert s.allow("user", 10) == (False, 13)
assert s.allow("user", 11) == (False, 13)
assert s.allow("user", 13) == (True, None)
Local verification executed 2,333 checks. Fixed cases include exact expiry, retained history after upgrades and downgrades, zero capacity, separate users, invalid input and backwards-clock rejection. A list-based independent model checks 1,000 generated operations with tier changes, repeated timestamps and advancing time.
In 100 controlled races, two threads attempt the last available slot at the same tick. Each trial verifies one acceptance, one denial, one stored timestamp and termination of both threads. These checks establish local behavior for this class; no Redis, HTTP, API token accounting or employer assessment was performed.
Complexity and limits of this implementation
Each admitted timestamp is appended once and removed at most once, so queue maintenance is amortized constant work per event. Dictionary lookup is expected constant time. But a deque's indexed access can be linear away from an end; calculating a retry after a large downgrade can therefore cost O(H). Do not label the entire method unconditionally O(1).
Stored events are bounded by prior admissions under the limits that applied then, not necessarily by today's lower limit. The implementation retains configured users indefinitely and only cleans a user's queue when that user calls. An inactive user's old records remain in memory. Long-running services need bounded key retention and careful cleanup rules.
The single lock makes configuration changes and admissions easy to reason about, but unrelated users wait for it too. Per-user locks could reduce contention while requiring safe lock creation, deletion and configuration ownership. Choose that complexity only after identifying a workload that needs it. These tests do not measure throughput or contention at production scale.
Distributed follow-up: preserve one atomic decision
Moving the queue behind a Store interface is useful only if its contract preserves correctness. A distributed operation must atomically read the applicable configuration, remove expired records, check capacity and record a successful admission. Separate read-count and append requests recreate the same last-slot race across servers.
One design can use a Redis script over a user's timestamp set and versioned configuration. Redis documents atomic execution of scripts, which also blocks other activity while the script runs. Keep work bounded. For a clustered store, ensure all keys needed by the operation are supported together; atomicity does not magically span independent shards.
Duplicate timestamps require unique event identifiers. If a timestamp is the sole sorted-set member, two accepted calls at the same time can collapse into one entry. Choose a unique admission identifier and store timestamp as its score. Define retention, clock authority and maximum cleanup work rather than translating the deque mechanically.
Atomic execution is also not a durability promise. Discuss failover and lost writes against the required strictness. A response timeout can mean the decision ran but the client did not receive it. Retrying with a new identifier can consume another slot. A request-ID receipt may make admission retries stable, but admitting once does not itself guarantee an external business action happens once.
Configuration changes need an explicit effective point. If limits live in a separate cache with eventual updates, different servers may apply different tiers. Use a shared authoritative configuration revision in the atomic operation, or document the permitted delay. The local set_limit and allow lock represents a single ordering only inside this process.
Outage policy: choose by the protected operation
“Fail open or fail closed?” has no universal answer. A free preview endpoint might accept limited traffic through a separate emergency local cap during a dependency outage. A costly job-submission endpoint might reject new admissions to protect compute capacity. Authentication and authorization checks should not disappear merely because the rate-limit store is down.
State the objective, tolerated overshoot, duration and monitoring. If every server uses an emergency cap of E requests, fleet capacity can reach approximately server-count × E over the window; a local fallback does not preserve a global cap. Label those values hypothetical until the workload is measured.
Rate limits also do not replace queue capacity, cancellation, backpressure or a spend budget. An admitted request may be extremely expensive. For an inference service, reason separately about request count, estimated input work, generated output, concurrent execution and final accounting. Avoid holding the limiter's local lock around a network call or a model run.
Prepare a project discussion and mission answer
Choose a system you actually worked on. Explain its users, constraints, your specific responsibility, the rejected alternative, the measurement and one failure. If a result was estimated rather than observed, say so. If you inherited the design, distinguish the part you changed from the part you only operated.
For a safety or reliability discussion, describe a concrete boundary: permissions, data exposure, rollout control, monitoring or recovery. Explain who made the decision and the cost of the safer option. A rehearsed statement about being interested in AI cannot substitute for a real example of careful engineering.
Prepare a disagreement where new evidence changed your view. State the opposing hypothesis fairly, the smallest useful check and the resulting decision. Use your own experience rather than adopting an invented interview success story. The same habit helps when an interviewer changes a requirement mid-problem: revise the assumption and tests openly.
A four-week preparation plan
This is an original practice schedule. It is not the employer's recruiting timeline, and the workload should change after your baseline reveals a gap.
| Week | Work | Evidence to produce |
|---|---|---|
| 1 | Staged coding in the permitted language | Contract, runnable baseline, boundary tests, two follow-ups |
| 2 | Systems reasoning | Atomic operation, clock model, retry and outage decisions |
| 3 | Real projects and mission discussion | Ownership map, measurement, limitation and changed decision |
| 4 | Mocks and targeted repair | Timed recordings, missed requirements and retested fixes |
In a coding mock, spend the first few minutes on an example and invariant. Implement the baseline, run it, then add a requirement without abandoning earlier cases. In a design mock, draw the critical decision path and one failed-response timeline before adding technology names.
If time is short, prioritize the assessment your recruiter actually assigned and the weakest demonstrated skill. Ask for the next-step window and whether references or team matching remain. Public guide estimates cannot tell you when your application will receive a decision.
Sources and scope
Reviewed October 8, 2026. Official sources establish public hiring and technology context.
- Anthropic careers — public technical-interview and company context.
- Candidate AI-use guidance — preparation, application refinement and assessment permissions.
- Claude API rate limits — actual token-bucket and multi-dimensional API context.
- Redis Lua scripting — atomic execution and its operational boundary.
- Python threading documentation — local lock behavior used by the example.