The Happy Path Doesn't Exist: Notes on Software Fluidity
ELI5/TLDR
Designers love the “happy path” — the clean storyboard where the user does the right thing in the right order and everything works. The problem is the language. Once you call the ideal flow the happy path, everything else becomes “edge cases,” which is a polite way of saying “we’ll deal with it later, which means never.” Saleh walks through designing a digital egg timer — the simplest time-bound thing imaginable — and shows that even three states (idle, running, elapsed) hide dozens of real decisions: pause behavior, OS-level notifications, re-entry from background, multiple timers, AI-inferred durations, screen reader users. The point isn’t the timer. The point is that software is always in motion, and most of what it’s doing isn’t on the happy path.
The Full Story
The vocabulary problem
The video opens with a vocabulary critique. “Happy path” sounds neutral — it’s just shorthand for the ideal flow. But the term creates a shadow: everything not on it gets called an “edge case.” And edge means marginal, peripheral, unlikely. Once you’ve named something that way, you’ve already decided it can wait.
“Edge cases fall through the cracks not because teams lack skill, but because their scenario is one designers would rather not consider.”
Saleh credits this framing to a Nielsen Norman Group analysis, but his angle is sharper: the problem is dispositional, not procedural. Software doesn’t experience the distinction between happy path and edge case. A paused state is just as real as a running one. An error state is just as present as a success state. The system spends most of its life in conditions the designer never explicitly drew.
Why AI generation makes it worse
The diagnosis predates AI tools by decades, but generation tools intensify it. You can spit out a component library, a visual language, and a set of plausible screens in minutes. The surface arrives fully formed before any structural questions have been asked. The result looks finished, which makes the gap between what’s visible and what’s actually been designed harder to see than ever.
The egg timer
To make the abstract concrete, Saleh designs a digital egg timer in his head — no Figma, no code, no generation. The point is to follow every design question wherever it leads.
Stripped down, an egg timer has three states: idle, running, elapsed. Two transitions: start, finish. That’s the atom. And even here, decisions cannot be avoided.
When the timer hits zero, does it hold at “elapsed” until you dismiss it, or auto-reset? Hold-until-dismissed assumes you need to acknowledge completion. Auto-reset assumes you’re already thinking about the next thing. Two different theories of how people end a task.
Does the countdown tick once a second or every half second? One feels stable and deliberate. The other feels alive and slightly anxious. Neither is neutral.
“The interface is the rendering of choices about state and transition. Design is not choosing what things look like. Design is choosing what the system does and then making that visible.”
Input modality is a worldview
Before you can run the timer, someone has to set it. The physical egg timer solved this with a dial — one affordance, one gesture. Software opens the question back up, and every answer encodes a model of who’s using it.
A number pad assumes precision. A scroll wheel assumes approximation. Presets (“soft, medium, hard”) assume your need maps onto a fixed vocabulary. Voice assumes you’d rather speak than tap. Each modality has a failure mode invisible in the interface itself. Presets fail silently. Number pads accept ambiguous input — a “5” could be five minutes or five hours. Voice fails through misrecognition with no error at all, just a wrong timer running while you’re not watching.
Even the empty first-launch screen is a decision. Empty field? Suggested value? Last used duration? There is no neutral option. Every blank screen is already a position.
The states no one draws
Once the timer is running, the flow stops being linear. Pause looks like a button but it’s actually a relationship with the operating system — on mobile, the OS is what fires the alert, so pausing means cancelling that scheduled notification and creating a new one. The pause button and the notification system are not separable.
Extend by 30 seconds (the microwave model) is intuitive until you tap it accidentally with 7 seconds left. Cancel and reset are not the same operation — cancel is abandonment, reset is preparation — but they get treated as the same all the time.
Re-entry is the one almost no design deliverable specifies. The user left the app. Time passed. They come back. What do they see? Not just current time remaining, but the elapsed time made legible at a glance. This isn’t on the happy path flow but it’s the first thing many real users encounter.
Errors are states, not failures
This is the line that does the most work in the whole video:
“Errors are not failures of the system. They are states of the system. And states require design.”
A poorly designed error like “something went wrong” is a state with no exit. A well-designed one names the condition and opens at least one path out. The egg timer’s error surface is small but instructive: a duration of zero (why was it allowed?), 3 hours instead of 3 minutes, partial input followed by a context switch, system clock changes mid-timer, notification permissions revoked silently, device restarts that lose pending alerts. Saleh acknowledges these get absurd, but the principle holds: every condition the system can encounter is a state, and undesigned states are not absent — they’re present as undefined behavior, which in user experience looks like confusion.
Notifications are presence
When the timer completes while you’re looking somewhere else, the design problem stops being about the interface and becomes about presence — the system’s ability to make itself known when its screen isn’t visible.
The technical detail here is interesting. On iOS, the standard pattern is to schedule the alert at the OS level when the timer starts. So if the user cancels and the app is force-quit before the cancellation propagates, the alert fires anyway — a “ghost notification” for a timer that no longer exists. On Android, scheduled alerts are cleared when the device restarts unless the app explicitly registers for the reboot event and reschedules. Without that registration, all running timers silently disappear after a restart.
Designing the timer without designing the notification is designing half a system.
Two timers is a different problem
Add a second timer (eggs and pasta) and the state machine forks in ways with no single-timer analog. Three timers require a list. Layout becomes a structural decision about representing concurrent processes. Labels like “timer 1” tell you nothing when your hands are wet. User-defined labels add friction to every timer for the benefit of the minority running multiple. List order — creation, time remaining, completion — produces different expectations and different bugs. If the list reorders as timers approach completion, the UI is in motion exactly when the user needs to track everything.
When the timer becomes a calculation
Push it further: the timer knows about eggs. Now duration is a function of doneness, size, starting temperature, water state, and altitude. A preset row is fast and silently wrong for anyone with refrigerated eggs, cold start water, or living above 1500 meters. Adding inputs for all five variables recovers accuracy but adds friction to every use, most of which won’t need it. Speed and accuracy are in genuine tension. The design has to take a position.
The AI layer breaks the line between intent and action
This section is the most useful part of the talk for anyone building with LLMs. Up until now, every state had a shared assumption: the user sets the duration, the system receives a value, the timer starts. Even ambiguous input is still input. The line between intent and action is direct.
The AI layer breaks that line. “I’ve got large eggs straight from the fridge, I want them jammy.” The system doesn’t receive a duration — it receives language and produces an interpretation. The timer that starts is not the one the user set. It’s the one the system inferred they wanted.
“An inferred state introduces a structural gap between what the user believes the system understood and what the system actually computed.”
Confirmation doesn’t fix it. Showing “7 minutes” surfaces the output of the inference but not the reasoning behind it. The user sees a number they didn’t enter with no visibility into the parameters that were assumed or the confidence behind the choice. They confirm or they don’t. Either way the inference process is opaque.
The deeper shift: the interface stops being a surface the user acts upon and becomes a surface the user supervises. That’s a fundamentally different cognitive relationship — and it requires interfaces that make inference legible, not just its output. Anyone who has used “planning mode” in coding agents will recognize this exact problem.
Same state machine, different rendering
A user navigating by screen reader encounters the same timer and the same states, but through a completely different output channel. Visual state changes are immediate and peripheral — the eye catches them. Audio state changes are interruptions. Multiple parallel timers are easy to scan visually but linear through audio, so the system has to decide what to announce, when, and how often.
The framing matters: these aren’t accessibility accommodations bolted on top. They’re the same design questions asked again through a different medium.
The fix is sequencing
The closing section is concrete. None of this requires new tools. What’s required is that state thinking precede surface thinking.
- Start from states, not screens. Map what the system can be before any visual decision.
- Rename edge cases. Call them alternate states, or just states. The vocabulary moves them out of the residue phase and into the structural phase where decisions are still available.
- Design the re-entry, not just the flow. Returning mid-session is often the first state real users encounter.
- Test by leaving the path deliberately. Pause halfway. Enter nothing. Switch away. Force completion while looking elsewhere. Every undefined response is a missing state.
- Treat error states as primary UI. An error message isn’t a label appended to a working interface — it is the interface at that moment.
“The sequence is not a minor adjustment. It is the difference between designing a system and decorating one.”
Key Takeaways
- The phrase “edge case” is doing real damage. It pre-decides that something will be handled later, which usually means never.
- A system’s state machine exists whether you’ve drawn it or not. Undesigned states show up as undefined behavior, which the user experiences as confusion.
- Pause is not a button — it’s a relationship with the operating system. Notifications are not UI elements — they’re how the system maintains presence when its screen is dark.
- AI-inferred state is structurally new: the gap between what the user meant and what the system computed has no good interface yet. Confirmation isn’t enough.
- Accessibility is not an accommodation; it’s the same state machine rendered through a different channel.
- The fix is sequencing: state thinking before surface thinking.
Claude’s Take
This is the best 28 minutes of design talk I’ve watched in a long time. Saleh is doing something rare — taking a piece of received vocabulary (“happy path,” “edge case”) and showing that the words themselves are doing damage. The egg timer is a brilliant choice precisely because nothing about it should be hard, which makes the cascade of real decisions feel inevitable rather than contrived.
The AI section is where the talk earns its keep. Every other state in the timer has a shared frame between user and system: a paused timer is unambiguously paused. Inferred state is the first thing in software where the system’s understanding and the user’s understanding can structurally diverge with no interface signal. The “user as supervisor instead of actor” framing is going to age well — it names something that anyone using coding agents has felt without having a word for.
Score is 9. Knock for being slightly long-winded in the middle stretch (the multiple-timer section repeats beats), but the framework is genuinely useful and the writing is sharp. The line “designing a system versus decorating one” should be on a poster in every product team’s office.
Further Reading
- Nielsen Norman Group — research cited in the video on where products consistently fail; their site has decades of UX research worth grazing.
- Don Norman, The Design of Everyday Things — original source of the “affordance” vocabulary Saleh uses; still the reference.
- State machines and statecharts — David Harel’s original 1987 paper on statecharts, or the more accessible XState documentation, both formalize the “state map vs flow diagram” distinction Saleh is gesturing at.
- Interface Studies — Saleh’s channel; this is the kind of thinking that doesn’t get made often enough.