class DeliverySystem:
The round: a phone screen (also reported as an onsite coding round): an in-memory payment system for a food-delivery platform, in three graded parts. Tens of thousands of drivers each have their own hourly rate, and a driver can run several deliveries at once. Several candidates passed with two parts working and the third talked through, though others were rejected after the same; one report says at least two parts are required. Reports from mid-2026 add a rate change to Part 1, and one of them also delivery ids and a 3-hour cap.
Given — what the tests call
The class name and the snake_case names are our setup (reports use both addDriver and add_driver); ask your interviewer:
class DeliverySystem:
def __init__(self): ...
# Part 1
def add_driver(self, driver_id, usd_hourly_rate): ...
def record_delivery(self, driver_id, start_time, end_time, delivery_id=None): ...
def update_driver_rate(self, driver_id, new_rate, effective_time): ...
def get_total_cost(self): ...
# Part 2
def pay_up_to(self, pay_time, prorate=False): ...
def get_total_cost_unpaid(self): ...
# Part 3
def max_simultaneous_drivers_in_past_24_hours(self, now): ...
Times are Unix epoch seconds (integers). A delivery pays rate * (end_time - start_time) / 3600; for example $10.00 an hour for 1h30m is $15.00. The tests print every amount with two decimals, so returning a float, a Decimal or a two-decimal string all pass.
Part 1 — total cost, rate changes, money
add_driver(driver_id: int, usd_hourly_rate: float) registers a driver; record_delivery(driver_id: int, start_time: int, end_time: int, delivery_id: str = None) records a finished delivery; get_total_cost() returns the cost of every delivery recorded so far.get_total_cost() feeds a live dashboard and may be called in a loop: make it fast (the written prompt calls amortised O(1) per call ideal).update_driver_rate(driver_id: int, new_rate: float, effective_time: int) changes the rate from effective_time on. A whole delivery is priced at the rate in effect when it started, and a delivery already recorded is never re-priced. record_delivery ignores a repeated delivery_id, and a delivery longer than 3 hours is ignored (exactly 3 hours counts). In that report delivery_id is the first argument and the total is returned as a two-decimal string; here it is a keyword, so both versions run with one class.One interviewer stressed caching because the total is computed very often, even in a loop, and followed up with more questions on caching and prefix sums; the written prompt says the total feeds a live dashboard and calls amortized O(1) per call ideal. One candidate answered with a hash map of drivers and a running total, analysed the complexity, and was told to move on. Standard answer: keep a running total that record_delivery adds each new delivery's amount to, so get_total_cost returns it in O(1) without scanning deliveries.
One report says the interviewer was very persistent about the time and space complexity of every method; others say the interviewer asked why each data structure was chosen, what its complexity is, and why the code is designed that way. Standard answer: add_driver O(1); record_delivery O(1) (plus O(log r) to look up the rate in effect at the start when rates can change, r = rate changes for that driver) and O(1) space per delivery; get_total_cost O(1) from the running total; pay_up_to amortized O(log n) per delivery paid when unpaid deliveries sit in a min-heap by end time, since each is popped once; Part 3 O(m log m) for the m deliveries in the window.
Two reports say the interviewer specifically asked how start and end times are handled; another says the detail was whether to store timestamps as a double or a long. The written prompt specifies Unix epoch seconds as a 64-bit integer, one-second precision, and 0 < end - start <= 3 hours. Standard answer: keep times as integer seconds (a long in Java), never a double; compute the duration as an integer and convert to hours only when pricing. The 3-hour cap keeps each delivery's amount small.
The written prompt asks for repeated calls to be efficient and suggests a min-heap by end time; another interviewer asked how to optimize Part 2 without asking for code. One candidate proposed per-driver ordered linked lists with binary search and an incremental sum; their AI assistant suggested a list with a pointer instead, and they discussed the trade-offs before time ran out. Another candidate stored every delivery in a priority queue and popped those ending before the pay time. Standard answer: keep unpaid deliveries in a min-heap keyed by end time; pay_up_to pops while the top delivery ends at or before the pay time and adds its amount to a paid total, so each delivery is paid once and a repeated or earlier time pays nothing more. Unpaid is total minus paid.
Four mid-2026 reports add a mid-delivery rate change to Part 1; three name it update_driver_rate(driver_id, new_rate, effective_time). One says a delivery that has already started keeps the rate in effect at its start even if the rate changes later; another gives the full rules: the whole delivery is priced at the rate in effect at its start, with no split pricing, and deliveries already recorded are never re-priced by a later update. Its example: $15.15/h, then $30/h from 04:30; 04:15-05:15 costs $15.15 and 04:40-05:40 costs $30.00. Because a delivery is recorded only after it ends, the latest rate at recording time is not necessarily the right one. Standard answer: keep each driver's rate history as a sorted list of (effective_time, rate) and binary-search it by the delivery's start time when the delivery is recorded.
One report calls Part 3 open-ended and another a bonus, and it is often unfinished: several candidates only described it, and one passed after a short discussion of it. Reports stress that it counts drivers, not deliveries (a driver can have several deliveries at once), that one version asks which time period had the most drivers online, and that one interviewer also wanted the time span returned. One candidate used a sweep line. Standard answer: for each driver, merge their deliveries inside the window into disjoint intervals; turn the merged intervals into +1/-1 events, sort by time with ends before starts at equal timestamps (intervals are half-open), and sweep, tracking the maximum count and where it occurs.
The report says this was asked before coding ended. In that round a test expecting $65.90 got $101.00; the $35.10 difference was exactly one delivery billed twice, so the bug was duplicate billing, not rounding. The candidate's AI assistant had also suggested rounding each delivery to the cent, which hand-checking against the tests showed was wrong. Standard answer: compare the expected and actual amounts and look for a difference that equals one delivery or one rate gap; rebuild the failing case by hand with a few deliveries; check duplicate ids, the rate chosen for each delivery, and when rounding happens, one at a time.
Reported formats vary: HackerRank with AI optional (some interviewers said the AI version is harder, one candidate was told it is the same question either way), or AI required, sometimes with a single skeleton file you copy to your own IDE and back to HackerRank to run its tests. One candidate had 45 minutes of coding. Interviewers ask the time and space complexity of every method, and one asked specifically how start and end times are handled. Two reports say two parts working plus Part 3 described can pass; one says at least two parts are required. In a 2026 report, the interviewer's tests covered several drivers, repeated delivery ids, fractional amounts, overlapping deliveries and deliveries across a rate change; a test expecting $65.90 got $101.00, and the $35.10 difference was one delivery billed twice, not rounding. That candidate's AI assistant also suggested rounding each delivery to the cent, which failed the tests. Before time ran out they were asked how they would keep debugging a failing test.
Also reported as Delivery Billing System · Food Delivery Billing System · Driver Payment Tracking
The round: a phone screen (also reported as an onsite coding round): an in-memory payment system for a food-delivery platform, in three graded parts. Tens of thousands of drivers each have their own hourly rate, and a driver can run several deliveries at once. Several candidates passed with two parts working and the third talked through, though others were rejected after the same; one report says at least two parts are required. Reports from mid-2026 add a rate change to Part 1, and one of them also delivery ids and a 3-hour cap.
Given — what the tests call
The class name and the snake_case names are our setup (reports use both addDriver and add_driver); ask your interviewer:
class DeliverySystem:
def __init__(self): ...
# Part 1
def add_driver(self, driver_id, usd_hourly_rate): ...
def record_delivery(self, driver_id, start_time, end_time, delivery_id=None): ...
def update_driver_rate(self, driver_id, new_rate, effective_time): ...
def get_total_cost(self): ...
# Part 2
def pay_up_to(self, pay_time, prorate=False): ...
def get_total_cost_unpaid(self): ...
# Part 3
def max_simultaneous_drivers_in_past_24_hours(self, now): ...
Times are Unix epoch seconds (integers). A delivery pays rate * (end_time - start_time) / 3600; for example $10.00 an hour for 1h30m is $15.00. The tests print every amount with two decimals, so returning a float, a Decimal or a two-decimal string all pass.
Part 1 — total cost, rate changes, money
add_driver(driver_id: int, usd_hourly_rate: float) registers a driver; record_delivery(driver_id: int, start_time: int, end_time: int, delivery_id: str = None) records a finished delivery; get_total_cost() returns the cost of every delivery recorded so far.get_total_cost() feeds a live dashboard and may be called in a loop: make it fast (the written prompt calls amortised O(1) per call ideal).update_driver_rate(driver_id: int, new_rate: float, effective_time: int) changes the rate from effective_time on. A whole delivery is priced at the rate in effect when it started, and a delivery already recorded is never re-priced. record_delivery ignores a repeated delivery_id, and a delivery longer than 3 hours is ignored (exactly 3 hours counts). In that report delivery_id is the first argument and the total is returned as a two-decimal string; here it is a keyword, so both versions run with one class.Part 2 — pay up to a time
pay_up_to(pay_time: int, prorate: bool = False) marks as paid every delivery that ended by pay_time (end_time <= pay_time). It may be called many times with later times; calling it again with the same or an earlier time must not pay anything twice.get_total_cost_unpaid() returns what is still unpaid. get_total_cost() keeps returning everything ever recorded, paid or not.prorate=True; one test checks each reading.Part 3 — most drivers at once in the past 24 hours
max_simultaneous_drivers_in_past_24_hours(now: int) returns the largest number of distinct drivers (not deliveries) delivering at the same instant within [now - 24h, now).get_total_cost is called very often, even inside a loop. How do you make it fast? (when: Part 1; one interviewer pushed hard on caching, then asked more follow-ups on caching and prefix sums. The written prompt calls amortised O(1) per call ideal)
What is the time and space complexity of each method you wrote?
How do you handle start_time and end_time: what type, what precision?
What type do you use for money, and when do you round?
Why did you choose this data structure?
If a driver's rate changes while a delivery is in progress, which rate applies?
How do you make pay_up_to efficient when it is called again and again, without paying anything twice?
Does paying up to a time include the hours already worked on a delivery that has not finished?
What is the largest number of drivers delivering at the same time in the past 24 hours: drivers or deliveries?
Also return the time span when the maximum held.
If you had more time, how would you keep debugging the tests that still fail?
Walk me through the code the AI wrote: why does it work, and what would you improve?
| Approach | Notes |
|---|---|
| Money: keep amounts exact | a Fraction, or a Decimal at full precision) and round only the total. One report notes that switching to Decimal does not fix anything if you quantize each delivery to the cent. |
| pay_up_to: the written prompt suggests a min-heap by end time. One candidate proposed an ordered list per driver with binary search and running sums | — |
| their AI assistant proposed a list with a pointer, and they discussed the trade-off. | — |
| Part 3: the written prompt recommends merging each driver's deliveries, then a sweep line over the merged spans | — |
| one onsite candidate used a sweep line. | — |
Common mistakes: Rounding each delivery to the cent before adding it up. A mid-2026 report shows $30.31 instead of $30.30 ($15.15 an hour for 1.5 hours plus 0.5 hours), and that candidate's AI assistant suggested exactly this; another candidate's total was two cents off until the interviewer gave a hint.; Storing whole cents: one interviewer said cents must keep their fractional part.; Converting dollars to cents by dividing by 100 instead of multiplying. One candidate lost most of Part 1 to it; the interviewer saw the bug and let them find it.; Pricing a delivery with the driver's latest rate when it is recorded, instead of the rate in effect when it started (mid-2026 version).; Counting deliveries instead of drivers in Part 3 (two reports warn about this).; Processing a start before an end at the same timestamp in the Part 3 sweep (one report).; Blaming rounding for every wrong total: in one round a $35.10 gap was one delivery billed twice.
Interviewer hints: One interviewer stressed that the total is computed very often, even in a loop, so caching matters; the written prompt calls amortised O(1) per call ideal.; One interviewer required double with two correct decimals and would not accept BigDecimal; that candidate hit precision issues and solved two of the three parts.; Two interviewers asked specifically how start and end times are handled.; Interviewers ask for the complexity of every method and why you chose each data structure.; In AI-assisted rounds, interviewers check that you understand the code the AI wrote.
What passers do: Several candidates passed with Parts 1 and 2 working and Part 3 talked through, but others were rejected after the same; one report says at least two parts are required.; One candidate who passed without AI wrote the base part and both follow-ups.; In an AI-assisted onsite, one candidate discussed their understanding, an example and the approach with the interviewer first, and only then had the AI implement it.; One candidate who passed the screen kept a hashmap of drivers and a running total, and explained the complexity.
Why people fail: Running out of time: one candidate blamed constant interruptions from the interviewer, others lost Part 1 to bugs.; Practising a Part 3 that differed from what the interviewer asked.; Fighting the tools in AI rounds: one candidate says HackerRank's built-in AI slowed them down, another had screen sharing break in a browser Zoom.; One candidate guesses that an AI-assisted round needs all three parts; that is their guess, not something they were told.
Edge cases probed: A driver's overlapping deliveries, each paid on its own.; pay_up_to called again with the same or an earlier time must not pay twice.; A delivery ending exactly at the pay time: the written prompt says end <= time, another candidate described end < time.; Mid-2026 version: a repeated delivery id, a delivery over 3 hours, a delivery that crosses a rate change. That round's tests covered several drivers, repeated ids, fractional amounts, overlapping deliveries and rate changes.; A total that lands exactly on half a cent: the report that gives the rounding rule says half up versus half to even was never confirmed.
Send them this page. It is free to read, no account needed.
What you just read — canonical solution, follow-up arc, what passing candidates actually did — exists for all 17 Rippling questions, refreshed monthly from new candidate reports.