Screens and flows
A spec says what the app is for. A flow says what the shop owner does, screen by screen, and what the app shows when the network fails halfway. In this lesson you turn the reorder spec into a flow, sketch the screens on paper, and test them in a partner shop before anyone writes code.
By the end you can
- Write a user flow for one task: the happy path, its branches and its tap count
- Sketch low-fidelity screens with one main job each, and every state each screen can be in
- Design what the app shows when an order is slow, fails, or might have gone through
- Run a 20-minute test of a paper prototype with a shop owner and turn your notes into ranked changes
- List the data each screen needs, ready for the data model in the next lesson
The everyday picture
When Mangrove Market opens a new store, the store manager does not order the shelves and checkout counters first. She marks the layout on the empty floor with masking tape: the entrance here, fruit and vegetables there, two tills by the door. Then she asks a cashier to push a full trolley along the tape, from the entrance to the till and out again. The cashier bumps into a corner, finds that the queue blocks the exit, and asks where the empty baskets go.
Moving a strip of tape takes a minute. Moving a counter that is bolted to the floor takes a week. Screens and flows are the tape on the floor for an app. A sketch on paper is cheap to change, while code that is written, tested and released is the bolted counter. This lesson is about walking the path with a real person while it is still tape.
From the spec to a user flow
In From idea to one-page spec you wrote the smallest useful version of the reorder app: Ravi signs in, sees his last orders, repeats one in three taps with quantities he can adjust, sees the order's status and gets a weekly summary. A spec lists what the app must do. It does not say in which order things happen, what the owner sees at each moment, or what happens when something goes wrong. A flow does.
A user flow is the path one user takes through the app to finish one task. Write it as numbered steps, and give each step three parts: the screen the user sees, the action they take, and what the app does in reply. Write one flow per task, and start from the user's goal in their own words: "Order the same as last Monday, with two more cases of cooking oil." Then write the steps.
- Home: Ravi sees his last three orders as cards. He taps last Monday's order. (tap 1)
- Order detail: he sees the 22 items and the total. He taps Repeat. (tap 2)
- Review: the same items appear with last week's quantities. He changes cooking oil from 6 to 8 cases and the total updates. He taps Place order. (tap 3)
- Status: the app shows that Mangrove received the order and the delivery day.
That list is the happy path: everything works and the user makes no mistakes. It is where you check the spec's promise. Count the taps. The "three taps" in the spec are open, repeat and place. Changing a quantity costs an extra tap or two, and that is fine because it is optional. Writing the count down early stops a common drift, where each review adds "one small confirmation" until the core task takes seven taps.
Then go back over each step and ask what else could happen there. Ravi has no orders yet. An item from last week is out of stock. The price of rice went up. The network drops while he taps Place order. Each answer is a branch, another way out of that step. You do not need to design every branch today, but you must list them, because each one becomes a screen state, a rule in the API or a test later. A good rule for a first flow: one happy path, and for each step the two or three branches that will happen every week.
Quick check
The spec says "repeat an order in three taps". Which change breaks that promise?
The pop-up adds a fourth tap to every order, even for owners who change nothing. The stepper is optional, and the total adds information without adding a tap. If you worry about mistakes, a clear Review screen with the total on the button already is the confirmation.
Sketching screens: one job each, every state
Now draw each step as a screen, on paper, on a whiteboard or as boxes in a design tool. Keep it at low fidelity: grey boxes, real words, no colours or logos. Low fidelity helps you. People criticise a rough sketch more freely than something that looks finished, and you can redraw a sketch in two minutes while the shop owner watches.
Give each screen one main job and one main action. The Review screen's job is "check this order and send it", and its main action is the Place order button. Everything else on the screen supports that job: the item list, the steppers, the total. Put the information a user needs for a decision on the same screen as the decision. Ravi decides whether to send the order partly from the total, so the total belongs on the button itself: "Place order, $412.60". Use real data in sketches: real product names, real quantities, prices with cents. Placeholder text like "Item 1" hides the very problems you are looking for, such as a long product name that wraps onto three lines on a small phone.
Every screen that loads data also has several states. Sketch each one, because each needs its own words and layout:
- Loading: the data is on its way. On patchy mobile data this can last several seconds, so show what you can, such as the orders saved on the phone last time, instead of a blank screen.
- Empty: there is nothing to show yet, as for a new shop with no orders. Say what will appear here and what to do next.
- Error: loading failed. Say what happened in plain words and offer one action, usually Try again.
- Partial: some of it works, such as an order where two of the 22 items are out of stock.
- Normal: the screen as you first imagined it.
Draw for the real conditions. Partner shop owners use their own phones, often cheap Android phones with small screens, used with one hand while a customer waits at the counter. Keep the main button near the bottom where a thumb reaches it, and make tap targets large. Android's accessibility guidance asks for at least 48 by 48 density-independent pixels, and Apple's asks for at least 44 by 44 points. Design for PMs and Designing for frontline work go deeper into hierarchy, accessibility and working conditions.
Quick check
A new partner shop signs in for the first time and has never ordered through the app. Which state of the Home screen does the owner see, and what should it do?
Nothing failed, and there is nothing to wait for, so error and loading both mislead the owner. An empty state tells him what the screen is for and what to do next. It is the state teams forget most often, because their test accounts always have data.
Designing the unhappy path: slow, failed or unknown
The most important branch in the reorder flow comes after Ravi taps Place order. On a fast connection the reply comes back in under a second. In a shop with patchy mobile data, one of three things happens. The server replies that the order is placed. The server replies with an error. Or no reply arrives before the app stops waiting, which is called a timeout. The third case is the dangerous one, because the app does not know whether the order reached the server. The request may have been lost on the way, or the order may have been saved and only the reply lost.
If the screen says "Failed, try again" in that case, Ravi taps again and may get two deliveries of 22 items. If it says "Placed", he may wait for a delivery that never comes. Neither is acceptable for an app whose purpose is to be more reliable than a phone call, when about 1 phone order in 12 is corrected after delivery today. So the flow needs a third message, such as "Checking your order", and a rule: the app asks the server whether the order arrived before it offers to send it again.
Protect the screen from double taps, too. From the moment Ravi taps Place order, the button shows "Sending" and cannot be tapped again. Each order attempt gets an idempotency key, a unique ID the app creates once and sends with every retry of that same order, so the server can recognise a repeat and return the first result. You will build the server side in Data model and API and the app side in Building the app, and the idempotency lesson in the API course goes deeper. At this stage your job is to put the states on paper, so the people who build them know they exist.
Quick check
Ravi taps Place order, and after a long wait the app still has no reply. What should the screen do first?
No reply does not mean no order: the server may have saved it and only the reply was lost. Asking first, and reusing the same key on any retry, avoids both a double order and a missing one. Sending again with a new ID is exactly how double orders happen.
Testing the flow with a partner shop
A flow on paper is still your guess. Before anyone writes code, put it in front of the people who will use it. Testing ideas cheaply explains usability tests in full. Here is the short version for one developer and one flow.
Make a paper prototype: one card per screen and per important state, drawn at roughly phone size. You play the computer. When the owner taps a button with his finger, you put down the next card. When he taps Place order, you put down Sending, wait three seconds, and then put down the result you planned to test, including Not sure. A clickable prototype in a design tool such as Figma or Penpot works too, and it runs on the owner's own phone, which is closer to the real thing. Paper is faster to change between sessions.
Run each session in the shop, at a quiet time the owner chooses, for about 20 minutes:
- Explain that you are testing the design, not him, and that nothing he does can be wrong.
- Give one task as a goal in his words: "Your delivery comes Thursday. Order what you ordered last Monday, with two more cases of cooking oil." Never name the button.
- Ask him to think aloud, and do not help. If he asks what to do, ask what he would try if you were not there.
- At each step, write down where he hesitated, what he tapped that did nothing, what he said and how many taps it took.
- At the end, ask what he expected to happen after Place order, and how he would know the order went through.
Three to five shops, in one or two days, will show you most of the serious problems. Then sort what you saw by severity. A blocker stops the task or makes the owner finish it wrongly. A slowdown makes the task slow or uncertain. A cosmetic problem is everything else. Fix every blocker in the sketches, then run a second small round with different owners to check that the fixes worked. Keep your notes and the list of changes: the course project asks for them.
Build it
Produce the three working documents for the reorder flow: the written flow, a screen inventory with the data each screen needs, and a test script. Keep them as Markdown files in a docs/ folder of your project repository, next to the spec from app-01, so they change in pull requests like code.
-
Write the flow
One file per task. Start with the goal in the user's words and the tap budget, then the happy path, then the branches, each tagged with the step it leaves from.
Markdown# Flow: repeat last order Goal (owner's words): "Order what I ordered last Monday, with two more cases of cooking oil." User: partner shop owner, own Android phone, patchy mobile data Start: Home, signed in. End: Status shows "Received". Tap budget: 3 (open, repeat, place). Quantity changes are optional taps. ## Happy path 1. Home: last 3 orders as cards (date, item count, total). Tap a card. [tap 1] 2. Order detail: items, quantities, total. Tap "Repeat and review". [tap 2] 3. Review: same items, last quantities, steppers, total on the button. Tap "Place order, $412.60". [tap 3] 4. Sending: button locked, "Sending". 5. Status: "Received by Mangrove at 10:42", order number, delivery day. ## Branches - 1 Home, no orders yet: empty state, "Your past orders will appear here". - 1 Home, offline: orders saved on the phone, banner "Offline". - 3 Review, item out of stock: shown at 0, greyed, "Not available this week". - 3 Review, price changed: old and new price shown, total updated. - 4 Sending, error reply: "Not sent. Try again." Retry uses the same order key. - 4 Sending, no reply in time: "Checking your order", then Status or Not sent. ## Open questions - What is the cut-off time for Thursday delivery? (ask the DC team) -
Make the screen inventory
One row per screen. The last column, the data each screen needs, is the input for the tables and endpoints in Data model and API. If a screen needs data that no system has, you find out now instead of in week three.
Markdown| Screen | Main job | Main action | States | Data it needs | |----------------|------------------------|-------------------|------------------------------------------|-------------------------------------------------| | Home | pick an order to repeat | open an order | loading, empty, offline, normal | last 3 orders: id, date, item count, total cents | | Order detail | see what was in it | Repeat and review | loading, error, normal | lines: article name, pack size, qty, price cents | | Review | check and send | Place order | normal, partial, sending, not sure, not sent | today's price and availability per article | | Status | know it arrived | back to Home | normal, error | order number, status, received time, delivery day | | Weekly summary | see the week | none | loading, empty, normal | orders this week, total cents, top 5 items | -
Write the test script
Use the same script word for word in every session, so the results can be compared.
Markdown# Test script: repeat last order (about 20 minutes) Before: owner agrees; paper cards at phone size or the prototype on his phone; notes sheet. Say: "We are testing the design, not you. Nothing you do is wrong. Please say what you are thinking as you go. I won't help, because I want to see where the design is unclear." Task: "Your delivery comes Thursday. Order what you ordered last Monday, with two more cases of cooking oil." Notes, per step: screen | what he did | what he said | pause (seconds) | taps If he asks for help: "What would you try if I weren't here?" After the task, ask: 1. "What did you expect to happen when you tapped Place order?" 2. "How would you know the order reached Mangrove?" 3. "What would make you use this instead of phoning your sales rep?" Result: finished alone / finished with help / did not finish -
Summarise the round in numbers
After the sessions, count before you discuss. Each finding gets a severity, how many owners hit it, and the change you will make.
OutputRound 1: 5 partner shops, 1 task, paper prototype, about 20 minutes each Finished alone 2 of 5 Finished with help 3 of 5 Did not finish 0 of 5 Taps, happy path 3 designed, median 5 seen (back-tracking from Review) B1 blocker 3 of 5 expected Repeat to send at once, missed the oil change -> "Repeat and review", Review titled "Check before sending" B2 blocker 2 of 5 tapped Place order twice while Sending -> lock the button, same order key on any retry S1 slowdown 4 of 5 asked how they would know it arrived -> Status shows order number and received time, large text C1 cosmetic 1 of 5 wanted a different icon for the summary Round 2: 3 new shops, after the changes: 3 of 3 alone, no blockers -
Write the flow as data and check the tap count
A flow is a small graph: screens are points and taps are the arrows between them. Writing it as data lets a test check the tap budget whenever someone adds a screen. The Practice exercise asks you to write the function that counts the fewest taps.
JavaScriptconst reorderFlow = { home: [{ tap: 'Open order', to: 'detail' }, { tap: 'Weekly summary', to: 'summary' }], detail: [{ tap: 'Repeat and review', to: 'review' }, { tap: 'Back', to: 'home' }], review: [{ tap: 'Place order', to: 'status' }, { tap: 'Back', to: 'detail' }], status: [{ tap: 'Home', to: 'home' }], summary: [{ tap: 'Home', to: 'home' }], }; // the spec's promise, as a check you can run on every change: // fewestTaps(reorderFlow, 'home', 'status') must be 3 or less
Bring it to your team
Ask these in the review of the sketches, before the first line of app code.
- What is the owner's goal for this flow, in his words, and how many taps does the happy path take?
- For each screen, what do we show while loading, when empty, on error and when only part of it worked?
- What does the owner see if Place order gets no reply, and what stops a double order?
- Who outside the team tried this flow, in their shop, and what did we change because of it?
- Does every screen's data exist somewhere today, and can the API give it in one call?
Red flags
- Polished, coloured screens before anyone has tried the flow with a shop owner.
- Only the happy path is drawn, and errors are "for later".
- The test was run with teammates or sales reps instead of shop owners.
- A confirmation pop-up added to every order "to be safe".
- Sketches full of placeholder text such as "Item 1, $0.00".
Practice
Count the fewest taps
Write fewestTaps(flow, start, goal). flow maps each screen name to a list of actions, and each action is { tap, to }: tapping it takes you to screen to, as in step 5 of Build it. Return the fewest taps that get from start to goal, 0 if they are the same screen, and -1 if the goal cannot be reached. Flows have Back buttons, so they contain loops. Hint: look at all screens one tap away, then all screens two taps away, and so on, and remember the screens you have seen.
function fewestTaps(flow, start, goal) {
// flow: { screenName: [{ tap: 'Label', to: 'otherScreen' }, ...] }
// return the fewest taps from start to goal, 0 if start === goal, -1 if unreachable
}
const reorder = {
home: [{ tap: 'Open order', to: 'detail' }, { tap: 'Weekly summary', to: 'summary' }],
detail: [{ tap: 'Repeat and review', to: 'review' }, { tap: 'Back', to: 'home' }],
review: [{ tap: 'Place order', to: 'status' }, { tap: 'Back', to: 'detail' }],
status: [{ tap: 'Home', to: 'home' }],
summary: [{ tap: 'Home', to: 'home' }],
};
test('the reorder happy path takes 3 taps', () => {
expect(fewestTaps(reorder, 'home', 'status')).toBe(3);
});
test('start and goal are the same screen: 0 taps', () => {
expect(fewestTaps(reorder, 'review', 'review')).toBe(0);
});
test('finds the shorter path, not the first one listed', () => {
const draft = {
home: [{ tap: 'Menu', to: 'menu' }, { tap: 'Repeat last', to: 'review' }],
menu: [{ tap: 'Orders', to: 'orders' }],
orders: [{ tap: 'Open', to: 'detail' }],
detail: [{ tap: 'Repeat', to: 'review' }],
review: [{ tap: 'Place order', to: 'status' }],
};
expect(fewestTaps(draft, 'home', 'status')).toBe(2);
});
test('a screen nothing leads to returns -1, even with Back loops', () => {
expect(fewestTaps(reorder, 'home', 'settings')).toBe(-1);
});
test('the summary is 2 taps from an order detail', () => {
expect(fewestTaps(reorder, 'detail', 'summary')).toBe(2);
});
Show a solution
function fewestTaps(flow, start, goal) {
if (start === goal) return 0;
const seen = new Set([start]);
let frontier = [start]; // screens reachable in `taps` taps
let taps = 0;
while (frontier.length > 0) {
taps += 1;
const next = [];
for (const screen of frontier) {
for (const action of flow[screen] || []) {
if (action.to === goal) return taps;
if (!seen.has(action.to)) { // skip loops such as Back
seen.add(action.to);
next.push(action.to);
}
}
}
frontier = next;
}
return -1; // ran out of new screens
}This is a breadth-first search: it tries every one-tap screen before any two-tap screen, so the first time it reaches the goal is the fewest taps. The seen set stops Back buttons from making it go round in circles.
Rank the findings and change the flow
You tested the reorder flow with four partner shop owners. Your notes: (a) 3 of 4 did not notice that two items were out of stock and expected them in the delivery; (b) 1 of 4 said the green of the Place order button was too bright; (c) 2 of 4 scrolled the Review screen for a while looking for the total, which was at the top; (d) 1 of 4 asked to add a new product he had never ordered, which the spec lists as a non-goal. Label each finding blocker, slowdown, cosmetic or out of scope, name the two changes you make before round two, and say what you will watch for in round two.
- (a) is a blocker, because owners finish the task with a wrong picture of what will arrive
- (c) is a slowdown, fixed by putting the total where the decision is, such as on the Place order button
- (b) is cosmetic and (d) is out of scope for this version, written down to count how often it comes up
- The two changes are concrete changes to the sketches, not "make it clearer"
- Round two uses new owners and checks that the out-of-stock items are noticed
Show a model answer
(a) Blocker: three owners would expect goods that will not come, which is the same kind of surprise as a corrected phone order. (c) Slowdown. (b) Cosmetic, leave it for now. (d) Out of scope: new-item search is a non-goal, so note it and count how many owners ask. Change 1: out-of-stock items move to the top of the Review screen under a heading "Not available this week (2)", shown at 0 with the reason. Change 2: the total goes on the button, "Place order, $388.20", and updates as quantities change. In round two, with three or four new owners, I watch whether each owner mentions the missing items before tapping Place order, and how long they spend on the Review screen.
Check yourself
Imran wants to add a "Rate your last delivery" screen between Review and Status in the reorder flow, because the team needs delivery feedback. What is the best response?
Any screen inside the path adds a tap to every order and breaks the three-tap promise. Moving it earlier changes nothing about that. The feedback is still useful, so put it where it costs the core task nothing. Sales reps are not the users of this flow.
In a test round, 4 of 5 owners finished the reorder task, but 3 of them needed help on the Review screen. What do you do next?
Finishing with help is not a clean success: in the real app nobody is there to help. Three of five is a clear signal, so more people would only measure the problem while it stays unfixed. A help video covers a design problem instead of fixing it.
Which choice in a paper sketch is most likely to hide a real problem?
Real names show you long labels that wrap on a small phone, and real prices show you whether the total is readable. Grey boxes are the point of low fidelity, phone size keeps the sketch honest, and state cards let you test the unhappy paths.
Flashcards
Key takeaways
- Write the flow before the screens: the goal in the user's words, the happy path with its tap count, then the branches for each step.
- Give each screen one job and one main action, put the decision's information next to the decision, and sketch every state with real data.
- After Place order, design for three outcomes: placed, failed and not sure. Lock the button and reuse one order key on every retry.
- Test paper sketches with three to five real owners in their shops, rank what you see by severity, fix the blockers and test again.
- The screen inventory's data column is the starting point for the data model and API.
Sources
- Paper Prototyping: Getting User Data Before You Code, Jakob Nielsen, Nielsen Norman Group, 2003
- Why You Only Need to Test with 5 Users, Jakob Nielsen, Nielsen Norman Group, 2000
- Thinking Aloud: The #1 Usability Tool, Jakob Nielsen, Nielsen Norman Group, 2012
- Touch target size, Android Accessibility Help, Google
- Accessibility, Apple Human Interface Guidelines