
Talk · Jun 2026 · AI Tinkerers Doha, Doha
Lessons from vibe coding TrainTimesUK
My talk at AI Tinkerers Doha, run by Scale AI and QSTP. AI gets you to 80-90% fast, but scaling something stable is the hard part. This is how I built TrainTimesUK for my own commute, and took its weekly fetch-error rate from 4.1% down to 0.6%.
It started with "Trash. Slop."
Before TrainTimesUK, I built a car site called AutoScout and put it on Reddit. Car people were blunt: eight features, none of them past 80% reliable. I had five or six features on the go at once. It felt like progress, but it was the illusion of productivity.
Shipping was right. Moving on to the next feature before I understood the problem was not. I'm not alone in that. Pendo measured 615 products and found 80% of features are rarely or never used.
So I took those lessons into TrainTimesUK and built fewer features, with far more focus. I used it on my own commute every day and hit every wrong platform and failed load myself.
One person, a small stack
I built it on my own, with no microservices: a Vite app on Vercel, a Supabase backend, and the whole product hanging on two train-data APIs.
- FrontendReact and Vite, with TypeScript, Tailwind and shadcn/ui. TanStack Query for live data and React Router for the 2,600 station pages.
- BackendSupabase: Postgres, edge functions and pg-cron. It caches the boards, runs the warmers and tracks API quota.
- HostingVercel: a static build plus one serverless proxy that keeps the keys server-side.
- InsightPostHog logs the latency, cache tier and errors of every fetch. Playwright guards against regressions.
- AgentCodex as the implementation agent and review partner, with GitHub as the rollback trail.
Two APIs and one hard lesson
National Rail's Darwin API is free and always on, at about 1.7 seconds. It gives times, status, destinations and most platforms. RealTimeTrains has the rich data, like exact platforms and live movement, but it's rate-limited to 9,000 calls a day and takes 6-19 seconds for a busy board.
At first I relied on RealTimeTrains alone. The agent kept hammering the quota until the board went down, and the model never warned me. I found out the hard way. Now Darwin is the backbone, and RealTimeTrains only adds detail behind a circuit-breaker.
Prompting with evidence
I stopped asking the model to guess product truth from memory. Before it suggested a fix, I gave it the evidence: API limits, latency traces, cache paths, error rates, session replays and failing tests.
My prompts went from vibe to spec in three steps. "Fix the platform issue" got plausible code and a confident story. Naming the files, the failing path and the API limit made the output smaller and safer. Adding the PostHog traces, the Playwright failure and the expected outcome, then asking for the smallest patch and how to verify it, is what worked.
For the hard fixes I ran the same prompt across frontier models. A deep reasoning model gave the best diagnosis, a Codex-style agent the cleanest patch, and a fast model was best for sweeping logs. The anchored prompt mattered more than which model I picked.
What the data showed, and what I fixed
PostHog changed how I saw the product. For the first time I could watch where people tapped, where they gave up and which stations they searched again and again. Over 30 days, 1.6% of live departure fetches errored, mostly timeouts on the slow live path. The peak week hit 4.1%, and the worst single fetch took 92 seconds.
Then I shipped fixes, one PR at a time. 11 pull requests: stopping cron jobs from burning the RealTimeTrains quota, backfilling missing platforms, loading later trains at busy stations, pre-rendering 2,599 station pages for SEO, and rebuilding the station health charts.
Two weeks after the platform and cache-warming fixes merged, the weekly error rate was 0.6%. It now hovers near 2%, and the next target is the slowest requests.
The last 10%
On the genuinely hard bugs, the model runs out of road. In plan mode it tells me the fix has landed, and the next morning the bug is still there. For a train app, reliability is the whole point, and vibe coding trades it away without you noticing.
So you dogfood it yourself. The last 10% is the hardest part to close and where I spend the most time. It's also what makes the app good, and deciding what to build is the part AI can't do for you.
Prompts to copy
My first version was "Fix the platform issue." This is where it ended up. Fill in the [brackets] with your own evidence.
The evidence-backed prompt
Give the model the evidence before it suggests a fix, so it stops guessing.
Here are the [PostHog traces], the [Playwright failure], the [cache tier], [examples of where it breaks] and the expected user outcome: [outcome].Use these files: [files]. This is the failing path: [path]. This is the API limit: [limit].Do not touch unrelated code.Propose the smallest patch, then explain how to verify it.Further reading
- TrainTimesUKThe app from the talk, live
- Pendo Feature Adoption Report80% of features rarely or never used
- The full deckOpen the slides on their own
- My LinkedIn postPhotos and thoughts from the day
More talks and workshops
Hopefully this helps you out! Feel free to reach out, or see all my talks and events.



