Building Routes for Training: From Frustration to an AI-Assisted Product
How the lack of a route builder that understood structured training led me to build one—and what AI changed about the process.
Most route tools can draw a line, estimate a distance and export a GPX file. What I could not find was a tool that started with the training session itself.
A Zone 2 endurance ride wants continuity. Hill repeats need the right climb in the right place. Threshold intervals are undermined by constant junctions. A gravel session needs more than a road route with a gravel label attached. I wanted to ask for the workout and have the software reason about the route.
That frustration became Routes for Training, an application for cyclists and runners that generates, scores, edits and analyses routes against training intent.

The dashboard brings route generation and the analysis tools into one workflow.
The question changed
Traditional route planning asks: “How do I get from these points?”
Routes for Training asks: “Where can I complete this particular session well?”
The user provides a start, sport, workout type, target duration or distance, route shape, surface preference and constraints such as avoiding main roads or steep gradients. Interval sessions can include repetitions, work duration and recovery. The generator creates candidate routes and ranks them against the job they are meant to do.
The workout types include Recovery, Zone 2, Tempo, Threshold, VO2 Max, Hill Repeats and Gravel Endurance. User FTP, heart-rate data, preferred units and pace overrides make the result personal rather than a generic loop of approximately the right length.

The route starts with the purpose of the session, not just a line on a map.

Area, surface and road preferences turn training intent into routing constraints.
A score needs an explanation
Route ranking considers workout match, gradient, interruptions, safety, geographic fit and surface fit. The weights change with the workout: a hill-repeat route should not be judged like a steady Zone 2 ride.
A number on its own would be another black box, so each result includes a plain-English “Why this route?” explanation. Maps show elevation, direction and climbing/descending segments. The user can see why a candidate ranked well and decide whether the software’s priorities match the real road.
That last step matters. Routing data is imperfect, conditions change and a locally experienced rider will always know things a dataset does not.

A generated route exposes the individual scores behind its overall recommendation.
Generation was only the start
The application grew into a full route workflow:
- generate by duration or explicit distance;
- create a route from ordered waypoints;
- compare scored candidates with elevation profiles;
- edit geometry with rerouting, previews and undo history;
- save, rename, archive and export GPX files;
- analyse uploaded GPX routes for surface mix;
- maintain bike profiles and produce tyre recommendations;
- upload FIT activities for private, asynchronous ride analysis;
- optionally hand a GPX to Strava’s supported import workflow.
Surface-aware routing was one of the harder parts. “Gravel” is not a single routing flag. The system uses OpenStreetMap and Overpass data, classifies surface tags, anchors candidates to suitable segments and then measures the result rather than assuming the request was satisfied.

Analysed routes remain available in a library for review, tagging, reanalysis and archiving.
The application behind the map
The frontend uses Next.js and TypeScript. A FastAPI backend handles the application API, PostgreSQL stores users and routes, and Redis with an arq worker runs route generation and FIT analysis asynchronously. Caddy presents one origin to the browser. The stack runs in containers and uses Google OAuth for sign-in.
Mapbox or OSRM provides directions; OpenTopoData supplies elevation; Nominatim-compatible search resolves locations; and OpenStreetMap/Overpass data supports surface analysis. These external systems are adapters, not invisible magic. Timeouts, quotas, partial responses and fallback behaviour are part of the design.
What AI actually contributed
I leaned heavily into AI-assisted development, but the useful pattern was not “write my app.” I supplied the problem, acceptance criteria, screenshots, failing behaviour and decisions. AI helped turn that intent into specifications, implementation slices, tests and reviews across a growing codebase.
The process became increasingly disciplined: isolate a feature, describe the observable behaviour, implement on a branch, run validation, inspect the diff and feed concrete failures back into the next pass. I also built orchestration around different planning, implementation and review agents, including local Ollama models where their limitations were acceptable.
AI accelerated exploration and made a much larger solo project possible. It also made verification more important. Generated code can be locally plausible while misunderstanding geography, units, authentication or an API contract. A passing build is not evidence that a route is safe or enjoyable.
The product lesson
Routes for Training began with a bugbear: existing tools made me adapt a training session to a route. Building my own reversed that relationship.
The result is not simply an AI experiment. It is a real application shaped by product decisions, geospatial uncertainty, background processing, privacy, security and the awkward details of exporting something another device can follow.
AI lowered the cost of trying the idea. The hard and worthwhile work was deciding what “a good route for this workout” should mean.