Design delivery driver payment system
Company: Rippling
Role: Software Engineer
Category: System Design
Difficulty: medium
Interview Round: Technical Screen
##### Question
Design and implement an in-memory, object-oriented service to track delivery-driver work and compute their payroll. The service must support the following operations (method names vary by interviewer, but the semantics are the same):
1. **add_driver(driver_id, usd_hourly_rate)** — register a driver with an hourly rate.
2. **record_delivery(driver_id, start_time, end_time)** — log a completed delivery work interval for a driver. Times have at least 1-second precision; a single delivery is at most ~3 hours and is recorded only after it has completed.
3. **get_total_cost()** — return the total accrued labor cost across all drivers and all recorded deliveries (independent of what has been paid).
4. **pay_up_to(pay_time)** — settle/mark wages as paid for all work performed up to the given timestamp. Calling it repeatedly with the same (or earlier) time must not double-pay.
5. **get_total_cost_unpaid()** — return the total unsettled wages remaining after payouts.
In your design, discuss and justify:
- **Data models and persistence** — how drivers, delivery intervals, and payout state are stored (in-memory maps for the interview; relational tables for production).
- **Time handling** — units, timezone, endpoint inclusivity, and validation of start/end ordering.
- **Money handling** — avoiding floating-point error, the cost formula, and the rounding strategy.
- **Payout semantics** — whether `pay_up_to` settles a whole delivery (mark-paid when `end_time <= pay_time`) or a partial slice of an in-progress interval (settle up to `min(end_time, pay_time)`), and how idempotency / monotonicity is guaranteed.
- **Overlapping and back-to-back intervals** — how to avoid double-paying overlapping work, and that adjacent (`end == next start`) intervals are not overlaps.
- **Late / back-dated deliveries, edits, and cancellations** — how the system stays correct when a delivery is added or changed after a payout already ran.
- **Performance** — expected time/space complexity of each operation and which indexes/aggregates make them fast.
Provide a clean object-oriented implementation and call out any simplifications you make for the interview versus what a production system would require.
Overview: A Rippling software-engineer system-design screen: build an in-memory, object-oriented driver-payroll service with add_driver, record_delivery, get_total_cost, pay_up_to, and get_total_cost_unpaid. It tests time-interval accounting, integer-cents money math, idempotent partial-vs-whole payout state, double-pay prevention for overlapping work, and a clean path from prototype to a transactional, indexed production design.
Community answers
Answer by prateeksinghal1999
#include
using namespace std;
class Delivery {
private:
string driver_id;
int start_time;
int end_time;
bool isPaid;
public:
Delivery(string driver_id, int start_time, int end_time) {
this->driver_id = driver_id;
this->start_time = start_time;
this->end_time = end_time;
this->isPaid = false;
}
bool getDeliveryPaymentStatus() {
return this->isPaid;
}
void setDeliveryPaymentStatus(bool status) {
this->isPaid = status;
}
int get_start_time() {
return this->start_time;
}
int get_end_time() {
return this->end_time;
}
};
class Driver {
private:
string driver_id;
double usd_hourly_rate;
public:
Driver(string driver_id, double usd_hourly_rate) {
this->driver_id = driver_id;
this->usd_hourly_rate = usd_hourly_rate;
}
double get_rate() {
return this->usd_hourly_rate;
}
};
class DriverManger {
private:
map driver_id_map;
map> deliveries_for_driver;
double total_cost, paid_amount;
public:
DriverManger() {
this->total_cost = 0;
this->paid_amount = 0;
}
bool add_driver(string driver_id, double usd_hourly_rate) {
if(driver_id_map.find(driver_id) != driver_id_map.end())
return false; // already present
Driver *driverPtr = new Driver(driver_id, usd_hourly_rate);
driver_id_map[driver_id] = driverPtr;
return true; // added new
}
int get_hours(int start_time, int end_time) {
double hours = (double)(end_time - start_time) / 3600;
return round(hours);
}
void add_delivery_cost(double delivery_cost) {
this->total_cost += delivery_cost;
}
double getDeliveryCost(string driver_id, Delivery* delivery) {