The Development Itinerary III: Built Around a Real Trip

By Miltos Vafiadis

Share:

Διαβάστε το και στα ελληνικά.

This is Volume III. Volume I maps iterations 1–24, from the first commit to a working product; Volume II maps 25–49, from quiet beta to public launch.

Volume II ended on 11 September with a third loyalty program live and a lesson about second pairs of eyes. Five weeks later the odometer reads 73 iterations, and the thing that shaped this volume wasn't a feature request. It was a plane ticket. I had a real trip to London booked for 2 October, and most of what follows was built because I wanted the app to carry that trip — then rebuilt because the trip disagreed with the app.

Volume I was "how do I build this." Volume II was "how do I know this is true." This one is "what happens when the person who built it has to live with it for a week." Here's the route.

The Development Itinerary, Volume III — iterations 50 to 73 of PlanItShare as a vertical travel itinerary, grouped into six legs

Leg 1 — Landing times and connections (iterations 50–53)

Will It Be Late? arrived at the end of Volume II answering one question: is the aircraft that will fly my leg running late somewhere else right now? Leg 1 is where it learned to answer the question people actually have — will I make my connection?

It started with an evening of real flights. I picked a set on purpose: a Ryanair flight a friend was on, my brother's routing, a codeshare, a once-a-week flight, some US carriers, and Aegean departures I could watch on Flightradar24 during a thunderstorm over Thessaloniki. Fixtures and mockups had found nothing wrong. Real flights found a runway time printed under gate words, a time zone label printed three times, and an arrival time on the wrong clock. Then they found the evening's best mistake, and it wasn't in the data: my AI pair-programmer concluded "it's the airline" after Aegean out of London and Lufthansa out of Singapore both came back without a tail number. I opened Flightradar24, which had both tails, and asked whether it was the data provider instead. It was. The experiment had varied two variables and held the real one constant.

Iteration 51 then collapsed the whole card to one number and one confidence word, because the rule from the top was: this tool is not for aviation geeks, it's for the frequent flyer who cares about fifteen minutes and the traveler with a tight connection. Under that number sits the connection check: our pessimistic landing time against the hub's published minimum connection time for that exact pair of terminals, with a bus or train between them stepping the verdict down a band. By iteration 52 it covered 39 Star Alliance hubs, each with a readable page a traveler can open — because when I asked "can anyone actually read this curated data?", the honest answer was no.

Also in this leg: a trip can now say whose miles it earns. A loyalty program used to be a column that couldn't say "no"; now it can, and one button compares a flight across all three programs, best first, with no red anywhere — a second-best program is not wrong.

Leg 2 — One rule for the desks (iterations 54–56)

Volume II opened the curation desks to volunteers. Leg 2 is where I noticed the desks didn't agree with each other on the most basic question: when does a curator's change reach a traveler? The loyalty desk said "after review." The hub tables said "immediately." The countries desk let a single curator publish alone.

So one rule, written down and then built: a curator's change takes effect only after a superuser approves it; a superuser's own change is live and recorded; every change keeps the row before and after. Sixteen kinds of write now go through one gate. Approving a proposal re-reads the row first — if it changed in the meantime, the proposal goes stale rather than silently applying to something it was never written against.

Iteration 56 added Restore on top, and it taught me something about my own specs. The spec said Restore would be cheap "because the before is kept by every proposal." That sentence was half true: a superuser's write makes no proposal, and three of the sixteen actions kept no "before" at all. It had been written with the path we'd just built in mind, not the one that already existed — and I approved it. The fix was to build the record first and Restore second.

One traveler-facing thing from this leg that I'm fond of: Smart Search's fare choices were renamed what-ifs. The row sends you to Google Flights, which may show a different fare or seller, so "if you book Aegean as Flex…" is the only honest phrasing. And a button that turned a traveler's guess into a site-wide alias was removed outright — a what-if is not evidence of what an airline's brand name means.

Leg 3 — Plans that change their mind (iterations 57–60)

Until iteration 57, a trip's data began after the deciding was over. A forwarded confirmation, and nothing about what it would cost to undo. For the London trip I wanted the other half, so every plan gained a collapsed Price and cancellation section, with terms as steps: until this moment, cancelling costs this. The trip page answers "what happens if we cancel?" per currency, never summed across them. And the parser was already volunteering cancellation policies as free text from forwarded emails, so it now reads them as structured steps — marked "from the email, check it" until a person saves them.

