In the last post I opened the source of Boolflow, my browser-based digital logic simulator, and found that the roadmap I had confidently published was in the wrong order. I ended it planning to build an explicit tick counter.
I did not build the tick counter. I opened the deployment instead, and spent the day finding four things that had been broken for months while looking completely fine.
That is the thread running through all of them. Not one of these bugs made anything fail in front of me. Every single one was hidden by the normal path working exactly as intended.
Boolflow is a Vite SPA with a prerender step: prerender.mjs walks a route table, renders each page with renderToString, and writes a real HTML file per URL. I wrote it on 4 June. It has been sitting in the repository ever since, correct and tested and doing nothing at all.
The same 4.5 KB on all of them. Same . Same description. — empty. It was the Vite build output before the prerender step overwrote it.
The prerender lived in build:full. The documented update procedure called build. Not sometimes — structurally, every time, for three months. The deployment instructions I wrote could not have produced a prerendered site if I had followed them perfectly.
And here is why I never noticed: the site looks perfect in a browser. You request /ru/help, get 4.5 KB of empty shell, the JavaScript loads, React hydrates, and the Russian help page appears. Every human visitor, including me, saw a working site. Only crawlers — and anything else that does not execute JavaScript — saw the shell.
You can no longer deploy half a build, because there is no longer a script that produces half a build.
The shell has a static — correct for the landing page, meaningless for the other sixty-four URLs it was being served as.
So the sitemap advertised 65 URLs, and all 65 responded with the same title, the same description, and a canonical tag pointing at the homepage. That is not "pages ranking badly." That is me explicitly instructing Google to collapse the entire site into one page.
Three smaller things were wrong in the same layer, and all three had the same character — a thing that returns 200 and looks fine: /nonexistent-page-xyz returned 200. Every typo was an indexable page. www.boolflow.site returned 200 with no redirect, so the whole site existed twice, on two hosts, with canonicals pointing at only one of them. The sitemap was hand-maintained next to a script that already knew the exact route list. It had drifted, of course. It now gets generated from that route list, so it cannot advertise a URL that has no page.
Boolflow has EN/RU/DE. Articles and help live under /ru/… and /de/…. The language context read the language from exactly one place:
The URL is not in that expression. So /ru/help rendered its body from the path and its chrome from localStorage, and the prerendered file proves it — a Russian under an English header:
Worse, the provider also set document.documentElement.lang from localStorage, so hydration rewrote the prerendered lang="ru" back to lang="en".
The URL prefix now wins, falling back to the stored preference only on paths that have no prefix. One consequence I did not expect: the moment the URL became authoritative, I found the translations for the landing page and all six tool pages were already written — full EN/RU/DE copy, sitting in the source, reachable only as a localStorage state. They had no addresses. Google had never seen them and could not have.
Giving them addresses took an afternoon and added fourteen pages of content that already existed.
This is the one I am actually pleased about, because it validated the last post in a way I did not intend.
Stateful elements — flip-flops, counters, latches — keep their state in a module-level Map, keyed by node id, mutated in place:
