This is an automated rejection. No LLM generated, assisted/co-written, or edited work.
Read full explanation
Epistemic status: Something I noticed while building things. Probably not new, but I haven't seen it stated clearly anywhere.
TL;DR:
In many systems, user behavior is shaped more by interface design than underlying logic. This creates a gap between what a system allows and what users actually do, which is often missed when debugging.
I've been thinking about a pattern I keep running into, and I'm not sure it gets talked about enough in the way I want to talk about it.
The standard assumption in software development, at least in my experience, is that behaviour follows logic. You write the rules, the system enforces them, and if something goes wrong, you look at the code. This seems obviously correct until you actually watch real people use things you've built.
Here's what I mean. I was working on a data-entry form, nothing fancy, just a form with a few options. I noticed that almost everyone picked the default, even in cases where the default was pretty clearly not the best choice for what they were trying to do. Not a few people. Almost everyone. I changed the default. Same options, same backend, nothing else touched. The distribution of what people picked shifted substantially.
The logic hadn't changed at all. The interface had, and that was apparently what mattered.
Two behavioral envelopes
I've started thinking about this in terms of two separate things a system has:
The first is what I'd call the logical envelope - the space of things the system actually permits. You can't do what the code doesn't allow.
The second is what I'd call the interface envelope - the space of things the interface makes obvious, easy, or default. This is much narrower than the logical envelope in most real systems, and it's the one that most users actually operate inside.
The gap between these two envelopes is where a lot of interesting and frustrating things happen.
For expert users - people who know a system well, have a mental model of how it works, and actively explore - this gap is small. They go looking for things. They override defaults. They read documentation. But most users aren't like that. Most users go with what's in front of them, pick the first reasonable-looking option, and don't dig around. For them, the interface envelope is basically the whole system. Everything outside it might as well not exist.
The interface isn't just displaying choices. It's running a kind of soft policy over which choices get made. It doesn't force anything, but it shapes the probability distribution pretty heavily. And in most real-world systems, that soft policy dominates.
Why this matters for debugging
When something goes wrong behaviorally - users consistently doing something you didn't expect, or not doing something you wanted - the instinct is usually to look for a logic error. That's where developers are comfortable looking. We have tools for it. Linters, tests, type checkers. We're trained to think in terms of what the system permits.
But if the problem is that users are predictably making a particular choice - not doing something impossible, just something suboptimal - then fixing the logic won't help. The logic was never the issue. The issue is what the interface makes easiest.
I think this is an under-recognized category of bug. I don't even have a great name for it - "interface-layer behavioural bug" is the best I've got. The distinguishing feature is that the system is working correctly at the code level while producing wrong outcomes in the world. Fixing logic in this case doesn't just fail to help - it can make things worse, because it makes the system look correct while the actual problem stays hidden.
We don't have good tooling for this. We don't even really have a good vocabulary for it. Which I think is part of why it keeps getting missed.
The obvious objection
Someone will reasonably point out that logic ultimately bounds everything - users can only do what the system allows. True. I'm not disputing that. What I'm saying is that within the space of allowed actions, the interface determines which ones actually happen. Most real failures aren't users doing impossible things. They're users predictably doing the suboptimal thing that the interface made easiest.
Where this probably doesn't apply
The more constrained the system and the more expert the users, the less this matters. If there are only two options, the interface can't do much to bias the choice. If users are domain specialists who actively investigate what a system can do, defaults matter less.
This is most relevant for systems with lots of options, casual users, and low-stakes individual decisions - which is, honestly, most consumer software.
What I actually think the takeaway is
Logic defines what a system can do. The interface defines what users actually do. In a lot of real systems, the second matters more than the first for predicting outcomes.
When something is going wrong behaviorally, the interface layer deserves to be treated as a first-class suspect - not an afterthought after the logic checks out. The system that exists in the world includes both.
The corollary that I find most practically useful: you can often improve outcomes without touching the logic at all. Sometimes the fix is just changing what's default.
Epistemic status: Something I noticed while building things. Probably not new, but I haven't seen it stated clearly anywhere.
TL;DR:
In many systems, user behavior is shaped more by interface design than underlying logic. This creates a gap between what a system allows and what users actually do, which is often missed when debugging.
I've been thinking about a pattern I keep running into, and I'm not sure it gets talked about enough in the way I want to talk about it.
The standard assumption in software development, at least in my experience, is that behaviour follows logic. You write the rules, the system enforces them, and if something goes wrong, you look at the code. This seems obviously correct until you actually watch real people use things you've built.
Here's what I mean. I was working on a data-entry form, nothing fancy, just a form with a few options. I noticed that almost everyone picked the default, even in cases where the default was pretty clearly not the best choice for what they were trying to do. Not a few people. Almost everyone. I changed the default. Same options, same backend, nothing else touched. The distribution of what people picked shifted substantially.
The logic hadn't changed at all. The interface had, and that was apparently what mattered.
Two behavioral envelopes
I've started thinking about this in terms of two separate things a system has:
The first is what I'd call the logical envelope - the space of things the system actually permits. You can't do what the code doesn't allow.
The second is what I'd call the interface envelope - the space of things the interface makes obvious, easy, or default. This is much narrower than the logical envelope in most real systems, and it's the one that most users actually operate inside.
The gap between these two envelopes is where a lot of interesting and frustrating things happen.
For expert users - people who know a system well, have a mental model of how it works, and actively explore - this gap is small. They go looking for things. They override defaults. They read documentation. But most users aren't like that. Most users go with what's in front of them, pick the first reasonable-looking option, and don't dig around. For them, the interface envelope is basically the whole system. Everything outside it might as well not exist.
The interface isn't just displaying choices. It's running a kind of soft policy over which choices get made. It doesn't force anything, but it shapes the probability distribution pretty heavily. And in most real-world systems, that soft policy dominates.
Why this matters for debugging
When something goes wrong behaviorally - users consistently doing something you didn't expect, or not doing something you wanted - the instinct is usually to look for a logic error. That's where developers are comfortable looking. We have tools for it. Linters, tests, type checkers. We're trained to think in terms of what the system permits.
But if the problem is that users are predictably making a particular choice - not doing something impossible, just something suboptimal - then fixing the logic won't help. The logic was never the issue. The issue is what the interface makes easiest.
I think this is an under-recognized category of bug. I don't even have a great name for it - "interface-layer behavioural bug" is the best I've got. The distinguishing feature is that the system is working correctly at the code level while producing wrong outcomes in the world. Fixing logic in this case doesn't just fail to help - it can make things worse, because it makes the system look correct while the actual problem stays hidden.
We don't have good tooling for this. We don't even really have a good vocabulary for it. Which I think is part of why it keeps getting missed.
The obvious objection
Someone will reasonably point out that logic ultimately bounds everything - users can only do what the system allows. True. I'm not disputing that. What I'm saying is that within the space of allowed actions, the interface determines which ones actually happen. Most real failures aren't users doing impossible things. They're users predictably doing the suboptimal thing that the interface made easiest.
Where this probably doesn't apply
The more constrained the system and the more expert the users, the less this matters. If there are only two options, the interface can't do much to bias the choice. If users are domain specialists who actively investigate what a system can do, defaults matter less.
This is most relevant for systems with lots of options, casual users, and low-stakes individual decisions - which is, honestly, most consumer software.
What I actually think the takeaway is
Logic defines what a system can do. The interface defines what users actually do. In a lot of real systems, the second matters more than the first for predicting outcomes.
When something is going wrong behaviorally, the interface layer deserves to be treated as a first-class suspect - not an afterthought after the logic checks out. The system that exists in the world includes both.
The corollary that I find most practically useful: you can often improve outcomes without touching the logic at all. Sometimes the fix is just changing what's default.