Marking a booking cancelled asks what it cost and whether you were refunded. It never asks why, because that's where health data would arrive. When every booking is cancelled, the trip offers once to be called off, and a called-off trip can come back.

Iteration 58 began with a question rather than a task: if a trip has only me on it, who votes? The answer became the rule for everything after it. A traveler is a member with an account who is actually going. Travelers vote; everyone on the trip can comment, including a trip of one, where a comment is a note to yourself. Two or more travelers make a group trip, which gets a vote row; fewer make a personal trip, which doesn't. Plans can now be options the group weighs — dashed on the itinerary, voted yes / maybe / no by name, made firm or set aside — and an option counts as no part of the trip: no country card, no delay check, no expense.

Iteration 60 put a comment thread on every plan and gave organizers a page called Where the trip stands. It was built in a cloud session that couldn't sign in anywhere, shipped unseen, and my first click crashed the page. The handoff put it plainly: "lint and build green" had been offered as if it meant "works."

The same iteration found a bug that had been shipping since 58: a vote nobody could take back. Withdrawing your vote after leaving a trip failed silently, because a delete only reaches rows the read policy also passes, so it matched nothing and reported success. The lesson went straight into the teaching material: test the promise from the seat it was made for, and make the test fail once on purpose.

Leg 4 — Getting around a city (iterations 61–63)

Iteration 61 was not the planned iteration. I tried to add a Thames river bus to the London trip, and every answer raised the next question. By the time it closed, the app had a worldwide City transport plan type, journeys with stops, changes and walks, and London journeys planned by Transport for London's own planner — with a rule I had to state twice: TfL must never leak into the rest of the world's pickers. Local providers appear only on a trip whose places fall inside their area.

My own Heathrow-to-West-Silvertown plan found most of the defects. Mapbox sent "London Heathrow Terminal 2" to Tennessee. TfL's planner read the Heathrow hub id as Camden Town. A batch of 25 stop lookups got refused because the limit is 20. None of it appeared in fixtures, because the fixtures were written from the shapes we expected.

Iteration 63 is the one I'd teach from: the second provider is where the first one's assumptions show. London's planner had been designed to be "one registry entry away" from any city. Adding Stockholm turned TfL's currency, time zone, the country of its stations, which mode filters it honors, and even the Greek preposition into fields. That refactor was the real deliverable; Stockholm, New South Wales and Switzerland are what it made cheap. Switzerland also answered a question of mine — "shouldn't this appear in more plan types?" — with a Swiss reason: intercity trains, PostBuses and lake boats are walk-on, so there they appear on Train, Bus and Boat plans too. And Swiss stations carry no country, because their ids put Liechtenstein's buses in Switzerland. An absent card states nothing; a wrong one names a country.

Leg 5 — The real trip (iterations 64–68)

Five iterations in five days, each reshaped by one real record.

