← Anthropic Interview Insights

Anthropic·Software Engineer·Onsite - System Design / Architecture·Senior

SeniorPrefer not to say
Jun 2026

Summary

System design round at Anthropic for a software engineering role, focused entirely on building a concurrent web crawler from scratch. The question had a lot of sub-parts and went pretty deep into concurrency models, which I wasn't fully prepared for.

Questions Asked (8)

Q1

Design and implement a concurrent web crawler starting from seed URLs, with deduplication, configurable depth and domain scope, and robots.txt support.

System DesignTechnical Trade-offs
Author's notes

This was the whole interview basically.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying requirements (scale, politeness, scope) and then present a high-level architecture with a URL frontier, deduplication, and worker pool. Dive into key components like robots.txt handling, domain-based throttling, and deduplication strategies, discussing trade-offs and potential bottlenecks.

Pro tip: Emphasize politeness and ethical crawling from the start—mention robots.txt, crawl-delay, and rate limiting per domain. This shows you understand real-world constraints and respect for servers, which is crucial for a company like Anthropic.

1. Clarify Requirements and Scope

Ask about scale (number of pages, domains), depth limits, domain scope, politeness policies, and whether dynamic content needs handling. Confirm if it's a distributed system or single machine.

2. High-Level Architecture

Outline components: URL frontier (priority queue), fetcher (HTTP client), parser (HTML parser), deduplication (Bloom filter or hash set), and storage. Explain how they interact and data flow.

3. Concurrency and Politeness

Describe worker pool model, per-domain queues with rate limiting, and robots.txt compliance. Discuss how to avoid overloading servers and handle backpressure.

4. Deduplication and Scope Enforcement

Explain URL normalization, deduplication using Bloom filters or distributed sets, and how to enforce depth and domain scope (e.g., only follow links within allowed domains).

5. Trade-offs and Scalability

Discuss trade-offs: Bloom filter false positives vs memory, politeness vs throughput, and how to scale horizontally (partitioning by domain, distributed queues). Mention monitoring and failure handling.

Key Points to Mention

  • Robots.txt parsing and caching, including crawl-delay directives
  • Deduplication techniques: URL normalization, Bloom filters, and distributed sets
  • Concurrency model: worker pools, per-domain queues, and rate limiting
  • Depth and domain scope enforcement with configurable parameters
  • Handling dynamic content and JavaScript rendering (if required)
  • Scalability and fault tolerance: partitioning, retries, and monitoring

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q2

How would you structure the URL frontier to preserve BFS ordering and per-host fairness?

System DesignAlgorithms & Data Structures
Author's notes

I went with a global BFS queue first and they pushed back on fairness pretty quickly.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the requirements: BFS ordering ensures breadth-first traversal of the web graph, while per-host fairness prevents overloading any single host. Then describe a two-tier architecture: a front queue for priority/ordering and a back queue per host for politeness, with a selector that picks the next host fairly.

Pro tip: Mention that strict BFS is often relaxed in practice to balance freshness and coverage, and that per-host queues can be implemented with a heap keyed by next-allowed-time to enforce politeness delays.

1. Clarify requirements and constraints

Define what BFS ordering means in a distributed crawler context and why per-host fairness is needed (politeness, avoid bans). Discuss trade-offs between strict BFS and practical crawling.

2. Design the URL frontier architecture

Propose a two-tier structure: front queues for priority/ordering (e.g., FIFO for BFS) and back queues for per-host politeness. Explain how URLs flow from front to back queues.

3. Implement BFS ordering

Use a FIFO queue for front queues to ensure URLs are processed in the order they are discovered. For multiple front queues, assign priorities and use a selector to maintain overall BFS order.

4. Enforce per-host fairness

Maintain a separate back queue for each host, with a minimum delay between requests to the same host. Use a selector (e.g., round-robin or heap based on next allowed time) to pick the next host to crawl.

5. Discuss scalability and optimizations

Address how to handle millions of hosts (e.g., distributed back queues, consistent hashing), and how to adapt to dynamic priorities (e.g., PageRank, change frequency).

Key Points to Mention

  • Two-tier architecture: front queues for ordering, back queues for per-host politeness
  • FIFO queues for BFS ordering; multiple front queues with priority if needed
  • Per-host back queues with delay enforcement (e.g., using a heap keyed by next allowed time)
  • Selector algorithm: round-robin or heap-based to ensure fairness across hosts
  • Handling of host resolution and mapping URLs to hosts (e.g., using a hash table)
  • Scalability considerations: distributed queues, consistent hashing, and dynamic prioritization

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q3

