The old flow had six screens between wanting a ticket and having one. None of them were badly built. Every one of them existed because somewhere, at some point, a question had no default.
- taps to buy
- 3
- median confirm
- 820ms
- completion
- +18%
was six screens, eleven taps
p95 at 1.4s
same traffic, four weeks after
Every screen was a question with a default#
We listed the six screens and next to each one wrote the answer that 90% of buyers gave. Five of the six had one. A screen that asks a question with a 90% answer is not a choice, it is a confirmation — and confirmations belong on the receipt, not in the flow.
| Screen | Question | What 90% answered |
|---|---|---|
| Seat class | Which tier? | The one they tapped from |
| Quantity | How many? | The number in the previous step |
| Delivery | Post or wallet? | Wallet |
| Fees | Accept the fee? | Yes — it was never optional |
| Account | Sign in or guest? | Guest, then an account after |
What had to move to the server#
Removing a screen usually means the server now has to be right about something. Three cases carried most of it:
- 01The hold happens on tap
The old flow held a seat when you reached the payment screen. Now it holds on the first tap, for ninety seconds, and releases on abandon. Buyers stopped losing seats between screens, which was the single biggest complaint.
- 02Fees are resolved once
Fee logic used to run in two places and occasionally disagree by a rappen. It now resolves server-side with the hold and travels with it — the client never computes money.
- 03The account is created after
We create the account from the payment record once the purchase clears, and email a link to set a password. Nobody has ever been happy to make an account before being allowed to buy something.
Sub-second, and how it is measured#
“Sub-second confirm” means from the tap on Pay to the ticket being on screen and valid at the door — not to a spinner, not to a redirect. The number that mattered was the p95, because the median was already fine and the tail is what people remember.
-- the hold is a row with a deadline; expiry is a query, not a job
select seat_id
from seat_hold
where event_id = $1
and expires_at > now()
for update skip locked;skip locked is doing the quiet work there: two buyers reaching for the same seat at the same moment no longer queue behind each other, the second one just gets the next free seat in the same round trip.
What we would do differently#
Would you cut the seat-class screen again?
Yes, but later. It was the one removal that needed a fallback: for multi-tier venues we now show tiers inline on the map instead of as a step, which took another two weeks we had not planned for.
Did guest checkout hurt repeat purchases?
No — repeat rate went up. People who buy twice make an account voluntarily, and the ones who never come back were never going to maintain a password either.
Ninety seconds for a hold — why not five minutes?
Five minutes is generous to the buyer and brutal to the venue on a hot event. Ninety seconds covers a card payment with a 3-D Secure detour, and we extend it once automatically if the payment sheet is open.