In 2010, Patrick McKenzie published a list of forty things programmers believe about names that are not true. People have one full name. People have exactly one full name. People's names fit in a certain number of characters. People's names do not change. It reads like a joke until you recognise your own schema in it.
Two years later, Noah Sussman did the same for time. There are 24 hours in a day. A minute has 60 seconds. The clock always moves forwards. A day that has already happened will not change.
Those lists have been circulating for over a decade, and the bugs they describe keep shipping. Not because engineers have not read them. Because none of these beliefs arrive as decisions. They arrive as background, as the furniture of the world, and nobody writes down the furniture.
Part 8 was about the questions a ticket leaves unanswered, which you can find by looking for them. This one is about the answers you never knew you had given.
What an invisible assumption looks like
Take a payments feature. The specification says refunds go against the original charge, which is correct, and everyone involved reads it and agrees.
Six weeks later, finance cannot reconcile the books. Refunds issued after the end of a billing month are landing in the wrong month's ledger. The code does exactly what the spec said. The spec was written by people who all quietly believed that a refund happens in the same month as its charge.
Ask any of them directly, "do refunds always happen in the same month as the charge?", and every one of them says no, obviously not. The belief never survives being asked. It just never gets asked, because it does not feel like a decision. It feels like a fact.
That is the whole category. Not the gaps you can hunt, the beliefs you cannot see because you are standing on them.
The five that catch most teams
These are the families those falsehood lists keep landing in, and the ones most likely to be load-bearing in ordinary business software.
1. Time is simple. Everything happens in one time zone, clocks move forwards, a month is a tidy container, and events arrive in the order they happened. Real systems have a customer in Auckland whose "today" is your tomorrow, a daylight saving night with a repeated hour, a subscription started on the 31st of January, and a refund in a different month from its charge. The question to ask of your own spec: which dates in here are "when it happened", which are "when we recorded it", and have we ever said which one we sort by?
2. Identity is simple. There is a logged-in user. That user is a person. One person has one account, one email, one name that fits in the field. Background jobs, webhooks, scheduled exports and admin impersonation all run with no logged-in user at all, and "the user" quietly becomes "whatever account the job runs under", which has different permissions. The question: when this code runs without a human in front of it, who is it acting as, and who decided that?
3. One of a thing means one of a thing. One email address, one account. One order, one shipment. One customer, one currency. Business reality produces the shared family email, the order that ships in three boxes, the customer who pays in euros and refunds in pounds. Cardinality assumptions get baked into the schema on day one and are the most expensive to undo later. The question: for each "one" in this spec, who checked that it stays one when the business grows?
4. Messages arrive once, in order, and succeed. Payment webhooks arrive twice. The "cancelled" event overtakes the "created" event. The third-party API is down for ninety seconds. The classic version of this bug is not a crash, it is a double charge, because the handler assumed each message was new. The question: what does this do if it receives the same message twice, and what happens if it never arrives at all?
5. Data stays put. Prices do not change after the invoice. The address on file is the address at the time of the order. The product name in the report is the name it had when the report ran. These are false in almost every system that stores something once and reads it later, and the symptom is a historical record that silently rewrites itself when someone edits a row today. The question: for every value we read later, do we need what it is now or what it was then?
None of the five is exotic. Every one of them has shipped in something you have used this week.
The tell
Invisible assumptions have a vocabulary. They hide in the words that signal "this part is not worth discussing": always, just, simply, obviously, of course.
"The refund just goes against the original charge." "The user is always logged in when this runs." "We simply need the current exchange rate."
Read your own specification out loud and mark every one of those words. For each, ask one question: is this always true, or is this what we have mostly seen?
That exchange rate line is a good example. A system that only ever asks for the current rate has decided, without noticing, that nobody will need a historical rate for a refund, a reconciliation or an audit next year. That decision might be right. The problem is that nobody made it.
Why agents make this worse
A human engineer building the refund feature by hand eventually has to write the line that picks a billing period. Writing it forces a half second of "wait, which month?", and that half second is where the assumption sometimes surfaces. Not reliably, but sometimes.
An agent given "refunds go against the original charge" writes that line immediately and correctly. It implements the sentence it was given, and the belief underneath the sentence rides along, undiscussed, into production.
This is the same mechanism as the last two articles, with one difference. In Part 7 the gaps were things the specification did not say. Here, the specification says something that is true in the common case and wrong at the edges, and the agent has no way to know which case you meant. A spec you believe is complete is the most dangerous kind of input for a system that cannot ask what you were picturing.
What this means for you
Two passes, and both are short.
First, read the spec back and mark every always, just, simply and obviously. Most will be genuine facts and the check costs ten seconds each. Occasionally one is the refund bug.
Second, run the five: time, identity, cardinality, delivery, and whether data stays put. Ask which of them this feature actually touches. Usually it is one or two. Write down what you are assuming about those, in a sentence, so the next person can disagree with it.
That is the end of the specification cluster. The next articles turn to the other half of the job: what to do after the agent hands something back, and how to read code you did not write with the suspicion it deserves.
Sources: Patrick McKenzie, "Falsehoods Programmers Believe About Names" (2010); Noah Sussman, "Falsehoods Programmers Believe About Time" (2012).
© Gabor Mayer. Licensed under Creative Commons Attribution 4.0 (CC BY 4.0). Free to share and adapt with attribution.