How do you prevent duplicate fetches across concurrent workers, and why does the visited check need to happen at schedule time rather than after fetching?

System DesignTechnical Trade-offs
Author's notes

The 'at schedule time' part is the key insight and I nearly missed it.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by framing the problem: concurrent workers can fetch the same URL multiple times, wasting resources and causing duplicate work. Explain that the visited check must be atomic and happen at schedule time to prevent two workers from enqueuing the same URL simultaneously. Then discuss implementation options like a shared visited set with locking or a database unique constraint, and highlight the trade-offs between consistency and performance.

Pro tip: Emphasize that moving the check to schedule time is a form of optimistic concurrency control: you claim the URL before doing expensive work, which minimizes wasted effort. Also mention that this pattern generalizes to any distributed task deduplication scenario.

1. Define the problem

Explain that without deduplication, multiple workers may fetch the same URL concurrently, leading to redundant network calls, wasted compute, and potential inconsistencies.

2. Describe the visited check at schedule time

Detail how the check must occur before enqueuing a URL: when a worker discovers a new URL, it atomically checks and marks it as visited (e.g., using a shared set with locks or a database unique constraint) before adding it to the queue.

3. Explain why post-fetch check fails

If the check happens after fetching, two workers might both fetch the same URL before either marks it visited, causing duplicate work and defeating the purpose of deduplication.

4. Discuss implementation strategies

Mention approaches like a centralized visited set with atomic operations (e.g., Redis SETNX), database unique indexes, or distributed locks, and note trade-offs in latency, scalability, and fault tolerance.

5. Address edge cases and trade-offs

Consider failure scenarios (e.g., worker crashes after marking visited but before enqueuing) and discuss mitigation like transactional outbox or idempotent processing.

Key Points to Mention

  • Atomicity of check-and-set operations to prevent race conditions
  • Centralized vs. distributed visited set and consistency models
  • Trade-offs between strong consistency and performance/latency
  • Idempotency and handling failures (e.g., worker crash after marking visited)
  • Scalability considerations for high-throughput crawling
  • Generalization to other deduplication problems in distributed systems

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q4

Compare async I/O, multithreading, and multiprocessing for a network-bound crawler. Walk through how each handles the frontier, per-host rate limiting, deduplication, retries, back-pressure, and graceful shutdown.

System DesignTechnical Trade-offsAlgorithms & Data Structures
Author's notes

This is where I started to sweat.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying that for a network-bound crawler, async I/O is generally the most efficient due to low overhead and high concurrency, but multithreading and multiprocessing have trade-offs. Then systematically compare each model across the six dimensions, highlighting how each handles frontier management, rate limiting, deduplication, retries, back-pressure, and graceful shutdown.

Pro tip: Emphasize that the choice depends on the bottleneck: if the crawler is truly network-bound, async I/O wins; if parsing is CPU-heavy, multiprocessing may be needed. Also, mention that hybrid approaches (e.g., async I/O with a process pool for parsing) are common in production.

1. Define the problem and constraints

Clarify that the crawler is network-bound, meaning most time is spent waiting for I/O. State that the goal is to maximize throughput while respecting politeness (per-host rate limits) and handling failures gracefully.

2. Compare concurrency models

Briefly describe async I/O (single-threaded event loop), multithreading (multiple threads in one process), and multiprocessing (multiple processes). Highlight their fundamental differences in concurrency, memory, and CPU utilization.

3. Analyze each dimension for each model

For each model, discuss how it handles: frontier (queue management), per-host rate limiting (timing and coordination), deduplication (shared state), retries (error handling), back-pressure (flow control), and graceful shutdown (cleanup and resource release).

4. Summarize trade-offs and recommend

Conclude with a recommendation: async I/O is typically best for network-bound crawlers due to scalability and low overhead, but mention scenarios where multithreading or multiprocessing might be preferable (e.g., CPU-bound parsing, legacy libraries).

