← DoorDash Interview Insights

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

SeniorPrefer not to say
May 2026Remote

Summary

DoorDash system design round focused entirely on a distributed job scheduler. Dense problem with a lot of moving parts and I felt like I was constantly playing catch-up with the scope.

Questions Asked (5)

Q1

Design a distributed job scheduler that accepts cron-style jobs, fires them at the right time, and runs them on a worker pool. Cover the full picture: CRUD for jobs, cron parsing, retries, run history, and how you handle scale to millions of jobs without missing or double-firing any.

System DesignTechnical Trade-offsData Modeling
Author's notes

This one sprawled fast.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying requirements (scale, latency, consistency) and then walk through the high-level architecture: a job store, a scheduler service that partitions time, and a worker pool. Focus on how to shard jobs by time and job ID to achieve scale, and use a distributed lock or leader election to avoid duplicate firing. Finally, discuss trade-offs between consistency and availability, and how to handle failures with retries and idempotency.

Pro tip: Emphasize idempotency and at-least-once delivery with deduplication at the worker level, since exactly-once is impossible in distributed systems. Also, mention using a time-series database or append-only log for run history to handle high write throughput.

1. Clarify Requirements and Scope

Ask about scale (millions of jobs), latency requirements (how close to scheduled time), consistency needs (no missed/double fires), and job types (cron, one-off). Define SLAs and constraints.

2. High-Level Architecture

Propose a microservices architecture: API for CRUD, a scheduler service that partitions jobs by time buckets, a job store (e.g., distributed DB), a message queue, and a worker pool. Explain how components interact.

3. Cron Parsing and Job Scheduling

Describe how to parse cron expressions and compute next run times. Discuss storing jobs with their next run time and using a time-wheel or priority queue for efficient scheduling. Mention sharding by time to distribute load.

4. Ensuring No Missed or Double Fires

Explain using a distributed lock (e.g., ZooKeeper, etcd) or leader election to ensure only one scheduler instance fires a job. Use idempotent workers and deduplication (e.g., job run ID) to handle retries and at-least-once delivery.

5. Retries, Run History, and Scale

Detail retry policies with exponential backoff, dead-letter queues, and storing run history in a scalable store (e.g., Cassandra, Kafka). Discuss scaling to millions of jobs via sharding, partitioning, and horizontal scaling of workers.

Key Points to Mention

  • Sharding jobs by time buckets (e.g., minute-level) to distribute scheduling load across multiple scheduler instances.
  • Using a distributed lock or leader election to prevent multiple schedulers from firing the same job.
  • Idempotent job execution and deduplication using unique run IDs to handle retries and at-least-once delivery.
  • Storing run history in an append-only log or time-series database for scalability and auditability.
  • Handling failures with retries, exponential backoff, and dead-letter queues for poison messages.
  • Trade-offs between consistency (e.g., strong vs. eventual) and availability, and how to achieve exactly-once semantics via idempotency.

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 storage layer to efficiently find which jobs need to fire in the next few seconds, across millions of scheduled jobs?

System DesignAlgorithms & Data Structures
Author's notes

I went with a time-bucket index, basically bucketing jobs by their next scheduled fire time so you can scan a small window instead of the whole table.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the scale and latency requirements, then propose a time-bucketed storage design (e.g., per-second buckets) that allows efficient range scans for the next few seconds. Discuss how to distribute the load across nodes and handle failures to ensure reliability.

Pro tip: Mention that you would use a distributed, in-memory store like Redis sorted sets or a custom time-wheel structure to achieve low-latency lookups, and highlight the trade-offs between precision and scalability.

1. Clarify Requirements

Ask about the expected number of jobs, acceptable latency for firing, and consistency needs. This ensures the design meets the actual use case.

2. Choose a Time-Bucketed Data Model

Propose storing jobs in buckets keyed by time (e.g., second-level granularity). This allows querying the next few seconds by scanning a small number of buckets.

3. Design for Distribution and Scale

Shard buckets across multiple nodes to handle millions of jobs. Use consistent hashing to distribute load and enable horizontal scaling.

4. Optimize for Low-Latency Retrieval

Use in-memory storage (e.g., Redis sorted sets) or a time-wheel data structure to quickly fetch due jobs. Consider indexing by timestamp for efficient range queries.

5. Address Fault Tolerance and Exactly-Once Semantics

Discuss replication, leader election, and idempotent job execution to handle node failures and avoid duplicate firing.

Key Points to Mention

  • Time-bucketing (e.g., per-second buckets) for efficient range scans
  • Sharding and consistent hashing for horizontal scalability
  • In-memory data stores (Redis, etc.) for low-latency access
  • Time-wheel or hierarchical timing data structures
  • Handling failures with replication and idempotent job execution
  • Trade-offs between precision (e.g., second-level vs. millisecond-level) and system complexity

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

Q3

How do you prevent the same job from being dispatched twice, especially when multiple workers or schedulers are running concurrently?

System DesignTechnical Trade-offs
Author's notes

Blanked for a second here.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the requirements and constraints, such as the need for exactly-once semantics and the acceptable trade-offs. Then, propose a layered approach using idempotency keys, distributed locks, and database constraints, explaining how each layer prevents duplicate dispatch. Finally, discuss how to handle failures and ensure scalability.

Pro tip: Emphasize that true exactly-once delivery is impossible in distributed systems; instead, aim for effectively-once processing by making dispatch idempotent and using unique constraints. Mention that at DoorDash, this is critical for avoiding duplicate deliveries and ensuring customer trust.

1. Clarify Requirements

