Free teaser. This page is the free start of the case: the prompt, the scope and the first two decisions. The full case is planned for the study pack, which does not exist yet. See the pack waitlist.
How to read this. This page is the free start of the case. You get the prompt, the scope and the first two decisions in full. The other parts are a short outline. The follow-up questions are my reconstruction of challenges that public prep material reports: offline, old data, battery, rollout. They are not transcripts of real rounds.
TL;DR. My answer: the hard part is not the timetable download. It is telling the user, on every row, how much the app knows. The timetable is one file that the app swaps whole. Realtime data is a short layer on top, and it shows its age. The server decides which disruption is worth a push.
1. Scope and constraints (minutes 0 to 4)
In short: Promise stops, scheduled departures and saved routes offline. Keep trip planning on the server.
A prompt I would expect: "Design an app that shows when my bus leaves, even with no signal." I ask five questions, with a default for each:
- One city or many? One city, one agency feed.
- Trip planning? Yes, on the server.
- Realtime data? Usually, so the app must work without it.
- Offline? Nearby stops, scheduled departures, saved routes.
- Location? Not required.
| In scope | Out of scope | Parked until you ask |
|---|---|---|
| Stop boards (lists of next departures) | Tickets and payment | Planning with no server |
| Saved routes | Walking directions | A live map of vehicles |
| Live, scheduled or stale labels | Reports from riders | A widget or a second phone |
| Disruption alerts, tunnels | Many cities or agencies |
The phone is not one box. It does four things to this design:
| What the phone does | Requirement at risk | Deep dive |
|---|---|---|
| Stops the app process | No half-written timetable | Data (2), structure (3) |
| Delays background work | A current timetable | Structure (3), alerts (4.2) |
| Loses signal, keeps a dead link | A time never lies | The honest row (4.1) |
| Has a battery and data plan | Small downloads, little location | Numbers (2), alerts (4.2) |
Your decision. What do you promise a user with no signal? "Plan any trip", or "the next departures for stops and routes you know"?
First idea, and why it fails. Promise full planning offline, because the tunnel is the main use. It fails on cost. The phone needs a route engine and a bigger timetable file.
What I would say.
Say this. "Offline means the stops and routes you already know. Trip planning stays on the server. Tickets, walking directions and rider reports are out. A live map, a widget and many cities wait until you ask."
The follow-up question I expect:
"Some apps plan trips on the phone."
That is a valid later step. I would want a measure first.
2. Data and contracts (minutes 4 to 14)
In short: The server builds one timetable file. The app swaps it whole. Realtime data stays in memory with its age.
Your decision. Where does the timetable live? Does the phone parse the agency zip, ask the server for every query, or download one compiled file?
First idea, and why it fails. Download the GTFS zip and read it into the app. GTFS (General Transit Feed Specification) is the standard set of timetable text files. Reading a big city's stop times blocks the app. If Android stops the process midway, the database holds half a timetable.
What I would say. The server compiles one SQLite file (a database in a single file). I call it the pack. The app downloads it, checks its hash and schema version, opens it as a new file, and then moves one pointer. Saved routes live in a second database that a swap never touches. I reject patches, because the numbers below do not need them.
The follow-up question I expect:
"A new pack arrives while the user reads a board."
Readers keep the old file. The next query uses the new one.
The follow-up question I expect:
"What about a trip at 25:10?"
GTFS writes times after midnight as more than 24:00:00, on the service day that started earlier. The spec measures them from noon minus 12 hours of that day (GTFS reference), in the agency time zone. On the day the clock changes, adding 86,400 seconds is wrong by one hour. That last point is my reasoning.
The contract has four calls:
- Manifest: version, valid dates, schema version, hash, size.
- Realtime slice for one stop. The raw feed covers the whole network, so the server cuts a slice.
- Journey request, answered by the server.
- Alerts, matched to saved routes by the server.
These are my own assumptions, not measurements:
| Assumption | Arithmetic | What it decides |
|---|---|---|
| 2 million stop time rows at 28 bytes. A zip is 3 times smaller | 2,000,000 x 28 B = 56 MB. Download 19 MB. Peak disk 3 x 56 = 168 MB. Daily download 19 x 30 = 570 MB a month. Weekly 82 MB | Download on an unmetered network, only when the version changes, with 200 MB free. No patches |
| Feed age limit 90 s. The board polls every 30 s | 90 + 30 = 120 s | A live time can be 2 minutes old. A row is stale after 180 s (1.5 times 120) |
| Walking 1.4 m/s. A geofence reports after 3 minutes on average, 6 at worst | 1.4 x 180 = 252 m. 1.4 x 360 = 504 m | A geofence for prefetch needs a radius of 500 m or more |
3 to 5 in short: what the full case covers
In short: Three more decisions follow. Here is each one, and the first idea that fails.
- 3. Client structure (minutes 14 to 22). Who owns the timetable and its updates? The first idea is to let each screen download when it opens. That starts two downloads and can leave a temporary file with no owner.
- 4.1 The honest row (minutes 22 to 31). What does a row say when live data is missing, late or old? The first idea is to treat a missing update as on time. In a tunnel the row would still say, for example, "2 min".
- 4.2 Who wakes the phone (minutes 31 to 40). Does the phone watch for a disruption, or does the server tell it? The first idea is to poll in the background and follow the user with location. Android limits both.
- 5. Break it, ship it, roll it back (minutes 40 to 45). What does the user see when a download stops, the clock is wrong or a release is bad? The first idea is to ship a fix and wait. A store lets you stop a rollout, not recall it.
The full case also has six follow-up questions that each change one constraint. It ends with what changes at mid, senior and staff level.
Get the full case
In short: The full case will be part of the study pack. The pack does not exist yet. You can join the waitlist.
The full case, with the follow-up questions, the failure table, the levels and the build guide, will be part of the study pack. The pack does not exist yet. You can join the waitlist: /pack/.
This page stays free at this address. If the pack is dropped, or six months after it launches, the full text goes live here.
Verification notes (last reviewed 13 Oct 2026)
this is the public teaser of the transport case. It holds the full text of sections 1 and 2 from the private full case, shortened to the outline for sections 3 to 5. The private file is the source and is not changed. Verified: I read the GTFS and Android pages behind sections 1 and 2 on 2026-10-14, as recorded in the notes of the full case. The 25:10 rule comes from the GTFS Schedule reference, which the fetch tool read only in part. The numbers in section 2 are my assumptions, and I recomputed the arithmetic: 2,000,000 x 28 B is 56 MB, 3 x 56 is 168 MB, 19 x 30 is 570 MB, 90 + 30 is 120 s, 1.5 x 120 is 180 s, and 1.4 x 360 is 504 m. Not verified on this page: the outline claims for sections 3 to 5 are not linked, because the full case holds the sources. The claim that a store cannot recall a build, and the Android limits on background polling and location, were checked in the full case but are not re-checked here. The pushes and follow-ups are my reconstruction, not transcripts. No price or launch date is claimed. The 6 month rule comes from MONETIZATION-PLAN.md and is not yet confirmed by the owner. The Basis, levels and failure table are not on this page. The build guide link is left out. Source-check pass, 2026-10-14: I added the GTFS reference link for the noon-minus-12-hours rule and marked an example.
Questions or corrections?
Add them to the discussion thread for this case on GitHub. To post, you need a free GitHub account. What you write there is public and follows GitHub's terms. Good answers are added to this page.
Keep reading
Get the next case study when it is published.
You get an email when a new case study is published, and nothing else.
You can unsubscribe at any time. Privacy.