← Back to projects
Apps · In progress

CigarBuddy

A national directory of cigar shops that would rather show you nothing than send you to a shop that has closed — plus a humidor and tasting log for what you actually smoke.

The map: clusters counted in the database, so the number on a bubble is the number of shops inside it.
The map: clusters counted in the database, so the number on a bubble is the number of shops inside it.1 / 2

CigarBuddy started as a map of cigar lounges and turned into an exercise in not lying to people. I ingested nearly 43,000 listings from open map data. Fewer than 500 of them are visible in the app. The rest are not deleted — they simply have not earned a pin yet.

That gap is the whole project. A shop only gets placed on the map when two independent geocoders agree on its address down to the house number and within a couple of hundred metres. Opening hours are only shown as fact when they came from the shop's own website; anything taken from map data is labelled as unconfirmed, because being told a lounge is open and finding it dark is worse than being told nothing. A nightly job re-checks whether each listing still looks like a going concern, and one of its checks exists only because a shop that had been shut for six months sailed through the other four.

Two things I tried did not work, and the repo says so. Guessing whether a shop is still trading from how stale its website looks fails badly — a closed shop on a hosted site scores as healthier than shops that are open every day. And a fixed cut-off for how old evidence can be decides the borderline cases essentially at random: one shop missed by a single day.

The map itself is hand-built rather than a plugin, because the counts have to be true. Clustering is computed on the server in the database using the same formula the browser uses, so a bubble saying 84 shops opens onto 84 shops — the earlier version fetched a slice of the data and clustered that, so the number on the bubble was fiction. Search fixed a similar honesty problem: it used to take a batch of rows and only then apply your distance filter, which quietly dropped nearby shops in dense cities. Moving the filter into the query took recall from 89.96% to 100% across 1,984 test cases, and a job replays the real code against a brute-force answer to keep it there.

The consumer half is simpler and more fun: a humidor with what you own and what you have smoked, an aging goal per cigar, and a tasting log with about thirty fields for the people who want them.

What it does

  • Nearly 43,000 listings ingested; under 500 shown, because the rest have not been verified
  • A shop is only placed when two independent geocoders agree to the house number
  • Hours shown as fact only when they came from the shop's own site — otherwise labelled unconfirmed
  • A nightly check on whether each shop still looks like a going concern
  • Map clusters counted in the database, so the number on a bubble is the real number
  • Distance filtering done in the query — recall went from 89.96% to 100% across 1,984 cases
  • Time zones resolved from real boundaries, so "open now" is right across the country
  • Accent- and symbol-insensitive search, checked against itself inside the database
  • Location only requested when you ask for it, never on load
  • A humidor of what you own and what you have smoked, with an aging goal per cigar
  • A tasting log with around thirty fields, for the people who want them
  • Installable as an app, with store-owner accounts for claiming a listing

Built with: Next.js, React, PostgreSQL, Leaflet, Railway