Part
4
  |  
Designing the Experience
  |  
Chapter
13

Flows Before Screens

Design the path, not the page
Reading Time
13
mins
BACK TO DESIGN FOR DEVELOPERS

You design screen by screen because that is how your code is organized. One route, one component, one file. You polish the signup page until it shines, then the dashboard, then the settings panel, each one a tidy island. Then you wire them together with navigate() and redirect() calls, ship it, and watch real users get stranded between two screens that each looked fine on their own.

The product never breaks on a screen. It breaks in the gaps. A user finishes signup and lands on an empty dashboard with no idea what to do next. They create their first project and the app dumps them back to a list instead of into the thing they just made. They hit "Save" and nothing visibly happens, so they hit it again. Every one of those failures lives in a transition — the seam between one screen and the next — and you can't see seams when you design one page at a time.

The trap: a screen is a noun, an experience is a verb

A screen is a static artifact. You can open it in isolation, center the card, fix the spacing, and call it done. That feels like progress because it is measurable — this page looks better than it did an hour ago. But users don't experience pages. They experience getting something done, which means moving through a sequence of pages while holding an intention in their head.

The intention is the product. The screens are just where the intention briefly touches the interface. When you design screens in isolation, you optimize the touchpoints and ignore the thing connecting them — which is exactly the part the user actually feels.

Think about what a user remembers after using your app. They don't remember that the form had nice padding. They remember "I signed up and it took forever to figure out where to put my API key." That memory is a flow memory. It spans four screens and two redirects, and not one of those screens was individually bad.

The product never breaks on a screen. It breaks in the gaps between them, where the real experience lives.

This is the same mistake as designing your navigation around your database tables — what the Don't Ship Your Schema framework warns against in the next chapter. There, the structure leaks from the data model into the UI. Here, the structure leaks from your file tree into the experience. Both come from organizing the product around how you built it instead of around what the user is trying to do.

The fix is to design the journey first and let the screens fall out of it. Get the path right, then build the pages it passes through.

Framework · Happy Path First · HPF

Design the single most common successful journey end to end before you polish any one screen or handle any edge case. Get the spine right, then add ribs. The happy path is the one sequence of steps a typical user takes to reach the outcome they came for — no errors, no detours, everything works.

Spine, then ribs. The spine is the load-bearing sequence: the exact route from "user arrives wanting X" to "user has X." If the spine is crooked, no amount of rib-polishing saves the body. If the spine is straight, the ribs are easy.

Map the happy path as intentions, not screens

Before you open your editor, write the main flow as a list. Not a list of screens — a list of what the user is trying to do at each step. Use verbs. Force yourself to describe the intention, not the interface, because the interface is a decision you make later and the intention is a fact you discover.

Here is a happy path for the core flow of an invoicing app, written as intentions:

1. I want to bill a client.
2. I pick which client.
3. I add the line items I'm charging for.
4. I confirm the total looks right.
5. I send it.
6. I see proof it was sent.

Six intentions. Notice there is not one word about screens, modals, or buttons yet. That is the point. This list is the spine. It is short enough to read aloud, and if you read it aloud and it sounds like a sane thing a human would want to do in order, the spine is straight.

Now — and only now — map screens onto the intentions. Sometimes one screen serves several intentions. Sometimes one intention needs its own screen. You decide that after you know the sequence, not before.

1. I want to bill a client.   → [button on dashboard]
2. I pick which client.        ─┐
3. I add line items.           ─┤ one screen: the invoice editor
4. I confirm the total.        ─┘
5. I send it.                  → [send button + confirm]
6. I see proof.                → success state on the invoice

Six intentions collapse into roughly three surfaces. That collapse is a design decision you can now make deliberately, because you are looking at the whole path at once instead of inventing screens one at a time and hoping they connect.

Key takeaway

Write the flow as a numbered list of user intentions in plain verbs before you name a single screen. The screens are the answer; the intentions are the question.

Writing intentions first also surfaces the entry point, which screen-first design almost always forgets. Step one here is "I want to bill a client" — but where does that desire become a click? On the dashboard? In an empty state? From an email? The entry point is part of the flow, and if you design screens in isolation you will build a beautiful invoice editor that no user can find the door to.