Iteration 64 paid down a security pass that was ten iterations owed (the handoff had said six; the previous report's own diff range said ten — count from the record, not from the story). Its worst finding: re-reading a forwarded email deleted and re-filed every plan it had ever made, which, now that plans carried votes, comments and cancellations, erased all of it. Same iteration, a correctness bug found on the way: a price of 19.99 had been refused for five days, because a rounding check that looks obviously right is false for it. And a new Home: your next trip as a boarding pass, your numbers beside it, tiles for what you haven't tried yet.

Then a real email arrived. Aegean reissued my ticket after an upgrade, and the importer filed my four flights a second time. The diff found six real changes beside a dozen that only read differently, and three things a naive replace would have destroyed. Two iterations later, booking changes went live: the email is held, compared against the stored flights — never against the old email — and only what you approve is applied, with Undo on every flight. The acceptance test was the same reissue, re-sent. Then the same email arrived a second time, was compared against the already-updated row, and would have made the upgrade's class the fare the miles are earned on. The check now replays the merge against its own output. Airlines resend; an update right the first time can be wrong the second.

Iteration 66 also brought weather onto every trip day, for where the day ends, with amber for strong wind and heavy rain because that's what delays a connection. The real Athens forecast for my 2 October showed 77 km/h gusts the same day. Iteration 68 added hour-by-hour on tap and forecast-based packing on the Prep list — which, on first contact with the trip, packed a windproof layer for a two-hour wait indoors at Athens airport. Fixed: only where each day ends counts.

And then Bring It Home: five typical things to take home from a place, and where locals buy them, on the same engine as the B-Side. The spike script took Mapbox's first result for "Chiang Mai" and produced a flawless list for Chiang Rai. An engine is reusable; the judgments wired into it are not.

Leg 6 — Fast, then personal (iterations 69–73)

A friend who builds software for a living said the site "feels slow." One response header explained it: the code ran in Washington while the database sat in Ireland, and every query crossed the Atlantic twice. Back in iteration 67 that moved the functions to Dublin and turned the trip page's eighteen sequential waits into three stages. Iteration 69 then worked through twenty more audit items, each with a number before and after, and put the public pages on the CDN; 70 did the same for the blog. The trip page went from 1,075 KB of JavaScript to 598. Public pages now arrive in about 0.13 seconds.

The blog move surfaced the quietest bug of the volume. The "forget these pages" calls from iteration 69 had named the route without its folder, so they matched nothing — compiled, ran without error, and refreshed nothing. A call that forgets by key succeeds whether or not the key exists.

Iteration 71 came home from London with three specs under one idea: a trip is not one journey everyone takes together. A long layover now asks "going into the city?" and files an ordinary plan if you say yes. "Missed or rebooked?" exists because I missed A3 608 on 2 October and Aegean sent nothing worth forwarding. And a plan can be for just some of the travelers — which iteration 72 then made every fact-reader respect. Before it, a reunion whose first booked flight came from London had three friends starting in Greece "visiting" Greece, and all five asked about Serbia, which two of them drive through. Now Prep asks each person about their own route, and country cards say who stays, goes, or passes through.

The night 72 shipped, other people used it. Two friends' sign-up emails landed in Junk because the domain had no DMARC record. Reading the admin page about a tester showed her two co-owners with "0 trips," and the same look showed everyone's AI usage had been frozen since 1 October — one read had stopped silently at the database's thousand-row page. And the tester, adding an AI plan, filled the whole form, was sent "over to The B-Side" to answer its questions, and came back to retype everything. Her one evening produced four fixes that no fixture, review or walk of mine could have.

So iteration 73 was built the other way round: from the data first. Of the first eleven people playing Been There Done That, seven had ticked countries only, and every axis of the radar counted cities or places, so their chart was a dot whether they'd seen one country or fifty-six. A Traveller view now counts what people actually do — continents, the ten most-visited countries and cities (ten, because that's what the source publishes), the hundred-country mark. And both games gained Sync with my past trips: you already told the app where you went, by planning the trip; the games just never asked. Then a walk through a brand-new account, at phone width, in Greek, found "0%" for a first country on the very screen meant to make people feel good. It now reads "<1%."

The numbers

Twenty-four iterations in thirty-two days, versions 1.50 to 1.73. Thirty-nine Star Alliance hubs with curated connection data, each with a public page. Five cities and one country whose transit planners are wired in: London, Stockholm, Sydney, Paris, Singapore, and all of Switzerland. One real trip, five days long, that produced more fixes than any spec in this volume. The trip page down to 139 KB from 460. Public pages served in about 0.13 seconds. One forecast that was "high confidence" two and a half hours out and wrong by 93 minutes, because of an air-traffic slot nobody could see that early. One price, 19.99, refused for five days.

Four things this volume taught me

Compare with the record, not with the previous message. Three times this volume a comparison was right about the wrong thing: an email against the older email instead of the stored plan, a security debt counted from a handoff instead of a diff, a walk read from a browser snapshot that lagged the page. The stored row is the only thing that doesn't lie about now.

A probe you misread is a fixture you will write wrong. Every transit provider surprised us on the first live call — a "no journey found" that was a 404, a planner that ignored its own date, a bus that had already left. Record the real answer first; type the fixture from it, never from the docs.

Real usage by different people finds what no walk can. Fixtures passed the review's bug; the review passed the walk's four; the walk could never have found the tester's four, because every one of them lived in a first time or in someone else's seat. So now I walk as a first-timer on purpose, and I read admin pages about other people rather than about myself.

The fix you're sure of can be the wrong fix. Bangkok's airport kept offering its suburb as a city. The obvious fix was the wrong one, and only repeating the exact step proved it. When a bug comes back, suspect the diagnosis before the patch.

The whole volume on one page

For sharing, and for the wall:

The full 24-iteration Volume III timeline as a single horizontal poster, grouped into six eras

There's a boarding pass for every iteration too — the deck now runs to all seventy-three — which I'll keep posting as the journey goes on.

Want to come along? Come aboard — it's free. If you'd rather help build it than just use it, the beta program and the curator desks are both open from your dashboard. And if you're going somewhere soon, take the app with you. Nothing finds the rough edges like a real trip.

Comments

No comments yet — be the first.