The Brief
Ship a complete community medicine-sharing product from a written spec: donors list sealed medicine they will not finish, receivers search nearby, the two meet, the handover is verified, and a moderator can see everything that moved. One codebase across iPhone, Android and a real desktop web app, in English and Urdu, for a market where every SMS is billed and a large share of users are on mid-range Android hardware.
The product risk is not discovery. It is trust, and it fails in two directions: nobody gives a stranger their address, or the platform becomes a channel for expired and prescription-only drugs. Nearly every engineering decision here answers one of those two.
Privacy Enforced in the Database, Not the Interface
The shortcut is to send the listing with the donor's address attached and simply not render it until a claim is approved. That is not privacy — it is a hidden element, and anyone reading the network tab already has the address.
Instead the exact address and the handover code live in their own tables with their own row-level security policies. Public listings carry only a neighbourhood and a coordinate blurred to roughly a one-kilometre grid: enough to sort by distance, not enough to find a house. The precise address becomes readable to exactly one receiver at exactly one moment — when the donor approves that person's claim. The server decides; the client never held it.

The Handover Is the Product
Everything before the meeting is a search problem. The meeting is where a platform like this earns or loses trust, so it got the most attention.
- A claim moves through explicit states — requested, approved, pickup scheduled, handed over, completed — and each transition is a database function, not a client-side status write, so no step can be skipped by a crafted request.
- At the meeting the receiver shows a six-digit code, also rendered as a QR so nobody reads digits aloud in a stairwell, and the donor confirms it.
- Both sides rate the exchange afterwards, and those ratings feed a visible trust score — the only reputation signal in the product.
- Verified volunteers can carry a pickup between two people who cannot meet, with their own scoped access to the addresses involved.
Safety Rails That Fail Closed
A donation platform that treats every medicine the same is a liability. This one does not: a prohibited-category list blocks controlled and high-risk medicines at listing time and can be updated without an app release; a database trigger refuses to complete a claim on a prescription-only medicine without a prescription photo; expiry rules are applied at listing and remaining shelf life is shown on every card.
Photo verification uses a vision model in an edge function that reads the pack for medicine name, strength and printed expiry and compares them with what the donor typed. When the model is unavailable or unsure, the listing does not quietly pass and does not claim a check that never ran — it goes to the human review queue marked unverified. An automatic check that fails open is worse than none, because it launders a guess into a badge.
Every safety rule in this product is enforced on the server. A rule that only exists in the app is a suggestion, and it stops applying the moment someone talks to the API directly.

A Cost Decision That Changed the Architecture
The original design signed users in with an SMS code. In Pakistan each of those messages is billed, which turns sign-up — the one action you want frictionless — into a per-user cost that scales with the exact number you are trying to grow.
Sign-in became channel-aware: email leads because email codes cost nothing, and phone remains available for people who do not use email. That was not a screen change. The data-client interface, both backends, the profile schema and the account-matching rules all had to stop assuming a phone number was the identity — one account per identifier, whichever channel it arrived on.
One Codebase, Two Backends, Three Layouts
The app talks to a single typed data client with two implementations behind it. One is the real backend — Postgres, row-level security, security-definer functions, storage and auth. The other keeps the entire product working on-device with seeded data and no network, which is how the public demo runs: a stakeholder can be shown the whole product on a phone with bad wifi, with no account, no cost and no chance of a demo writing to real data. Because both satisfy the same interface, the demo cannot drift from the product.
On the front end, React Native for Web makes a phone app run in a browser but does not make it a web app — a 375-pixel column stretched across a monitor reads as a port. The desktop build swaps the tab bar for a persistent sidebar, moves lists into a real grid and caps reading-width content, from the same components. Light and dark are both first-class, and the interface mirrors correctly for Urdu.

What Was Delivered
- 24 screens covering the full journey: listing, search and map, claims, approvals, scheduled pickups, code and QR handover, ratings, chat with contact-detail stripping, reports and blocking.
- Admin surfaces: review queue with the automatic check's confidence, user management, audit log and platform analytics.
- Database migrations carrying the whole safety model — policies, triggers and transactional handover functions — so the rules travel with the schema.
- A live public demo, plus a native iOS build verified in the simulator.
Stated plainly: the public link runs in demo mode, so it is a product walkthrough rather than a live service with users, and the cloud backend — fully written and migrated — has not yet carried production traffic. That first deployment is where row-level-security edge cases usually surface, and it is scoped rather than finished.
Building a marketplace where trust is the hard part?
Two-sided products live or die on the safety model, not the feature list. Baydot designs the data boundaries first — who can see what, enforced where it cannot be bypassed — then builds the product on top of them.
Book a Free Call