← All articles

6 September 2026

Twenty-six rides, and a list that said nothing

To show each ride's teaching, we first had to tidy up: the names of the five teachings existed in six separate copies in the code. Tidying up brought down nine defects nobody could see.

An instructor teaches Spinning® on Monday, Schwinn® on Wednesday, Globe Cycling® on Saturday. Saturday morning, they open RideComposer, click My Rides, and read twenty-six lines that all say the same thing: a name, a track count, a duration.

So they adapt. They call their rides HILL_Disney, FARTLEK 1 - 06 06 26, eXTENSIVE iNTERVAL _6x4. They write the classification into the title, for lack of anywhere else to put it.

That workaround is what this article is really about. Because the missing information already existed.

Data that was there, and never shown

Since early September, every ride records the teaching and the class type it was built with. That data sat in each ride file. It served exactly one purpose: stopping a Solo plan user from opening a class composed in another teaching.

So the software knew, line by line, that this ride was a Globe Cycling® Fartlek. It just never told anyone.

Tidying up before adding

Showing that information meant a badge on each row: the teaching's name and its colour. An hour's work — except those names and colours already existed in six copies, hand-written in six different places.

Six copies of the same truth don't stay identical for long. They had drifted:

  • BICA wore its brand's yellow-green on its button… and Globe Cycling®'s blue on two other screens. Two different teachings, the same colour, with nothing to flag it.
  • Free Ride was missing from one of those copies. The direct consequence: a Free Ride instructor opened Quick Ride and read "🔵 Spinning®" above their own Free Ride cards.
  • Heart-rate zones — which belong to no teaching at all — were labelled "Schwinn®" in the symbol assignment window, simply because they fell into the "otherwise" branch of a list of conditions.

The temptation, in that situation, is to add a seventh copy and move on. We did the opposite: one single declaration of the five teachings, which every screen reads. A test now refuses to let a seventh reappear.

What the tidying brought down

This is the interesting part. Putting things in order turns up what nobody was looking for.

The magnifier that lied. In the Ride Builder, each zone has a small magnifier: it opens the library showing only what fits that zone. It announced "intensity 1 to 20" — and filtered from 1 to 7. The reason: an intensity filter set during an earlier search stayed in place, and the magnifier landed on top of it. The zone announced one thing, the screen applied another, and nothing said so. The two criteria are now one: the zone decides, alone.

The relocated file that wasn't. When a track has been renamed or moved, RideComposer offers to find it again with a "Browse" button. You'd point at the file, the window said OK, the track played. But at the next reload, the window came back. The cause goes back to a technical library: Electron removed, in a recent version, the property that gave the path of a chosen file. It returns nothing now — and above all, it raises no error. The track did enter memory for the current session, but no path was ever saved. The button now goes through the native dialog, which returns a real path, and the track takes the name of the file you pointed at.

The arrow nobody had ever seen. Your library doesn't render all at once: it loads in pages, and a line at the bottom announces "⏬ 17,909 more tracks". That line had existed for a long time. It was crushed into a column twenty-eight pixels wide, and therefore invisible. And each page loaded rebuilt the list from the beginning: scrolling through 18,000 tracks rendered 1.3 million of them. Today each track is drawn once, and the arrow can be seen.

The version number that was wrong. The sidebar showed "V 1.3.0" — hand-written in the code — while the installed software was 1.3.1. That same number stamps the rides you export: your exports carried an outdated version. It is now asked of the application itself.

None of these defects causes a crash. That is precisely what makes them last: nothing breaks, the software simply becomes slightly wrong, and nobody has any reason to report it.

What you'll see

In My Rides, every class now carries its badge: the teaching in its colours, and the class type. Older rides show "unclassified" — you classify them by opening them and choosing their type.

And above all, two rows of filters. The first, the teaching, with each one's number of classes. The second, the energy zone — and this is where the detail matters: each teaching has its own vocabulary. Spinning® speaks of Recovery, Endurance, Resistance / Strength, Interval, Race Day. Globe Cycling® and BICA speak of terrain: Recovery, Foothills, Highland, Summit, Redline, Interval, Fartlek. Schwinn® speaks physiology: Low aerobic, Aerobic, High aerobic, Anaerobic. Offering "Z3" to someone filtering Schwinn would make no sense. The zones shown are therefore those of the chosen teaching, and only the ones where you actually have classes.

A mistake we nearly shipped

To name those zones, we first wrote a rule: if a single session occupies a zone, it gives the zone its name. Elegant, short, and wrong. The rule displayed "Free" where a Globe Cycling instructor reads "Fartlek".

We replaced it with a table written out in full, one name per zone per teaching, copied from the application's own selectors — which come from the manuals. On matters of theory, a rule that guesses is a mistake waiting to happen: it works on the known cases and fails silently on the next one.

Two automatic checks protect it: every zone used by a class must have a declared name, and every declared name must match a zone still in use.

In numbers

The application's main file lost 634 lines in the operation, and our safety net went from 492 to 624 automated checks. They are what guarantees those nine defects won't come back — and that the seventh copy of the teachings will never see the light of day.

Version 1.3.2 is available. It will be offered to you at your next launch.