Some CogniPrep pages always render dark regardless of the visitor's theme preference: the marketing pages, the auth screens, and every assessment provider hub. They are designed dark, and a light rendering of them looks broken rather than merely different.
Implementing that is a two part problem, because a theme has to be applied before first paint and also on client side navigation.
Before first paint, a blocking inline script in the root layout adds the class, because anything that waits for React has already let the wrong theme paint:
var path = window.location.pathname;
var forceDarkPaths = [ /* ... */ ];
var shouldForceDark = false;
for (var i = 0; i < forceDarkPaths.length; i++) {
if (path === forceDarkPaths[i]) { shouldForceDark = true; break; }
}
if (shouldForceDark) {
root.classList.add('dark');
root.style.colorScheme = 'dark';
} else {
var theme = localStorage.getItem('theme') || 'system';
// ...apply the user's preference
}
On client side navigation, window.location.pathname never changes again, so a small component re-applies the same decision on every route change:
export function ForceDarkMode() {
const pathname = usePathname();
useEffect(() => {
if (shouldForceDark(pathname)) { /* force dark */ }
else { /* restore the user's preference */ }
}, [pathname]);
return null;
}
Two places. One list. You can already see it.
Drift one: two copies
The list existed twice, written out in full in both files. Both copies had to agree or a page would flash the wrong theme on load and then correct itself, or navigate into the wrong theme and stay there.
They had already diverged. Two provider hubs were missing from one copy, so those two rendered light beside nine dark siblings. Nobody filed it. A page in the wrong theme does not throw, does not log, and does not look like a bug unless you happen to open two provider pages in a row.
The fix is the obvious one: a single exported constant, serialised into the inline script at build time.
// Serialised from the shared list rather than hardcoded, so this script and
// ForceDarkMode (which handles client-side navigation) cannot drift apart.
const themeScript = `(function(){
var forceDarkPaths = ${JSON.stringify(FORCE_DARK_PATHS)};
// ...
})();`;
JSON.stringify of a string array into a script template is one of the few places where generating code is clearly the right call. The alternative is asking two humans to keep two lists identical forever, which is the thing that had already failed.
That felt like the end of it. It was not.
Drift two: one copy, still wrong
Later we added several assessment providers in parallel, each built by a different worker in its own branch. Two of the new provider hubs shipped without an entry in the list, and rendered light beside their siblings. Exactly the same symptom as before, with the duplication already fixed.
Because deduplicating a list does not make it correct. It makes it consistently whatever it is. The failure mode this time was not two lists disagreeing, it was one list that nobody adding a provider had any reason to know existed. There is no import, no type error, no missing property. A provider hub is a new route folder, and the route folder works.
The obvious answer is to derive the list instead:
...ALL_PROVIDERS.map((p) => `/games/${p}`)
We did not, and the reason is worth stating, because "just derive it" is the advice everyone reaches for first. This list is not about providers. It is a mixed list of paths that happen to include every provider hub, alongside /pricing, /login, /terms, /about and a dozen others. Deriving the provider slice would leave the other twenty entries hand written, add a moving part, and still not tell the next person that the hand written part exists.
So the rule we ended up with: if nothing derives a list, a test has to.
// This list has now drifted twice. The first time, Aon and Korn Ferry rendered
// light beside nine dark siblings; the second time it was Thomas and TestGroup,
// each added by a different provider worker that had no reason to know the file
// existed. Nothing derives the list, so only a test catches the omission.
describe('FORCE_DARK_PATHS', () => {
it('covers every provider hub page', () => {
const missing = ALL_PROVIDERS.filter(
(provider) => !FORCE_DARK_PATHS.includes(`/games/${provider}`)
);
expect(missing).toEqual([]);
});
it('matches exactly, so a game route under a hub is not forced dark', () => {
expect(shouldForceDark('/games/shl')).toBe(true);
expect(shouldForceDark('/games/shl/shl-verbal')).toBe(false);
});
it('has no duplicate entries', () => {
expect(FORCE_DARK_PATHS.length).toBe(new Set(FORCE_DARK_PATHS).size);
});
});
Three assertions, and each one is a different kind of guard.
The first is the drift guard, and note what it does not do: it does not check that the list is right, only that it contains every provider. That is enough, because the whole failure mode was omission. A test that catches the one thing that has actually gone wrong twice is worth more than a comprehensive one nobody writes.
The second pins a semantic that is invisible in the data. Matching is exact, so /games/shl is forced dark and /games/shl/shl-verbal, the test itself, is not: inside a test the candidate's own theme preference applies. Nothing in a list of strings communicates that, and a future refactor to startsWith would look like a tidy-up while silently changing the product.
The third is free and catches copy-paste.
The failure class this belongs to
The reason this one drifted twice is that it has no feedback loop:
- It does not throw.
- It does not fail to compile.
- It is not visible in any diff of the change that broke it.
- It is only detectable by a human looking at two pages side by side.
Hand maintained lists keyed by an enum are everywhere in a codebase this size: sitemap entries, analytics allowlists, feature flags, theme paths, redirect maps. Each one is a silent correctness dependency between "add a thing" and "remember a file".
The general rule we now apply: derive where the list is genuinely a projection of another list, and test where it is not. If you find a hand maintained list keyed by a provider, a plan, a locale or any other enum in your code, the fix is a test that enumerates the enum, not a note in a document and not being careful.
See it
Open a few provider hubs, for example ACER, Assessio and Thomas International. All dark, including the two that once were not, and including the ones added months apart by people who never opened the file that makes it so.
Then open your own codebase and grep for an array of strings keyed by something that grows. Ask what fails when the next person forgets to add an entry. If the answer is "nothing fails, it just looks a bit wrong", you have found the same bug we shipped twice.
Top comments (0)