The entry point is step zero

Every flow has a "step zero": the moment the user decides to start. Write it down explicitly. If you can't name where the journey begins, your users won't be able to find it either, and the most polished flow in the world is worthless if nobody can enter it.

Count the steps, then cut

Once the happy path is written as a sequence, you have something you can measure: its length. And length is the enemy. Every step is a chance for the user to stop, get confused, or wander off. Every screen is a page load and a fresh decision. Every decision is a fork where someone hesitates.

Treat the step count as a budget you are trying to spend down. Go through the list and ask of each line: can this disappear, merge, or get a default?

There are three moves that shorten a flow, in order of power:

  • Delete the step. Does the user actually need to do this, or did you add it because it was easy to build? An "are you sure?" confirmation on a reversible action is a deletable step.
  • Merge the step. Two screens that each ask for a little can often become one screen that asks for both. Picking a client and adding line items don't need separate pages.
  • Default the step. If there's an obvious answer 90% of the time, pre-fill it and let the user override. Invoice date defaults to today. Currency defaults to their account setting. Due date defaults to net-30.

Defaulting is the quiet superpower. A step the user can skip because you already answered it is nearly free. This is the same instinct behind the Field Tax framework for forms — every field you can remove or pre-fill is friction you delete — applied to the whole journey instead of a single screen.

Treat the step count as a budget you are trying to spend down. Every step is friction; the shortest correct path wins.

Watch a concrete before-and-after. A typical "create a project" flow, designed screen by screen, sprawls. The same flow with the step budget enforced collapses.

✕ Screen-by-screen (7 steps)
  • Click 'New Project'
  • Choose a project type from a grid
  • Name the project on the next screen
  • Pick a team on the next screen
  • Set visibility (public/private)
  • Confirm on a review screen
  • Land on an empty project page
✓ Happy path enforced (3 steps)
  • Click 'New Project'
  • Type a name in one inline field (type defaults to 'blank', team defaults to current, visibility defaults to private)
  • Land inside the new project, ready to work

Seven steps became three, and nothing important was lost — the rare user who wants a non-default type or team can change it inside the project, where it matters less and interrupts no one. The left column wasn't badly designed screen by screen. Each of those seven screens could be pixel-perfect. It was badly designed as a flow, and that is the only level at which the problem is visible.

Don't confuse fewer steps with fewer screens

Cutting steps does not mean cramming everything onto one giant screen. A wizard with three calm screens often beats one screen with thirty fields. The goal is fewer decisions and dead-ends, not the smallest possible page count. Sometimes the shortest-feeling path has more screens, each one trivially easy.

Then — and only then — handle the branches

Here is where engineers get nervous, because your training screams that you must handle every case. Null states. Network failures. The user who has no clients yet. The expired card. The duplicate name. All of it real, all of it eventually necessary.

But not yet. The happy path comes first on purpose, because if the spine is wrong, every branch you built hangs off a broken structure and has to be redone. Get the main success flow working end to end — clickable, coherent, complete — before you spend a single hour on the case where the API times out.

Once the spine is solid, then walk back through it and ask at each step: what else could happen here?

Step 2 — pick a client:
  happy:  they pick from a list
  branch: they have no clients yet  → inline "add client" path
  branch: client search finds nothing → "create new" suggestion

Step 5 — send the invoice:
  happy:  it sends, they see confirmation
  branch: send fails (network)      → error state, retry, draft saved
  branch: missing required field    → inline validation before send

This is where the Four States Rule comes back in force. Every screen along the path needs its empty, loading, error, and ideal states designed — but you design them for a path that already works, so each state has a clear job: get the user back onto the happy path, or tell them honestly why they can't be.

The discipline is sequencing, not neglect. You are not skipping edge cases. You are refusing to design them before you know what they are edge cases of. A branch only makes sense relative to a trunk.

Key takeaway

Edge cases are branches off a trunk. Build the trunk first, or every branch is attached to something you'll tear out.

