I Turned “Falsehoods Programmers Believe” Into a Daily Game Where Every Card Proves Itself
A daily card game built on the falsehoods programmers believe. Every answer comes with a JavaScript proof you can edit and run in the browser.
A quick quiz before we start. True or false?
- Every day has 24 hours.
'👩💻'.lengthis 1.Math.max()with no arguments returns-Infinity.- A US ZIP code can be stored as a number.
If you’ve been programming for a while, you probably smelled a trap in all four. The answers are at the end.
That instinct comes from the “falsehoods programmers believe” lists. Patrick McKenzie started the genre in 2010 with Falsehoods Programmers Believe About Names, and Kevin Deldycke’s awesome-falsehood collects hundreds more about time, addresses, phone numbers, money, and text. It has more than 27,000 stars on GitHub.
I love reading these lists. I also forget most of them a week later, because a list lets you nod along without ever committing to an answer. You can’t be wrong while you’re reading.
So I made a game that makes you commit: Well, Actually.
How it plays
You get ten cards a day. Each card makes a claim, like “Everyone has a family name” or “0.1 + 0.2 === 0.3”. You answer True or Well, actually…, with a button, an arrow key, or a swipe. Then the card flips over to show the answer, a sentence on why it matters in real code, and a proof.
Most claims are false, but not all of them. Some true ones are mixed in, so answering “well, actually” every time won’t get you a perfect score. Everyone on the same date gets the same ten cards, and at the end you can share your score as a row of squares.
Every card proves itself
This is the part I care about most. A card that says “well, actually” has to back it up, so most cards end with a few lines of JavaScript that run in your browser, right on the card. Here’s the proof for “Every day has 24 hours”:
const ny = new Intl.DateTimeFormat('en-US', {
timeZone: 'America/New_York',
dateStyle: 'medium',
timeStyle: 'long',
});
const day = 24 * 60 * 60 * 1000;
const march8 = Date.UTC(2026, 2, 8, 5); // midnight in New York
console.log(ny.format(march8)); // Mar 8, 2026, 12:00:00 AM EST
console.log(ny.format(march8 + day)); // Mar 9, 2026, 1:00:00 AM EDT
Twenty-four hours after midnight, it’s 1 AM the next day. That day only had 23 hours.
The output isn’t typed into the card. Your browser computes it, using the same time zone data your own code would use. And every proof has an Edit button: change the code and run it again. In the length card, swap the technologist emoji for a flag and you’ll see 4, 2, and 1 instead of 5, 3, and 1. Don’t take the card’s word for it.
A few favorites from the deck
There are 61 cards in seven categories: time, text, numbers, names, places, money, and the web. These are the ones that surprised me most while I was writing them.
That emoji is five characters long
const coder = '👩💻';
console.log(coder.length); // 5
console.log([...coder].length); // 3
console.log([...new Intl.Segmenter().segment(coder)].length); // 1
The woman technologist emoji is a woman, a zero-width joiner, and a laptop. length counts UTF-16 code units, the spread operator counts code points, and only Intl.Segmenter counts what you actually see.
Uppercasing can make a string longer
console.log('straße'.toUpperCase()); // STRASSE
German ß traditionally uppercases to SS. If your code uppercases text and then indexes into the original by position, the two have just drifted apart.
Samoa skipped a Friday
const apia = new Intl.DateTimeFormat('en-US', {
timeZone: 'Pacific/Apia', dateStyle: 'full', timeStyle: 'short',
});
const thursday = Date.UTC(2011, 11, 29, 21);
const day = 24 * 60 * 60 * 1000;
console.log(apia.format(thursday)); // Thursday, December 29, 2011 at 11:00 AM
console.log(apia.format(thursday + day)); // Saturday, December 31, 2011 at 11:00 AM
In 2011, Samoa moved across the international date line to line up with its trading partners. December 30 never happened there. Time zone rules change all the time, which is why your code should get them from a maintained database instead of hard-coding offsets.
(1.005).toFixed(2) is "1.00"
console.log((1.005).toFixed(2)); // 1.00
console.log((1.005).toPrecision(20)); // 1.0049999999999998934
The number you typed can’t be stored exactly. The closest float is a hair below 1.005, and toFixed() rounds that value correctly.
The first ZIP code loses its zero
console.log(Number('01001')); // 1001
console.log(Number('SW1A 1AA')); // NaN
01001 is Agawam, Massachusetts. Stored as a number, it becomes 1001. British postcodes don’t survive at all. Postal codes are identifiers that happen to contain digits, so they belong in strings.
Three dates at once
With offsets running from UTC-12 to UTC+14, the planet spans 26 hours of local time. Between 10:00 and 12:00 UTC every day, it’s yesterday on Baker Island, today in London, and tomorrow in Kiritimati. The card’s proof prints all three for one moment.
Built without a build
Well, Actually is one HTML file, one stylesheet, and five small ES modules on GitHub Pages. It has no framework, no bundler, no dependencies, and no analytics. The page loads nothing but its own files, and your score and streak stay in your browser.
The deck is a plain JavaScript array. Each card has a claim, the answer, a one-line truth, an explanation, a source link, and the proof, written as a String.raw template so the code you see is exactly the code that runs. Running it is short:
export function runProof(code) {
const lines = [];
const log = (...args) => lines.push(args.map((a) => formatValue(a)).join(' '));
try {
new Function('console', `'use strict';\n${code}`)({ log, info: log, warn: log, error: log });
return { lines, error: null };
} catch (error) {
return { lines, error: `${error.name}: ${error.message}` };
}
}
The daily deck is a seeded shuffle of the date, with a few rules on top: no more than three cards from one category, between one and three true claims, never a true card first, and never two cards that give each other away (like “every year divisible by 4 is a leap year” and “2000 was a leap year”). A new deck starts at midnight in your own time zone, which means two people can be playing different decks at the same moment. There’s a card about that too.
Testing a deck of facts
A game that corrects people had better be right, so the test suite treats every card like code. It runs each proof and compares the output with a reviewed snapshot. Then it runs every proof again under three other time zones (Kolkata, Los Angeles, and Kiritimati) and three locales (English in India, German, and Thai) and requires the same output each time. A proof can’t quietly depend on where the player happens to be.
One thing I didn’t expect: Intl output isn’t identical across ICU versions. Depending on the version, the space before “AM” can be a regular space or a narrow no-break space (U+202F), and Swiss German can separate thousands with ' or ’. Different Node.js releases ship different ICU data, so the tests normalize those few characters and compare everything else exactly. That’s a falsehood worth its own card: “The same code formats a date the same way everywhere.”
Add a card
The deck is open source, and adding a card is one object in src/cards.js plus npm test. If you know a belief that deserves a “well, actually”, I’d love a pull request: github.com/OmShiv/well-actually.
The answers
- False. Days with a daylight saving change have 23 or 25 hours.
- False. It’s 5: two UTF-16 code units for the woman, one for the joiner, and two for the laptop.
- True.
Math.max()starts from-Infinity, which any number beats. - False.
Number('01001')is 1001, and the leading zero is gone.
How did you do? Today’s ten cards are waiting at omshiv.github.io/well-actually. Tell me your score.