Skip to content
← News

Roadmap #7: Breaking It On Purpose

Danny de Haas · August 21, 2026

  • roadmap

At the end of Roadmap #5 I told you that I stopped building, started testing, and that quite a lot of it broke. This article is about what was actually wrong, what I did about it, and what still stands between today and the day sign-ups open.

At the end of Roadmap #5 I told you that I stopped building, started testing, and that quite a lot of it broke.

This article is about what was actually wrong, what I did about it, and what still stands between today and the day sign-ups open.

Why A Working App Was Still Broken

Here is the trap in building software feature by feature. Everything gets tested a little, by the person who built it, in exactly the way they expect it to be used. That is not testing. That is a demonstration.

Real users do not behave like that. You will open a page from a link somebody pasted in Discord six weeks ago. You will change a car number, then change your mind. You will type a web address slightly wrong. You will log in as a team manager on your phone at a service station. So I stopped adding things and went through the app as a driver, then as a team manager, then as Staff, then as a steward, clicking everything and specifically trying to break it.

It turns out I am quite good at breaking it.

"Application Error"

A lot of pages simply showed: "Application error: a client-side exception has occurred". No explanation, no way forward. If you had met that page on sign-up day, you would have assumed the DEC was a scam and closed the tab, and you would have been right to.

The tempting fix is to repair pages one at a time. That is almost always wrong. When twenty pages fail at once, you are rarely looking at twenty bugs. You are looking at two or three shared causes in something every page uses.

So I went at it properly, with Claude Code doing the work and me deciding the approach. First, build the app the way it will actually run for you, because this type of failure loves to stay invisible during development. Then temporarily make the app report the real underlying error instead of the polite useless one. Then write a throwaway script that visits every single page in the application, as every role, and records what happened on each: the status, any error, whether the page died.

That produced a complete table of every page in the platform and its true condition, and from a table like that the pattern is obvious immediately. A handful of shared causes, exactly as expected. One improvement from that work is permanent. The app now records the real error on the server and shows you a reference code, so if you ever do hit a broken page, I can find out why instead of guessing at it.

The Bug That Looked Cosmetic

Some pages that should have said "not found" were instead quietly reporting success while displaying a "not found" message. That sounds like a detail only a developer could care about.

It is not, and the reason matters to you.

When you ask for something you are not allowed to see, this platform returns a plain "not found" rather than "you are not allowed to see this". Because the second one confirms that the thing exists. On a platform where several organizations eventually run their championships side by side on the same software, "that entry list exists but is not for you" is information leaking across the wall. Small, but our entire multi-tenant design rests on organizations being properly invisible to each other, and walls do not get to be almost solid.

There is now a permanent automated test that checks a list of deliberately wrong addresses and fails the build if any of them stops behaving. This one cannot creep back.

The Unglamorous List

Then came everything that was not broken, just wrong.

The homepage hero image and text are now editable per organization instead of fixed. A dead link is alive. The sidebar collapses properly and remembers how you left it, stored against your account rather than your browser, so it follows you from your desktop to your phone.

Publishing a news article now confirms it worked and takes you back to the overview, and if it fails, your text is not lost and the error tells you what actually went wrong instead of "something went wrong". In car class management, the input clears after adding a model, duplicates are refused, changes show up in the season settings instead of a stale list, and a class can be removed from a season unless entries are registered in it, in which case the app blocks it and shows you exactly which entries are in the way.

Nobody will ever thank me for any of that. It is also the entire difference between software that technically functions and software a tired steward can trust at four in the morning.

Three Gaps That Became Features

Some of what I found were not bugs but holes, and three were serious enough to build properly.

Staff can fix a line-up after the deadline. Real life happens: a driver falls ill the morning of the race. The deadline stays a hard deadline for teams, because otherwise it is not a deadline. But Staff can correct a line-up afterwards, every such change is written to the audit log, and your team is notified. Discretion is necessary in endurance racing. Invisible discretion is not acceptable.

The Discord invite is always one click away. It now sits at organization level, appears right after you register, and lives permanently in your profile menu. A small thing, but new members were falling straight through the gap between the website and the community. Teams have a proper lifecycle. Managers could not delete a team or hand one over, and both are needed. But deleting a team outright was never acceptable, because a team is not just a name: it carries entries, rosters, results and stewarding history.

So deleting a team now moves it to a Trash Bin, where Staff can restore it for three months before it is permanently removed. A team can be transferred to another manager, and the receiving manager has to accept it. A team with active entries cannot be deleted at all until those entries are withdrawn. And withdrawing an entry requires confirmation from whoever originally signed it up, so nobody loses their seat to a misclick by someone else.

Through all of it, one rule does not bend. Published results, standings and steward decisions keep displaying correctly no matter what happens to a team afterwards. You cannot delete your way out of history here.

What Still Has To Happen

Stage one is where I am now: stability. The rule I have given myself is that nothing new gets built until the app is boring again.

Stage two is the remaining build steps. I originally planned these as post-launch additions, and I have changed my mind, because several of them are the difference between a competition that works and a competition that feels professional in round one.

That means live timing and a live track map inside the officials' dashboard, so Race Control is not squinting at separate software on a second screen while judging your incident. The automated media engine from Roadmap #4, posting standings and results graphics by itself. Automatic Mumble voice channels for your team. The "What if?" championship calculator. End-of-season awards generated from the season's own statistics. A broadcast room for commentators. And the analytics dashboard with the Endurance Index, the rating built from your pace, consistency, racecraft and incident record.

Not included, for the reasons in Roadmap #6: anything that depended on reverse-engineering RaceControl.gg.

Stage three is the invisible work that nobody writes articles about, and that everybody notices the day it is missing. Hosting inside the EU, automated backups, automated tests around the penalty calculation and permission rules, a separate staging environment, and a deployment pipeline so an update can never reach the live site untested.

Stage four is a closed test with real people. Real teams, real drivers, a real event run end to end inside the platform, with everything that goes wrong written down and fixed before anybody's entry fee is anywhere near it.

Then sign-ups open.

How Long?

I am not giving you a date, because a date invented today is a promise made with no information, and I would rather under-promise to you now than apologise later.

The target is straightforward: the platform is live, tested and taking entries before Season 1 sign-ups open, and the first race is run entirely inside it. If a stage takes longer than I hoped, it takes longer, and you will read about it here.

I would much rather explain a delay to you now than explain to twenty-four teams why the platform fell over five hours into a night race. A missed date is annoying. A competition that cannot be trusted is fatal, and not building that is the entire reason this project exists.