Section 2: Writing the Contract - Requirements (Functional & Non-Functional)
Executive Summary: This section converts Section 1's clarifications into promises a test could verify. We pin the exact meaning of "active between" (half-open events, a closed window, open events included, point queries for free), define the read surface (city-scoped listing, pagination, counts, async exports) and the ingest surface (start, end, correction), and give every non-functional requirement a number, from latency per query class to 5-second freshness and zero loss of acknowledged events. The most important line is the correctness contract, no misses as of as_of, possible duplicates across pages, because it will shape the read and write paths more than any latency target does.
The interviewer caps the marker: "Good questions. So what exactly are we promising?" The interviewer wants requirements someone could be paged on. "Fast" and "accurate" are decoration. "The 'now' query returns its first page in 100 ms at p99, and never omits an event committed before its as_of" is a commitment every later section has to deliver on.
The Functional Contract
Semantics come first, because Section 1 showed the obvious query gets them wrong:
- Events are half-open intervals
[start, end). A ride that ends at exactly 17:10:00 was not active at 17:10:00. Back-to-back rides by one driver hand off cleanly. - The query window
[qs, qe]is closed, andqeis clamped to now. An event is active in the window iffstart <= qe AND (end IS NULL OR end > qs). - Open events count. A ride still in progress is returned with
end_ts: null. - A point query is just
qs = qe. "Who was riding at exactly 17:20:00?" needs no separate endpoint.
Run the running example through those rules. The safety-ops query is SF, 17:10:00Z to 17:40:00Z, executed at 17:45:00Z:
| Ride | Interval | Verdict | Rule that decides it |
|---|---|---|---|
Maya's trip_8f3a |
16:52:10Z – 17:31:45Z | Included | start ≤ 17:40 and end > 17:10. The naive start BETWEEN query misses it |
trip_c41e |
17:38:20Z – (open) | Included, end_ts: null |
start ≤ 17:40 and end IS NULL |
trip_77d0 |
… – 17:09:59Z | Excluded | end ≤ qs |
Then the operations:
- List active events: required
city, required[qs, qe], optionaltypefilter. Returns full event records, open events included. - Paginate: default and maximum page size 1,000, using an opaque cursor. SF's busy half hour returns ≈ 25,000 rides, which is 25 pages.
- Count: the same parameters with no payload.
- Async export: anything past the synchronous ceilings (more than 250K estimated rows, a window longer than 7 days, or a lookback beyond 90 days) becomes a job rather than a request.
- Ingest: accept start, end and correction for each event. About 5% of events get a later correction (an end time adjusted, or a cancellation), so corrections are a normal input, not an edge case.
- Generic event types: rides are the first tenant, but the contract is keyed by
type, so Eats deliveries or scooter rentals can join without a new service.
The Pushback: "Why make city mandatory? Ops will want 'all rides worldwide, right now.'"
You: "Worldwide at peak is about a million open rides. That isn't a list anyone reads. It's an aggregate, and aggregates over time get a separate stream of +1 on start and −1 on end, per city per minute, written into an OLAP store. Scanning intervals to answer 'how many, every minute, worldwide' is the wrong tool. A mandatory city also lets each city live in one home region. A real multi-city list goes through the export path."
Quick check before moving on:
Sign in to continue reading
The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.
Sign in to read the full lesson