Key Points to Mention

  • Async I/O uses a single thread and event loop, enabling high concurrency with low memory overhead; but requires async-compatible libraries and careful handling of blocking calls.
  • Multithreading shares memory, simplifying deduplication and rate limiting, but suffers from GIL contention for CPU-bound tasks and higher memory usage per thread.
  • Multiprocessing avoids GIL and scales across CPUs, but inter-process communication adds complexity for shared state like deduplication and rate limiting.
  • Per-host rate limiting can be implemented with token buckets or timestamps; async I/O can use asyncio.sleep, threads can use locks and timers, processes need shared memory or a coordinator.
  • Back-pressure in async I/O is natural via bounded queues and await; threads use blocking queues; processes use multiprocessing.Queue with size limits.
  • Graceful shutdown requires cancelling pending tasks, draining queues, and releasing resources; async I/O uses task cancellation, threads need join with timeout, processes need terminate and join.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q5

How do you reliably detect that the crawl is finished and not just that the queue is temporarily empty?

System DesignAlgorithms & Data Structures
Author's notes

Classic distributed termination problem.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the crawler's architecture and concurrency model, then explain that a temporarily empty queue doesn't imply completion. Propose a robust termination detection mechanism such as tracking in-flight requests and using a global counter or distributed barrier to know when all work is truly done.

Pro tip: Mention that you'd combine multiple signals (e.g., in-flight count, frontier emptiness, and a grace period) to avoid false positives, and that you'd instrument the system with metrics to monitor termination conditions in production.

1. Clarify the problem and assumptions

Ask about the crawler's concurrency model, whether it's single-threaded or distributed, and how the queue is implemented. This ensures your solution fits the actual system.

2. Identify why an empty queue is insufficient

Explain that workers may be processing URLs that will enqueue new ones, so the queue can be temporarily empty while work is still in progress.

3. Propose a termination detection mechanism

Describe a method such as maintaining a global count of in-flight tasks, using a distributed barrier, or employing a heartbeat/timeout approach to detect when all work has ceased.

4. Address edge cases and reliability

Discuss handling failures, retries, and slow workers; suggest using a grace period or quiescence detection to avoid premature termination.

5. Summarize and validate

Conclude with how you would test the mechanism (e.g., unit tests, chaos engineering) and monitor it in production.

Key Points to Mention

  • In-flight request tracking (e.g., atomic counters or distributed counters)
  • Frontier emptiness combined with no active workers
  • Quiescence detection and grace periods
  • Distributed termination detection algorithms (e.g., Dijkstra-Scholten, token passing)
  • Idempotency and deduplication to avoid infinite loops
  • Metrics and monitoring for termination conditions

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q6

How do you handle transient versus permanent HTTP errors, set timeouts, implement retry with backoff, and bound memory and concurrency under load?

System DesignTechnical Trade-offs
Author's notes

Pretty standard reliability stuff.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by categorizing errors into transient (e.g., 5xx, timeouts, connection resets) and permanent (e.g., 4xx like 400, 401, 404), then explain how you apply different handling strategies. For transient errors, describe implementing retries with exponential backoff and jitter, while for permanent errors, fail fast and avoid retries. Then discuss setting appropriate timeouts, and bounding memory and concurrency using techniques like semaphores, bounded queues, and backpressure to maintain system stability under load.

Pro tip: Emphasize the importance of idempotency keys and circuit breakers to prevent retry storms and cascading failures, showing you understand production-grade resilience beyond basic retry logic.

1. Classify errors

Distinguish between transient errors (e.g., 5xx, timeouts) that may succeed on retry and permanent errors (e.g., 4xx) that should not be retried. Explain how you inspect status codes, error types, and context to make this decision.

2. Implement retry with backoff

For transient errors, use exponential backoff with jitter to avoid thundering herd, and set a maximum retry limit. Mention idempotency to ensure retries are safe.

3. Set timeouts

Configure connection, read, and overall request timeouts to prevent hanging requests. Use context propagation to cancel downstream calls when a timeout occurs.

4. Bound concurrency and memory

Use semaphores, worker pools, or bounded queues to limit concurrent requests and memory usage. Apply backpressure to upstream services when limits are reached.

5. Monitor and adapt

Instrument metrics for retries, timeouts, and resource usage, and use circuit breakers to stop retries when failure rates are high. Continuously tune parameters based on observed behavior.

Key Points to Mention

  • Exponential backoff with jitter to avoid synchronized retries
  • Idempotency keys to safely retry non-idempotent operations
  • Circuit breakers to prevent cascading failures and retry storms
  • Timeouts at multiple layers (connection, read, overall) and context cancellation
  • Bounded concurrency via semaphores or worker pools, and bounded queues for memory
  • Backpressure mechanisms to signal overload and protect the system

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q7

