Model the job, not the match
The central entity in a marketplace is not the user and not the match. It is the job, and its state machine is the product.
Getting this explicit early prevents most later confusion. A minimal but honest version:
- Draft — customer is composing, nothing is visible to supply.
- Open — visible to eligible technicians, no commitment yet.
- Assigned — a technician has accepted; other technicians should no longer see it as available.
- In progress — work has started, confirmed by someone.
- Completed — the technician says it is done.
- Confirmed — the customer agrees it is done. This is not the same state, and merging them is a mistake.
- Cancelled / Expired / Disputed — the states everyone forgets and that generate all the support load.
Every transition needs an owner (who can trigger it), a notification (who finds out), and a timeout (what happens if nobody acts). Writing that table out is a day of work and removes months of ambiguity.
Both sides need a truthful current state
The most common marketplace failure is the two sides holding different beliefs about what is happening. The customer thinks a technician is on the way; the technician has not opened the app since accepting.
Two design responses:
Never show an inferred state as a fact
"Your technician is on the way" is a claim you usually cannot support. "Ahmed accepted your job at 14:20" is something you know. Report events, not assumptions — users tolerate uncertainty far better than they tolerate being told something untrue.
Make silence visible
If nothing has happened for a while, say so and offer an action. An empty screen where the user expects progress generates support contacts and cancellations. A line saying "no update since 14:20 — message the technician or cancel without charge" converts a frustrating wait into a controllable one.
Mobile constraints are architectural
The supply side works on a phone, outdoors, often on poor connectivity, frequently with the app in the background. This is not a UI concern — it changes the backend contract.
- Every state-changing action must be idempotent. A technician on a bad connection will tap "accept" three times. All three requests may arrive. Client-generated request IDs, not server-side deduplication guesswork.
- Assume the client is stale. The job list a technician sees may be minutes old. The server must reject an acceptance for an already-assigned job cleanly, and the app must handle that as a normal case with a clear message — not an error dialog.
- Push notifications are not a delivery guarantee. They are a hint. The app must reconcile state on foreground, and anything time-critical needs a fallback channel.
- Queue actions taken offline and replay them with their original timestamps. A completion recorded in a basement should not be timestamped when the signal returns.
These are unglamorous and they are the difference between a marketplace that works in the field and one that works in the office.
Matching starts simple on purpose
Sophisticated matching requires liquidity you do not have at launch. With twenty technicians, ranking by a weighted score is theatre — the honest answer is usually "whoever is available and nearby".
We start with transparent, explainable rules: eligibility filters (skill, service area, availability), then a simple ordering (proximity, then rating, then time since last job for fairness). Both sides can understand why they saw what they saw, which matters enormously for trust while you are recruiting supply.
Sophistication becomes worthwhile when you have enough density that choices are actually being made. The important architectural decision is to keep matching behind a clean interface so it can be replaced without touching the job lifecycle around it.
The eligibility question people miss
Broadcast to everyone eligible, or offer to one technician at a time? Broadcast fills jobs faster and creates a race that frustrates supply. Sequential offers respect technicians' time and fill more slowly. Most marketplaces need a hybrid — a short exclusive window for the best match, then broadcast. Decide it deliberately; it strongly shapes supply retention.
Trust is infrastructure
Marketplaces run on the belief that the other party is real and accountable. That belief needs support in the system:
- Verified identity on the supply side, at minimum. Customers are letting these people into their homes.
- An immutable event log per job. When there is a dispute, "who did what when" must be answerable without interpretation.
- Contact without exposing personal details. Proxied messaging or numbers, so neither side leaves with the other's contact information — which is also what keeps transactions on the platform.
- Ratings that mean something. Ratings collected immediately after completion, with a low-friction path to describe a problem, or you will only ever see fives and silence.
What we would sequence the same way again
The build order held up well, and we would repeat it:
- Specification and the job state machine. Everything else is downstream of this.
- Backend and authentication, including the role model — one human may be both customer and technician.
- The supply-side mobile app. Supply is harder to acquire and quicker to churn, so their experience deserves to be built and tested first.
- Matching, once the lifecycle it plugs into is proven.
The instinct is to build the customer app first because it is the one you demo. Resist it. A marketplace with unhappy supply has nothing to demo.