The signal-debt ledger: every free physical cue you delete by digitizing becomes a signal you owe the user in code.
A user clicks Pay. Nothing happens for 400 milliseconds, so they click again. Now you have a duplicate charge, and the root cause is a request that succeeded. Somewhere else, someone files “the export is broken” about an operation that completed perfectly. And every week someone insists your AI feature is broken when the model is working fine. Three tickets, three teams, three “can’t reproduce” replies. They are the same bug. Most engineers never name it, so they keep paying for it.
I shipped that bug for a year and closed every instance as “works as intended.” Read the docs. Check the error message. Prompt it better. I write backends and AI agents, plumbing nobody sees, so I got away with treating the human on the other end as someone else’s problem, until the duplicate charges and the false bug reports started costing real time.
Then I opened Don Norman’s The Design of Everyday Things, expecting a gentle book about doors, and it diagnosed me by page 25.
What follows is the mental model that turned three unrelated production fires into one fixable thing. I call it signal debt: the liability you take on the instant you move any process out of the physical world and into software. The physical world emits status for free. Software emits nothing by default. Every signal a user needs, you now owe them, explicitly, in code. Ship without paying it and the bill arrives as duplicate writes, false failure reports, and “your AI is broken” tickets you can’t reproduce. Naming the debt is what lets you stop shipping it.
Four ideas from Norman’s first chapter are really four unpaid installments of the same debt. Here’s each one, the version of it I shipped, and what it costs in production.
Installment 1: “If only people would read the instructions”
Here’s the sentence that stopped me. Norman, on why so much technology is hard to use:
”Much of the design is done by engineers who are experts in technology but limited in their understanding of people. ‘We are people ourselves,’ they think, ‘so we understand people.’ … Engineers make the mistake of thinking that logical explanation is sufficient: ‘If only people would read the instructions,’ they say, ‘everything would be all right.’”
That’s not a critique of some other engineer. That’s a transcript of my internal monologue every time I closed a ticket as “works as intended.”
The trap is the we’re people too, so we understand people fallacy. We don’t. We understand the system from the inside, intimately. The user meets it cold, from the outside, with a completely different model in their head. “Be more logical” has never once made a confused person less confused.
The first installment of signal debt is the one engineers don’t even register as debt: the happy path is where we live, the error states are where users live. Norman’s instruction is blunt: ”designers need to focus their attention on the cases where things go wrong, not just on when things work as planned.” We optimize the wrong region of the space and then bill the user for our blind spot.
Installment 2: the affordance is free, the signifier is the debt
This is the distinction I’d tattoo on every product team.
An affordance is what’s possible, a relationship between an object and a user. A chair affords sitting. The subtle part: an affordance is not a property of the object, it’s a relationship. ”Whether an affordance exists depends upon the properties of both the object and the agent.” A drag-to-reorder list affords reordering to a mouse user and affords nothing to someone on a screen reader. You didn’t change the code. You changed the agent, and the affordance evaporated. This is exactly why accessibility isn’t a feature you bolt on at the end.
A signifier is the clue that tells a human what to do and where:
”Affordances determine what actions are possible. Signifiers communicate where the action should take place.”
Now hold that against any AI product from the last two years. Your model affords summarizing, translating, writing SQL, drafting emails, extracting structured data, an effectively infinite set of affordances. And what signifies any of it? A blank text box with a blinking cursor. The most capable software humans have ever built, wearing the interface of a 1970s command line.
That’s the entire “AI is hard to use” complaint, decoded. It was never a capability problem. It’s a signifier problem. The capability is the free 90%, already sitting in the code. The signifiers are the 90% we skip. As Norman says, ”in design, signifiers are more important than affordances, for they communicate how to use the design.”
There’s a moment where a young designer shows off swipe gestures and calls them “affordances.” His mentor corrects him: the swipes afford nothing new, the actions were always possible. ”So call them by their right name: signifiers.” I have shipped that exact mistake. I added a keyboard shortcut and called it a feature. The capability existed the whole time. What I built, late and badly, was the signifier. That gap between the free affordance and the unbuilt signifier is signal debt, line item two.
Installment 3: digitizing deletes signals you never knew you had
This is the one I didn’t see coming, and the one that gave the whole pattern its name.
Norman splits signifiers into two kinds. Some are placed on purpose, like a PUSH sign on a door. Others are accidental: nobody planned them, but the world leaks the clue anyway, for free. His example is a physical book. The stack of pages under your right thumb tells you how much is left. Nobody designed that. The physical structure just emits the information.
Then:
”Electronic book readers do not have the physical structure of paper books, so unless the software designer deliberately provides a clue, they do not convey any signal about the amount of text remaining.”
Read that as an engineer and it becomes a law. Every time you move a process from the physical world into software, you delete all of its accidental signifiers for free, and you only get back the ones you explicitly rebuild. That deletion is the moment you go into debt.
The paper form told you it was three pages by its weight. The web form tells you nothing unless someone codes “Step 2 of 5.” The physical queue showed you twelve people ahead. The API request shows nothing unless someone returns a progress event. We celebrate digitization for removing physical friction and rarely notice we deleted a hundred free signals along with it.
AI is the most extreme case I’ve worked on. A model thinking for eight seconds emits zero accidental signal. No spinning disk, no whirring fan, no paper feeding through. Nothing. If you don’t manufacture every streaming dot, every token-by-token render, every “analyzing your request…”, the user experiences a void and concludes it’s broken.
Installment 4: the 100ms line is a hard spec, not a vibe
Norman, on feedback:
”Feedback must be immediate: even a delay of a tenth of a second can be disconcerting. If the delay is too long, people often give up, going off to do other activities.”
I’d read a dozen “users hate slow apps” posts and filed them under nice to have. Norman gives the actual number, and it isn’t his alone: roughly 100 milliseconds is the boundary where an effect still feels caused by your action. It traces back to Robert Miller’s 1968 response-time research and shows up again in Jakob Nielsen’s response-time limits, the line under which a user feels they directly caused the change on screen. Past it, the causal link breaks and the brain starts asking “did that even work?”
The 100ms line: under it, action and effect feel connected; over it with no signal, the user assumes failure and retries, even when the operation succeeded.
For a backend engineer this reframes feedback from polish into correctness. Watch what happens past 100ms with no feedback:
The user clicks Pay again, because nothing happened. Now you have a duplicate charge, caused by a successful request with no signifier of success.
- The user refreshes checkout. Your idempotency key had better be real.
- The user files a bug that says “it’s broken,” about an operation that completed perfectly.
The action worked. The absence of feedback made it indistinguishable from failure. Every async call, every LLM completion, every cold-start function lives on the wrong side of 100ms by default. Unless you put a signal there, success and failure look identical from the only seat that matters. Feedback design isn’t decoration on top of the system. It is the system, from where the user stands. This is signal debt with the shortest payment window: you owe it inside a tenth of a second or the user assumes default.
The gap is the bug, and the gap is yours
Here’s the line that should go on the wall of every engineering team: when there’s a gap between what a thing can do and what it tells you it can do, users blame themselves. They feel stupid in front of your software. They are not stupid. They’re reading your unpaid balance.
So here’s how I now pay down signal debt, as concrete practice:
Stop defending the happy path. Audit error states and empty states first. That’s where users live.
2. Separate affordance from signifier in your own head. The capability is the easy part. For every feature, ask: what signals it exists? If the answer is “the docs,” you haven’t shipped a signifier.
3. Count what digitization deleted. For any flow you moved into software, list the accidental signals the physical version gave for free. Rebuild the ones that mattered.
4. Treat 100ms as a hard line. If an action can cross it, it emits feedback before it does, not after it completes.
5. Audit every screen against three questions. Are the possible actions visible? Do controls map to their effects? Does every action over 100ms emit feedback?
Run that checklist against your last AI feature and you’ll find unpaid debt in minutes, the async call with no signal, the agent that thinks in silence, the success that looks identical to failure. The engineers who ship AI to production without getting burned aren’t smarter about models. They just stopped leaving signals unpaid. That’s the whole edge, and it costs nothing but the discipline to name the debt before the user does.
I came to Norman expecting a design book. I left with a debugger for the bugs I kept closing as “works as intended.”
If you’ve shipped one of those, the duplicate charge, the false “it’s broken,” the AI that wasn’t actually broken, reply and tell me which installment bit you. I’m reading the rest of the book this way, turning each chapter into a production model, and the replies are where the sharpest ones come from.