There is a second reason to delay the branches: most of them never get hit at the volume you fear. The user with zero clients exists for about ninety seconds, once, at the very start — which is precisely the Time to First Win problem, and it deserves real attention. But the user whose third concurrent invoice fails to send because of a rare race condition? Design a clean error state, log it, move on. Spend your design hours where users spend their time, and users spend almost all their time on the happy path. That is what makes it the happy path.

Prototype the flow before you build the UI

The whole argument so far is that flows beat screens — so the worst way to test a flow is to build the screens. Building real UI is the most expensive way to discover that step four shouldn't exist. You will have written the component, styled it, wired the route, and your sunk cost will fight you when the flow tells you to delete it.

Prototype the path in the cheapest medium that still shows the sequence. In rough order of cost:

  • The numbered list. You already have it. Read it aloud. Half of all flow problems are audible — "and then they pick the team, and then they confirm, and then…" If it sounds tedious to say, it is tedious to do.
  • Boxes and arrows. One box per screen, arrows for transitions, a scribbled note for what each screen asks. Paper, a whiteboard, or a Figma frame. Five minutes. Now you can see the seams — the arrows are the transitions you were ignoring.
  • A clickable stub. Plain HTML pages, or empty React routes with nothing but a heading and a button that goes to the next route. No styling, no real data, no logic. Click through your own happy path as if you were the user.

That last one is the highest-leverage thing in this chapter. A clickable stub — gray boxes, placeholder text, working links — lets you walk the entire journey in two minutes and feel every dead-end before any of it is precious.

<!-- the entire "send invoice" flow as a clickable stub -->
<main>
  <h1>Dashboard</h1>
  <a href="/invoice/new">+ New invoice</a>
</main>

<!-- /invoice/new -->
<main>
  <h1>New invoice</h1>
  <p>[client picker] [line items] [total]</p>
  <a href="/invoice/sent">Send</a>
</main>

<!-- /invoice/sent -->
<main>
  <h1>Invoice sent ✓</h1>
  <a href="/dashboard">Back to dashboard</a>
</main>

Three files, no CSS, ten minutes. Click New invoice, then Send, then Back to dashboard. You just walked the spine. If it feels smooth as gray boxes, it will feel smooth in production. If it feels clunky now, no amount of beautiful styling will fix it later — styling makes an ugly flow prettier, never shorter.

The flow problem hides under the polish

Once you build real UI, a clunky flow gets camouflaged by nice typography and smooth animations. It still frustrates users — they just can't tell you why, because each screen looks fine. Catch flow problems while everything is still gray boxes, where the only thing you can possibly be judging is the path itself.

Prototyping the flow cheaply also makes it disposable, and disposability is the whole point. You will delete steps without grief because deleting a line in a list or a box on a whiteboard costs nothing. The moment a flow is made of real components, every deletion feels like throwing away work — and that sunk-cost reflex is exactly what keeps bad steps alive in shipped products.

If the flow feels smooth as gray boxes, it will feel smooth in production. Styling makes a flow prettier, never shorter.

What to do Monday morning

Write your core happy path as a numbered list of intentions

Pick the single most important thing users do in your app. Write it as 4 to 8 steps using verbs that describe what the user wants, with zero mention of screens or buttons. Read it aloud. If it sounds sane, the spine is straight.

Count the screens and decisions on that path

Go through the list and tally two numbers: how many distinct screens the user passes through, and how many decisions or forks they hit. Write both numbers down. This is your friction budget, and now you can see it.

Cut exactly one step

Find one step you can delete, merge into another, or kill with a smart default. Make the change today. The most common win: pre-fill a field the user almost always sets the same way, and let them override it instead of forcing the choice.

Build the path as a clickable stub

Create one bare page per screen — a heading and a link to the next step, no styling, no real data. Click through your own happy path start to finish. Feel every seam before any of it is precious.

List the branches to handle next

Only now, walk back through the working spine and write down what else could happen at each step: empty states, errors, the brand-new user. That list is your edge-case backlog — built on a trunk that already stands, ready for the Four States Rule.

Design the path, not the page. Get the spine right, and the screens take care of themselves.