Case study
Dramour.
Find your love of whisky.
- RoleDesign, engineering, AI — solo
- Scale29 screens · ~61k lines of TypeScript
- BackendSupabase — 103 migrations · 11 edge functions
- Cadence60 production releases in the first 5 weeks
What it is
Dramour is a whisky collection, discovery and tasting app. Capture bottles, build a cloud-synced collection that follows you across devices, and talk to Dramcierge — an AI concierge that actually knows your shelf and your palate. I built and run the whole thing solo: brand, product design, mobile app, database, AI pipeline, and the marketing site.
What it does
Point your camera at a bottle and vision AI reads the label into structured data — name, distillery, region, ABV, age, cask, flavour profile — no barcode required.
Photograph a bar’s menu and Dramour extracts up to forty drams and ranks them against your taste profile. After the single vision call, the ranking is pure deterministic maths — zero further AI spend per menu.
Chat with Dramcierge, a concierge with real agency. It doesn’t just talk: through tool use it can look up bottles, pull community reviews, search the web, explain a term, and add to or update your collection mid-conversation. Every reply ends with three tappable follow-up suggestions, and inline bottle mentions render as product cards. A voice mode listens hands-free and talks back — speech-to-text, a fast model tuned to answer in two spoken sentences, and streamed text-to-speech so audio starts almost immediately.
Your collection, your taste. An eight-axis flavour radar built from what you rate, a wishlist, community reviews, achievements and a rank ladder, share cards rendered on-device, and an offline queue so the app works on a plane or in a thick-walled dram room.
How it’s built
- App: React Native + Expo (New Architecture), TypeScript strict, Reanimated and Skia for the fancy bits.
- Backend: Supabase — Postgres with row-level security everywhere (59 policies), 103 migrations, 11 edge functions, and completely separate dev and production projects so a staging experiment can never touch real users’ data.
- AI: Anthropic Claude — a vision-grade model for label and menu reading, a fast model for chat and voice at roughly a twentieth of the cost. Every AI call routes through a server-side proxy with a model allowlist and per-user rate limits; no API key ever ships inside the app.
- Recommendations are deliberately not an LLM. Home-screen picks come from a deterministic flavour-axis engine: dot-product matching against the bottle catalogue, a “match” pick for your dominant profile plus a “stretch” pick to keep your palate honest, and hash-based jitter so cards don’t reshuffle every time you open the app. A bottle without a verified image can’t be recommended — the rule lives in the SQL query itself, so it’s structurally impossible to break.
Lessons that cost something
The $20 afternoon. A runaway image-lookup loop once burned through about $20 of API spend in a single session. The fix became a feature: a daily AI budget table with a hard circuit breaker, a client-side concurrency cap, and a session cache. Cost control is now designed in, not bolted on.
The cascade that wasn’t. One analytics table was missing an
ON DELETE CASCADE, which silently broke account deletion for real users
until a migration hunt fixed it — and a dozen follow-up migrations now make
the cascade explicit everywhere. Deleting your account is a feature that has
to work.
The push that nearly was. A routine schema push once came within a dry-run of shipping the entire dev-only tooling schema — including an anonymously-callable function — into production. Production pushes now go through a guarded script that makes that mistake impossible to repeat.
The craft
Dramour is dark-only: Playfair Display over DM Sans, gold on near-black, with the muted text colour deliberately lifted to clear WCAG AAA contrast. The writing follows one persona rule — the most helpful person at the bar who knows everything about whisky but never makes you feel stupid. British English throughout. Whisky, never whiskey.