The simple problem: unlimited use is not economically unlimited

Some guests booked a car and drove far more than a typical trip. Hosts absorbed the additional tyres, servicing, depreciation and downtime, and some responded by cancelling before the booking began. The customer saw an unreliable marketplace; the host saw a product whose usage promise did not match its economics.

The answer could not be a blanket price increase. Most trips were not extreme, and charging every guest for the tail would damage conversion. We needed understandable limited-kilometre packages alongside an unlimited option, each priced as a different economic promise.

This article is intentionally public-safe. It explains the engineering decisions and lessons without exposing customer data, proprietary prompts, internal identifiers, operational commands or confidential decision thresholds.

From usage data to a product contract
  1. DISTRIBUTIONMeasure distance and duration actually booked
  2. BOUNDARIESFind stable, understandable usage bands
  3. ELASTICITYEstimate response to package and price changes
  4. SCENARIOCompare conversion, cancellation and GBV outcomes
  5. CONTRACTCarry the chosen flex through every pricing layer

Start with the booking distribution, not round numbers

I profiled distance travelled per booking, distance per day, duration, city, car segment, cancellation stage and realised GBV. The centre of the distribution described normal use; the long tail showed where wear and host discomfort concentrated. I examined quantiles and changes in slope rather than choosing a package limit because it looked commercially neat.

A useful boundary had to satisfy three conditions: enough bookings on both sides to estimate behaviour, a meaningful change in host cost or cancellation risk, and a number a guest could understand. Segments with too little data borrowed a broader estimate rather than pretending to have local precision.

Model the response, then admit what is observational

For each candidate package, I built elasticity-aware response models. Conversion and host cancellation were treated as probabilities; GBV was modelled on its own scale. Price change, included kilometres and their interaction were evaluated with duration, lead time, city and calendar controls so a weekend or market-mix shift did not masquerade as package sensitivity.

The output was a scenario surface, not one magical coefficient: if the limited package moved by this amount and the unlimited premium moved by that amount, what range of conversion, cancellation and GBV should we expect? Because historical choice was not randomised, uncertainty bands and sensitivity checks mattered more than decimal precision. The models were used to narrow the decision space, then the release itself remained the strongest evidence.

Turn the product choice into a complete pricing identity

Once the package economics were chosen, flexibility became part of the recommendation key. A row was no longer only city, product and duration; it also identified the kilometre promise. This stopped limited and unlimited values from overwriting one another and let both use the same generation pipeline without hard-coded forks.

The range service expanded accepted behavioural multipliers across active flex packages, applied the relevant adjustment and used the same dimensional identity for storage and Kafka publication. Downstream systems did not have to infer which price belonged to which promise.

Release checks and result

I reconciled every expected city-product-duration-flex combination, duplicates, missing mappings and unaffected products. Economic checks covered positive and ordered ranges, the relationship between limited and unlimited offers, host earning protections and the absence of discontinuities at package boundaries.

The result was more than another fare column. The system could express two clear contracts: a lower price for bounded usage and a premium for genuine flexibility. The design addressed a cancellation and wear problem without making ordinary customers pay for the extreme tail, and it made future package changes a versioned data decision rather than a code fork.