Ask about the expected scale, latency requirements, and whether exactly-once semantics are necessary. Understand the consequences of duplicate dispatches.

2. Design Idempotent Dispatch

Generate a unique idempotency key for each job (e.g., based on job ID and timestamp) and ensure that dispatching the same job multiple times has the same effect as dispatching once.

3. Use Distributed Coordination

Employ a distributed lock (e.g., using Redis or ZooKeeper) or a database unique constraint to ensure only one worker can dispatch a given job at a time.

4. Handle Failures and Retries

Implement retries with exponential backoff and ensure that locks are released properly. Consider using a state machine to track job status and prevent re-dispatch after completion.

5. Monitor and Alert

Set up monitoring to detect duplicate dispatches and alert on anomalies. Use metrics to track dispatch attempts and successes.

Key Points to Mention

  • Idempotency keys to uniquely identify each job dispatch
  • Distributed locks (e.g., Redis Redlock, ZooKeeper) for mutual exclusion
  • Database unique constraints or conditional writes (e.g., INSERT ... ON CONFLICT DO NOTHING)
  • State machines to track job lifecycle and prevent re-dispatch
  • Trade-offs between consistency, availability, and latency (CAP theorem)
  • Exactly-once vs. at-least-once semantics and the need for idempotent consumers

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

Q4

Walk through how you'd handle retries on job failure without violating exactly-once semantics.

System DesignTechnical Trade-offsAPI & Integrations
Author's notes

The tension between retrying and not double-executing is genuinely tricky.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the system context and defining exactly-once semantics as effectively-once processing via idempotency and deduplication. Then walk through a concrete design: idempotent consumers, unique message IDs, transactional outbox, and dead-letter queues with bounded retries. Finally, discuss trade-offs like at-least-once delivery with idempotency versus true exactly-once, and how to handle poison messages.

Pro tip: Emphasize that exactly-once is a system-wide property, not just a broker feature—focus on idempotent processing and deduplication at the consumer, and mention how you'd monitor and alert on retry storms or duplicate processing.

1. Clarify requirements and define exactly-once

Ask about the system: is it a job queue, event stream, or workflow? Define exactly-once as effectively-once: each job's effect is applied exactly once, even with retries. Mention that true exactly-once is impossible in distributed systems; we aim for at-least-once delivery with idempotent processing.

2. Design idempotent job processing

Ensure each job has a unique ID and that processing is idempotent—e.g., using a deduplication table or upsert with a unique key. For side effects like payments, use idempotency keys with external APIs.

3. Implement retry mechanism with backoff and DLQ

Use exponential backoff with jitter for retries, and cap the number of attempts. After max retries, move the job to a dead-letter queue for manual inspection. Ensure retries don't cause duplicate side effects by checking idempotency before processing.

4. Handle transactional boundaries and outbox pattern

If the job involves database writes and message publishing, use the transactional outbox pattern to atomically commit the state change and the intent to publish. This prevents lost or duplicate messages.

5. Discuss trade-offs and monitoring

Acknowledge trade-offs: idempotency adds storage and latency; true exactly-once may require distributed transactions (e.g., 2PC) which are complex. Monitor retry rates, DLQ size, and duplicate detection to ensure system health.

Key Points to Mention

  • Idempotency keys and deduplication tables
  • At-least-once delivery with idempotent consumers vs. exactly-once semantics
  • Exponential backoff with jitter and max retry limits
  • Dead-letter queues for poison messages
  • Transactional outbox pattern for atomic state and message publishing
  • Monitoring and alerting on retries, duplicates, and DLQ growth

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

Q5

How would you scale the worker pool and the dispatch coordinator as job volume grows?

System DesignTechnical Trade-offs
Author's notes

Pretty standard scaling question at the end.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the current architecture and scaling requirements, then propose a multi-layered scaling strategy that addresses both the worker pool and the dispatch coordinator. Emphasize horizontal scaling with partitioning, asynchronous communication, and trade-offs between consistency, latency, and cost.

Pro tip: Highlight the importance of monitoring and backpressure to prevent system overload, and discuss how you would incrementally scale components based on metrics rather than over-provisioning upfront.

1. Clarify Requirements and Assumptions

Ask questions to understand the expected job volume growth, latency requirements, and current bottlenecks. Confirm whether the system needs to handle bursts or steady growth.

2. Scale the Worker Pool

Propose horizontal scaling by adding more worker instances, using a queue to distribute jobs, and implementing auto-scaling based on queue depth. Discuss partitioning work by job type or tenant to avoid contention.

3. Scale the Dispatch Coordinator

Address the coordinator as a potential single point of failure. Suggest sharding the coordinator by job key, using a distributed consensus algorithm for coordination, or moving to a decentralized dispatch model.

4. Address Data and State Management

Discuss how to handle shared state, such as job status and worker assignments. Consider using a distributed database or in-memory data grid with appropriate consistency models.

5. Discuss Trade-offs and Monitoring

Evaluate trade-offs between consistency, availability, latency, and cost. Emphasize the need for monitoring, alerting, and backpressure mechanisms to ensure system stability.

Key Points to Mention

  • Horizontal scaling with sharding/partitioning of the dispatch coordinator
  • Use of message queues (e.g., Kafka, RabbitMQ) for decoupling and load leveling
  • Auto-scaling worker pools based on queue depth and CPU utilization
  • Distributed coordination services (e.g., ZooKeeper, etcd) for leader election and configuration
  • Trade-offs between strong consistency and eventual consistency in job dispatch
  • Backpressure and rate limiting to prevent overload during traffic spikes

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