How do you enforce per-host politeness and respect robots.txt crawl-delay without hammering a target site?

System DesignAPI & Integrations
Author's notes

Per-host delay buckets with a last-fetched timestamp.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by framing the problem as a distributed rate-limiting and scheduling challenge, then walk through a design that respects robots.txt crawl-delay and enforces per-host politeness. Emphasize a centralized or coordinated approach to avoid hammering, and discuss trade-offs between throughput and politeness.

Pro tip: Mention that you would treat robots.txt as a cacheable, versioned artifact and use a token bucket per host with a shared Redis backend to coordinate across crawler instances. This shows you understand both the protocol and distributed systems realities.

1. Parse and cache robots.txt

Fetch robots.txt per host, parse crawl-delay and disallow rules, and cache with TTL. Handle missing or malformed files gracefully.

2. Enforce per-host rate limits

Use a token bucket or leaky bucket algorithm per host, with the rate set to the crawl-delay (or a default). Store state in a shared store like Redis for distributed coordination.

3. Schedule requests with backoff

Queue requests per host and dispatch only when tokens are available. Implement exponential backoff on errors (e.g., 429, 503) and respect Retry-After headers.

4. Monitor and adapt

Track response times and error rates per host; dynamically adjust crawl rate if the host shows signs of strain, even if crawl-delay is absent.

5. Handle edge cases

Deal with redirects, multiple hostnames for the same site, and robots.txt changes. Ensure politeness across subdomains if applicable.

Key Points to Mention

  • Robots.txt parsing and caching with TTL
  • Token bucket or leaky bucket algorithm for rate limiting
  • Distributed coordination using Redis or similar for shared state
  • Exponential backoff and Retry-After header handling
  • Default crawl-delay when not specified (e.g., 1 request per second)
  • Monitoring and adaptive throttling based on host response

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q8

Analyze the data structures and synchronization primitives used in your design. How do you prevent deadlocks and starvation, and what are the time and space complexity trade-offs across the three concurrency models?

Algorithms & Data StructuresTechnical Trade-offsSystem Design
Author's notes

I was pretty tired by this point and my complexity analysis was hand-wavy.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clearly defining the three concurrency models you designed (e.g., thread-per-request, event-driven, actor-based) and the data structures and synchronization primitives each uses. Then systematically analyze deadlock and starvation prevention for each, and compare time and space complexity trade-offs, using concrete examples and metrics.

Pro tip: Quantify trade-offs with real numbers (e.g., lock contention rates, memory overhead per connection) and acknowledge that no model is universally best—show how you'd choose based on workload characteristics like throughput, latency, and scalability.

1. Define the three concurrency models

Briefly describe each model (e.g., thread-per-request, event-driven, actor-based) and the core data structures (e.g., queues, maps, buffers) and synchronization primitives (e.g., mutexes, condition variables, atomics, channels) they use.

2. Analyze deadlock prevention

For each model, explain how deadlocks are avoided: e.g., lock ordering, lock-free structures, timeouts, or actor isolation. Mention any residual risks and how you mitigate them.

3. Analyze starvation prevention

Discuss fairness mechanisms: e.g., fair locks, FIFO queues, work-stealing, or backpressure. Explain how each model ensures progress under contention.

4. Compare time and space complexity

For each model, analyze time complexity (e.g., lock acquisition, context switching, message passing) and space complexity (e.g., thread stacks, event loop memory, actor mailboxes). Use Big-O notation and practical overheads.

5. Summarize trade-offs and selection criteria

Conclude with when to use each model based on workload (e.g., CPU-bound vs I/O-bound, scalability needs) and highlight any hybrid approaches you considered.

Key Points to Mention

  • Specific synchronization primitives used (mutexes, semaphores, condition variables, atomics, channels) and their properties (blocking vs non-blocking, fairness).
  • Deadlock prevention techniques: lock ordering, lock-free data structures, timeouts, deadlock detection, and resource hierarchy.
  • Starvation prevention: fair scheduling, FIFO queues, priority aging, and backpressure mechanisms.
  • Time complexity: overhead of lock contention, context switching, message passing latency, and scalability with core count.
  • Space complexity: memory per thread (stack size), event loop overhead, actor mailbox sizes, and garbage collection impact.
  • Real-world examples or benchmarks from your design that illustrate the trade-offs (e.g., throughput vs latency under load).

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.