DEV Community

Shawn Bure
Shawn Bure

Posted on Originally published at unscrewed.lol

Building a Marketplace Where Zero Is a Feature, Not an Embarrassment

Most marketplace dashboards are designed to make small numbers look large.

They celebrate registrations, page views, clicks, and gross inventory. Those
numbers are easy to move. They are also weak evidence that a marketplace is
doing its job.

I am building unscrewed.lol, a very early direct-barter
marketplace for goods and skills. At the time of writing, its public evidence
is intentionally unimpressive: two active members, one active remote listing,
one measured listing-share visit, and zero completed trades.

That is not the launch announcement most founders would choose. It is the
product constraint.

If a barter marketplace says it is creating community value, it should be able
to distinguish an opened link from an account, an account from a genuine
offer, an offer from a two-sided conversation, an agreement from a real
exchange, and an exchange from one both participants independently confirm.

This is how that constraint shaped the system.

The outcome ladder is part of the data model

The basic funnel is:

  1. a real person opens a measured invitation;
  2. that same person creates an account;
  3. the person posts a real offer or proposes against one;
  4. both people participate in the negotiation;
  5. both sign clear terms;
  6. the actual exchange happens; and
  7. each participant separately confirms completion.

Each transition means something different. Collapsing them into “engagement”
would make the dashboard easier to celebrate and harder to trust.

Anonymous campaign visits are stored first-party with a random visitor ID plus
source, medium, and campaign. A unique constraint prevents the same visitor
from inflating one channel repeatedly. If that visitor later creates an
account, the exact attribution tuple is carried to the user record.

The system does not call a negotiation two-sided merely because a row exists.
Both participants must actually take part. It does not call signed terms a
completed trade. A contract has separate append-only completion timestamps for
each party, and the trade is counted only when both are present.

That distinction shows up in the UI, email subjects, growth queries, and public
copy. If one layer quietly redefines success, the metric stops being useful.

Sparse marketplaces need scope-aware metrics

A remote mentoring offer belongs to a national pool. It does not become local
inventory in Chandler merely because its owner lives there.

That sounds obvious, but it is easy to get wrong when every account has a ZIP
and every listing is counted with one generic query.

Every listing therefore has an explicit exchange mode:

  • local for an in-person exchange;
  • remote for an exchange that can happen across the United States; or
  • either when both are genuinely possible.

Remote listings do not expose the owner’s ZIP, appear as map pins, trigger
local-watch email, or count toward a ZIP-specific launch. Local campaigns only
receive credit for supply inside their real geographic boundary. National
campaigns can count remote supply without pretending that a neighborhood has
liquidity.

The practical lesson is broader than barter: marketplace metrics need the same
scope as the user promise. “We have inventory” is not useful if the inventory
is unavailable to the person seeing the claim.

One real click exposed a better conversion bug

The first measured visit to the live remote listing did not create an account
or proposal.

The tempting response would have been more promotion. The better response was
to walk the exact recipient path.

A signed-out visitor could read the offer and choose “Create account to
propose,” but signup required a five-digit home ZIP. That requirement makes
sense for local discovery. It does not make sense for someone responding to a
nationwide remote exchange.

The fix was deliberately narrow:

  • the listing creates a signup route that preserves the exact proposal and marks the exchange as remote;
  • the signup screen makes ZIP optional only in that context;
  • the client sends the proposal listing ID to the API; and
  • the API independently verifies that the listing exists, is active, is not deleted, and is actually remote before it accepts a ZIP-less account.

A forged query parameter is not authority. Ordinary signup and local or
“either” proposals still require a valid ZIP.

This was a small UI change backed by a server-side invariant. It removed
irrelevant friction without weakening the geographic model.

The infrastructure is ordinary on purpose

The application is a TypeScript monorepo:

  • React, Vite, Tailwind CSS, and React Router on Cloudflare Pages;
  • Hono on Cloudflare Workers for the API;
  • D1 and Drizzle for relational state;
  • KV for sessions and rate limiting;
  • R2 for listing photos;
  • one Durable Object per negotiation for realtime fan-out;
  • Turnstile for bot protection; and
  • Cloudflare Email Service for privacy-minimal trade alerts.

D1 remains authoritative for negotiation history and read cursors. Durable
Objects improve the live experience, but reconnecting on another device cannot
erase the durable state.

Transactional email contains no offers, terms, messages, or meetup details. It
only tells a verified recipient that trade activity exists and links back to
the authenticated thread. Email is a prompt; the application remains the
system of record.

None of these choices is novel by itself. The useful part is where the
boundaries sit: realtime is not authority, a signature is not fulfillment, a
home location is not listing scope, and a marketing visit is not community
value.

Public benefit needs testable claims

The project is public-benefit-intended, but it is not a nonprofit, public
benefit corporation, cooperative, university program, or endorsed community
service.

Core barter currently has no listing or transaction fee. Browsing, posting,
proposing, negotiating, and signing basic trade terms are free. The
public-benefit page turns that intention
into claims people can inspect: what is free, what data is collected, what
outcomes exist, and what the project is not.

Publishing a zero is useful because it creates pressure in the right direction.
A new signup cannot hide the absence of supply. A new listing cannot hide the
absence of a plausible counterparty. A signed agreement cannot hide the
absence of fulfillment.

That changes what gets built next.

What I would keep for any two-sided product

If I were starting another marketplace, I would keep five rules:

  1. Define the real-world outcome before designing the dashboard.
  2. Store each meaningful transition separately.
  3. Make geography or eligibility part of attribution, not a later filter.
  4. Treat client context as a hint and verify exceptions on the server.
  5. Publish the denominator and the zeroes.

The current goal is not to make unscrewed look popular. It is to help enough
real people make useful, fair exchanges that popularity becomes evidence
rather than copy.

You can inspect the live
community evidence, review the
operating commitments, or try the one
real remote offer.

Disclosure: this article was drafted with AI assistance and fact-checked
against the production code, deployed system, and live marketplace counts. It
contains no fabricated users, listings, conversations, or trades.

Top comments (0)