The happy path is 10% of the work
Designing a two-sided marketplace booking flow taught me that the interesting design decisions all live in the states nobody demos.

Every marketplace demo looks the same. Customer opens the app, picks a service, sees a provider, taps Book, provider accepts, everyone is happy, cut to logo.
I designed and shipped that flow for Blik, a home-services marketplace in Toronto. The happy path took about a week.
The other three months were spent on everything that happens when the demo doesn't go like that.
What a booking actually is
The mistake I made early was thinking of a booking as a screen. Tap here, confirm there, done. That framing survives right up until the moment you ask what happens if the provider doesn't respond.
A booking isn't a screen. It's a state machine:
requested → matched → hired → dispatched → accepted → completed
↓
declined / timed out
↓
reassigned
Once you draw it that way, the design questions change completely. They stop being "where does this button go" and start being "what does the customer see during dispatched, and for how long, and what happens to their money while they wait?"
That last part is the one that turns a design problem into a product problem.
The states nobody designs
Here's the list I ended up with. None of these appear in a pitch deck.
A provider was dispatched and hasn't responded. The customer is now sitting in a state they didn't choose, waiting on someone they've never met. How long do you let that run? Show a countdown and you create anxiety. Show nothing and you create abandonment. We landed on showing progress without showing a deadline — the customer sees that things are moving, not how long until failure.
A provider declined. Do you tell the customer? If you say "your provider declined," you've made a stranger's scheduling conflict feel like a personal rejection. If you say nothing and silently reassign, you're withholding something real. We reassign silently but surface it honestly if the customer asks — the status detail says the request moved, not that someone said no.
The provider accepted, then went quiet. This is the worst one, because now there's a commitment on both sides and the product has to decide whose problem it is.
Nobody is available at all. Every marketplace has this state and almost none design it. An empty state that says "no providers found" is a dead end. The version that works tells the customer what to change — widen the window, widen the radius, try tomorrow — because the only useful empty state is one that hands you back some agency.
I spent more design time on the reassignment path than on the entire browse-and-select experience. Reassignment is invisible when it works, and it's the whole product when it doesn't.
Money makes it real
The part that changed how I think about product design was payments.
Blik uses Stripe Connect with manual capture — the customer's card is authorized when they book, but the money isn't actually taken until the job is done. Two-sided marketplaces do this because charging up front for work that hasn't happened is a great way to generate refunds and chargebacks.
Manual capture is the right call. It also means the design has to answer a question most apps never face: what is the customer's mental model of money that has been authorized but not taken?
Because there is a real hold on their card. It shows up in their banking app. It reduces their available balance. And it is not a charge.
Nobody has an intuition for this. "Pending" is a word banks use, not a word people understand. If your confirmation screen says "You've been charged $180," you're lying. If it says "A hold has been placed," you sound like a rental car company.
The copy I ended up with does three things in order: names the amount, says it hasn't been taken yet, and says exactly what event causes it to be taken. Not because it's elegant — because anything vaguer is an invitation to a support ticket.
This is the part I'd tell any designer moving into fintech or marketplaces: you cannot design a payment flow you don't understand mechanically. The difference between authorize and capture isn't an implementation detail the engineers handle. It determines your copy, your states, your error handling, and how much your customers trust you. I only got this right because I was also writing the code that called Stripe.
The states that cost money when you get them wrong
A few more, briefly, because they're the ones that keep you up:
Double charges. If a capture request fails at the network layer but succeeded on Stripe's side, a naive retry charges twice. The design consequence is that you need idempotency and a way to show the customer a truthful state while you reconcile — which means designing a state for "we're not sure yet," which nobody wants to design.
Cancellation timing. Cancel before dispatch and it's free. Cancel after a provider has driven across the city and it isn't. Where the line sits is a business decision. Making the line legible before the customer commits is a design decision, and if you skip it you get disputes.
Refunds against a partially-captured payment. Genuinely hard. I won't pretend I solved this elegantly.
What this changed about how I work
Three things stuck.
I now map states before I map screens. Not user flows — those are a happy-path artifact. States, with the transitions between them, including the ones that go backwards. The screens fall out of that almost mechanically afterward.
I read the API docs of anything that touches money. The Stripe docs are a design document. So are the Firestore security rules, honestly. If a system can put your product into a state, its constraints are your constraints, and you can't design around a mechanism you haven't read.
I stopped treating edge cases as a phase. "We'll handle edge cases later" means "we'll ship the demo and let support absorb the difference." The states above aren't edges. In a marketplace, provider unavailability isn't an exception — it's Tuesday.
The uncomfortable version
The honest summary is that the beautiful part of this project — the map, the discovery, the provider cards — is the part I'd struggle to defend as senior work. It's competent. It looks good. Any strong mid-level designer produces it.
The part that was actually hard was deciding what a customer sees at 7:14pm when the provider they were matched with 20 minutes ago hasn't opened the app, their card has a hold on it, and the job is tomorrow morning.
There's no Dribbble shot of that. It's most of the job.
Blik is live at theblik.ca. More on the design system and research behind it in the case study.