Search
⌘K

Design and implement Dasher payout calculation system

Asked at:

DoorDash


DESCRIPTION

Design an API that calculates total payout for a driver based on their delivery events. Each delivery has events (accepted, cancelled, delivered) with timestamps, and payment is calculated using fixed rates plus bonuses based on concurrent deliveries. For example, if a driver has 2 ongoing deliveries when accepting a new one, they earn a higher rate for that delivery.

Input:

Events: [
    {id: 'D1', status: 'accepted', time: '10:00'}, 
    {id: 'D1', status: 'delivered', time: '10:30'}
] 
base_rate: $10

Output:

$10 (1 delivery completed with no concurrent deliveries)


Explanation: Single delivery with base rate, no bonus multiplier applied

Constraints:

  • Each accepted delivery has a corresponding completion event (delivered or cancelled)
  • Events are provided with timestamps and delivery IDs
  • Payment calculated per delivery based on concurrent delivery count at acceptance time
  • Handle one driver at a time with mock data from dependent APIs

Understanding the Problem

The core challenge is tracking concurrent deliveries at each acceptance event to determine the correct bonus multiplier. You must process events chronologically to maintain an accurate count of ongoing deliveries (accepted but not yet completed). The payout for each delivery is locked in at acceptance time based on how many other deliveries were active. This requires careful state management to track which deliveries are in-flight and applying the correct rate formula when calculating final payout.

Building Intuition

A naive approach might calculate payout by simply counting total deliveries and applying an average rate, but this ignores the temporal aspect of concurrent deliveries. The better approach is to process events chronologically, maintaining a running count of active deliveries and capturing the multiplier at each accepted event. For example, if delivery D1 is accepted (count=0), then D2 accepted (count=1), then D1 delivered, D2's payout uses the higher rate even though D1 finished first.

This pattern applies to any time-based incentive system where rates depend on current workload or capacity. Real-world applications include surge pricing, capacity-based bonuses, and dynamic rate calculations in gig economy platforms. Understanding event-driven state tracking is essential for building accurate financial systems.

Common Pitfalls

Implementation

Event Processing and State Tracking

Process delivery events chronologically to maintain accurate concurrent delivery counts. Use a state machine approach where accepted events increment the active count and store the current multiplier, while delivered/cancelled events decrement the count. Track each delivery's locked-in rate in a map keyed by delivery ID. For example, when processing {id: 'D2', status: 'accepted'} with 1 active delivery, store multiplier=1.5 for D2, which persists even as other deliveries complete.

Solution
def process_events(events):
events_sorted = sorted(events, key=lambda e: e['timestamp'])
active_count = 0
delivery_multipliers = {}
for event in events_sorted:
delivery_id = event['id']
status = event['status']
if status == 'accepted':
multiplier = get_multiplier(active_count)
delivery_multipliers[delivery_id] = multiplier
active_count += 1
elif status in ['delivered', 'cancelled']:
active_count -= 1
return delivery_multipliers
Payout Calculation Logic

Calculate final payout by iterating through completed deliveries and applying their stored multipliers to the base rate. Only include deliveries with delivered status in the sum, ignoring cancelled ones. The formula is payout = base_rate * (1 + bonus_factor * concurrent_count) where concurrent_count is captured at acceptance time. For instance, a delivery accepted with 2 concurrent deliveries and base_rate=$10, bonus_factor=0.25 yields $10 * (1 + 0.25*2) = $15.

Solution
def calculate_payout(deliveries, base_rate, bonus_factor):
total_payout = 0.0
for delivery in deliveries:
if delivery['status'] == 'delivered':
concurrent_count = delivery['concurrent_count']
payout = base_rate * (1 + bonus_factor * concurrent_count)
total_payout += payout
return total_payout
API Design and Data Integration

Structure the API to accept delivery events from mock dependent services and return total payout for a single driver. Use dependency injection for the mock data source to enable testing and future integration with real APIs. The endpoint should validate that all accepted deliveries have corresponding completion events and handle edge cases like empty event lists or missing timestamps. Return a structured response with total payout and optionally a breakdown per delivery for debugging purposes.

Solution
from typing import Protocol, List, Dict, Any
from dataclasses import dataclass
@dataclass
class PayoutResponse:
total_payout: float
breakdown: List[Dict[str, Any]]
class DeliveryDataSource(Protocol):
def get_delivery_events(self, driver_id: str) -> List[Dict[str, Any]]:
...
class PayoutAPI:
def __init__(self, data_source: DeliveryDataSource):
self.data_source = data_source
def calculate_payout(self, driver_id: str) -> PayoutResponse:
events = self.data_source.get_delivery_events(driver_id)
if not events:
return PayoutResponse(total_payout=0.0, breakdown=[])
self._validate_events(events)
breakdown = self._compute_breakdown(events)
return PayoutResponse(total_payout=sum(d['payout'] for d in breakdown), breakdown=breakdown)
def _validate_events(self, events: List[Dict[str, Any]]) -> None:
accepted = {e['delivery_id'] for e in events if e['status'] == 'accepted'}
completed = {e['delivery_id'] for e in events if e['status'] in ('delivered', 'cancelled')}
if accepted != completed:
raise ValueError('Mismatched accepted and completion events')

What We've Learned

  • Pattern: Event-driven state tracking with temporal locking captures time-sensitive business rules accurately
  • Use Case: Apply to any system where rates/bonuses depend on real-time workload or capacity metrics
  • Design: Separate event processing logic from payout calculation for testability and maintainability
  • Debugging: Store intermediate state (multipliers per delivery) to enable audit trails and troubleshooting

Problems to Practice

Overview
Lesson
Intervals

The Dasher payout system requires processing delivery events with timestamps, which are essentially intervals. This lesson teaches how to work with time-based intervals, sort them, and handle overlapping periods - crucial for tracking concurrent deliveries and calculating bonuses based on the number of ongoing deliveries.

Merge Intervals

medium

Intervals

This problem directly applies to the payout calculation system where you need to track overlapping delivery periods. Understanding how to merge and process intervals helps determine how many deliveries are ongoing at any given time, which is essential for calculating the bonus factors mentioned in the problem.

Employee Free Time

hard

Intervals

This problem involves processing multiple schedules (similar to multiple delivery events) and finding gaps or overlaps. The technique of tracking concurrent intervals across different entities mirrors the challenge of calculating payouts based on how many deliveries a driver has ongoing simultaneously.

Question Timeline

See when this question was last asked and where, including any notes left by other candidates.

Late February, 2026

DoorDash

Mid-level

Late February, 2026

DoorDash

Mid-level

Mid February, 2026

DoorDash

Staff

Warner bros tech round

Comments

Your account is free and you can post anonymously if you choose.