Noble
Booking and SMS reminders for solo barbershops at $15 a month — against ~$2,600 lost to no-shows.
For a founder — cost, outcome, time
- Price
- $15/mo
- Payback
- ~170×
- Setup
- 5 min
- To live
- 10 days
A solo barber loses 20–30% of bookings to no-shows. The tools that fix it cost more than the revenue they save.
$15 a month against ~$2,600 a month in no-shows — about 170× payback.
- Next.js 16
- React 19
- TypeScript
- Supabase
- Stripe
- Twilio
- Tailwind v4
In short
Multi-tenant booking SaaS for solo barbershops — full build, solo, 234 commits since April. Stripe subscriptions, Twilio A2P 10DLC SMS (passing US carrier compliance), Supabase row-level security for tenant isolation, and an autonomous weekly marketing pipeline running on GitHub Actions.
74 of those commits landed in the first ten days — empty repo to live multi-tenant bookings. 152 by April's end, hardened against the 16 named QA bugs that production surfaced. All 16 were fixed in commit 6b29244 and locked in a 170-line docs/STABLE_LOGIC.md that forbids future refactor of the surviving edge cases.
Steady-state now runs 6–11 commits/month. The interesting work migrated from "build the app" to "build the machine that grows the app" — Claude authors marketing content, not code.
Noble is a multi-tenant booking SaaS for barbershops and salons: $15/month, 5-minute setup, SMS reminders that actually work. Built with Claude over 234 commits, hardened by 16 named bugs, and running autonomous marketing since June.
Seventy-four commits in ten days got Noble to live bookings. Sixteen named bugs got it to production. One locked file keeps it there.
Context
Noble is the biggest thing I've built with Claude — 234 commits since April, 74 of them in the first ten days and 152 by April's end. It's a real product: Stripe subscriptions running, Twilio SMS with A2P 10DLC compliance passing US carrier checks, Supabase row-level security enforcing tenant isolation at the database layer, and an autonomous weekly marketing pipeline pushing content to Instagram every Monday.
Problem
A solo barber makes $40–60 per cut and does 6–8 cuts a day. They lose 20–30% of that to no-shows — bookings taken by phone, forgotten by the client, and never reminded. Fixing that is not a hard engineering problem. It's a nobody-can-afford-to-build-it problem, because the standard tools cost more than the incremental revenue they save.
Three real options exist for a solo shop. Noble sits below all three — same core features, a tenth of the price ladder, nothing a solo owner doesn't need.
| Criterion | Booksy $49/mo Overkill | Fresha / Square $99+/mo For chains | WhatsApp + paper Free Breaks at 30 clients | Noble $15/mo Built for solo |
|---|---|---|---|---|
| Online booking page | ||||
| SMS reminders | ||||
| Staff schedule | ||||
| Multi-location / franchise | ||||
| Loyalty + marketing dashboard | ||||
| Price fits solo-shop ROI | ||||
| Scales past 30 clients |
Division of Labor
| Layer | Who |
|---|---|
| Architecture (dual Supabase client, server-first booking page, RLS boundaries) | Me → Claude implements |
| Booking flow logic (slot calculation, cache invalidation, race handling) | Claude, gated by 16 named QA bugs |
API routes (/api/book, /api/reschedule, cron reminders, Stripe webhooks) | Claude |
| SMS/email templates + A2P 10DLC compliance | Me spec → Claude implementation → real Twilio testing |
| Pricing model (drop from 3 tiers to 1 tier) | Me |
| Design system (cream + gold, Apple-style premium) | Me spec → Claude in Tailwind v4 tokens |
| Marketing agents (Strategist → Creator → Designer → Publisher) | Both — my prompts + brand voice; Claude's pipeline |
| Weekly content approval | Me — 1 hour on Mondays |
| Deployment | Vercel autopilot (git push to main) |
Prompt Architecture — Lock what works
The single pattern that made this scale past week two: once code is verified, it gets locked and named.
The booking-page slot calculation lives in src/lib/slots.ts and SalonClient.tsx. It went through 16 rounds of QA in April. Each bug got a name — BUG-01 through BUG-15 — and a commit hash. All 16 are documented in docs/STABLE_LOGIC.md, which opens with the rule that anchors every Claude session on this repo:
docs/STABLE_LOGIC.md — the anti-drift directiveprompt
Do not refactor the functions listed below. Do not "improve" them. Do not restructure them. They handle a specific set of edge cases. If they need to change, add a test first, then change with the test as spec.
This is the anti-drift mechanism. Claude, given a codebase without this rule, will helpfully "clean up" a function that survives eight edge cases by removing the special-case handling for two of them. STABLE_LOGIC.md is the sign that says this looks messy for a reason; leave it alone.
The rest of session context is split across single-purpose files: CLAUDE.md (project brief), CONTEXT_MAP.md (session restore), TECH_STACK.md (versions), DESIGN_SYSTEM.md (UI rules), STRUCTURE.md (file map). Loading one loads the right slice. No Claude session reads the whole tree.
Iteration Moment #1 — the April slot-cache freeze
Ten days. 74 commits. A booking page that shipped, worked in dev, worked in staging, and started producing double-bookings within an hour of the first real user. By month's end that number had climbed to 152 — the extra 78 commits were the triage of what shipping fast actually produced.
Every failure mode showed up: race conditions on insert (two people booking the same slot because check-then-insert wasn't atomic). Cache stale between browser refresh and server state. Break windows validated wrong. Timezone drift between the browser calendar and the block-timestamps table. Phone regex too permissive. Email accepting garbage.
"fix: all 16 QA bugs — slot cache, overlap, reschedule, break validation, phone/email validation, timezone UTC". 200+ line diff. Each fix numbered. Each numbered fix landed in STABLE_LOGIC.md. No refactor allowed after.
Lesson: AI-collaboration velocity is real, and produces exactly the number of bugs you don't test for. 74 commits in 10 days shipped 16 broken edge cases that only showed up under real traffic. QA discipline is the ceiling, not code output.
Iteration Moment #2 — Vercel's cache is not your friend
SalonClient.tsx fetched bookings client-side on mount. Vercel's Next.js Data Cache — a layer I didn't know was on by default — held Supabase responses for 1–5 minutes. Slots appeared available that had been booked minutes ago. Race conditions returned at scale.
"seed bookedSlotsMap from server on page load, eliminating all client cache issues". Plus three sibling commits the same day: bypass Next.js Data Cache on the admin client, force-dynamic on the availability route, sync both bookedSlotsMap and localBookedSlots after every booking. Server fetches 16 days of bookings via supabaseAdmin with cache: 'no-store'. Passes as initialBookings to the client. Client seeds state immediately. Background refresh continues for freshness. Zero cache layers, top to bottom.
Booking request · steady state
- Actor · person / role
- Event / message · data / signal
- Server work · process step
- Decision · decision / gate
- Flow
- Key path / outcome
- Async / optional
Read as steps (10)
- 01Customer → Picks slot — clicks time
- 02Picks slot → /api/book — POST
- 03/api/book → Signed in?
- 04Signed in? → Slot free? — yes
- 05Signed in? → Sign-in modal — no
- 06Slot free? → INSERT booking (✓ approved) — free
- 07Slot free? → 409 Conflict (✕ denied) — taken
- 08INSERT booking (✓ approved) → Twilio SMS
- 09INSERT booking (✓ approved) → SSE broadcast
- 10409 Conflict (✕ denied) → Picks slot — retry (async / optional)
Lesson: Managed platforms cache things you didn't ask them to cache. The right response is not to work around the cache — it's to eliminate the cache layer entirely at the point where correctness matters.
Trade-Offs
Supabase over Firebase
Postgres + row-level security pushes tenant isolation into the database itself — if the app layer has a bug, the DB still refuses cross-tenant queries. Firestore couldn't guarantee that the same way. Cost: no built-in realtime, solved with server-seed + polling.
Tailwind v4, no config file
Design tokens live in one CSS @theme block in globals.css. New contributors don't decode a config JS to understand color usage. Cost: complex theme variations (dark mode, seasonal skins) require CSS variable choreography, not JIT compilation.
Server-render fetch, client-render UI
Booking page splits into page.tsx (server, admin queries) and SalonClient.tsx (~650 lines, all interactivity). Security: sensitive queries can't leak to the browser. Cost: two files, prop-drilling coordination.
One $15 tier, not three
The code still carries scaffolding for Pro ($39) and Business ($79) — hidden until a customer explicitly asks for tier-2 features. Simplicity at signup beats incremental revenue per tenant. Cost: hidden upside.
Autonomous weekly content, not manual monthly posts
marketing_system/ is a full agent pipeline — Strategist plans the week, Creator drafts captions, Designer renders reels + carousels (Playwright + FFmpeg), Publisher pushes to Instagram. GitHub Actions runs it on free tier. Cost: the creative ceiling is bounded by the prompts I've written, not Claude's raw ability.
Results
- 7 reels + 7 feed posts per week, autonomously generated by the marketing system
- Stack integrations shipped: Stripe subscriptions, Twilio A2P 10DLC SMS, Resend email, Sentry error tracking
Honestly on customers: no public customer numbers yet. Testimonials on the landing page are aspirational. The product is production-ready, the marketing pipeline is running, the funnel from Instagram to sign-up is measurable — but "how many salons pay us $15/month" is not a number I'm ready to put on a page. That's the current work.
Reflection
AI-collaboration velocity is real, and produces exactly the number of bugs you don't test for. QA discipline is the ceiling, not code output.
-
The April sprint proved two things at once. AI-collab velocity is real — 74 commits in 10 days to live multi-tenant bookings is not a lie. And that velocity produces exactly the number of bugs your QA didn't cover: 16 of them surfaced in production between day 10 and April's end, pushing the month total to 152. The fix isn't slower code; it's a
STABLE_LOGIC.mdthat names the bugs after you find them, so the next Claude session doesn't unwind the fixes. -
Steady-state isn't done. Noble hit feature-complete around June. Commit velocity dropped roughly 20× in daily terms — ~7 commits/day during the April sprint to ~0.3 commits/day in steady state (6–11/month). But the project isn't paused; it moved layers. Claude now writes marketing content, not code. The interesting work migrated from "build the app" to "build the machine that grows the app." That reallocation is what AI-first solo work makes possible — a traditional team wouldn't reslot this cleanly.
-
The most valuable file in this repo is
docs/STABLE_LOGIC.md, not the code. It's 170 lines. It documents the exact functions that survived edge cases and forbids future refactors. Anything I ship after this will have one — before the first bug fix, not after the tenth.
Receipts
How it was made
Token scorecard
- Complexity
- L
- Duration
- 61 days active
- Tier
- 0 · naked
Model mix
- Opus
- 2%
- Sonnet
- 98%
Artifacts
- timelineBuild window
2026-04-08 → ongoing · 5 months 25 days as of 2026-10-03
- timelineApril sprint
74 commits in 10 days to live bookings · 152 by month's end
- costPricing
$15/mo flat · 14-day trial · no credit card
- costCodebase size
~10,000 LOC · 73 TypeScript files
- commit6b29244
fix all 16 QA bugs — slot cache, overlap, reschedule, break validation, phone/email, timezone UTC
- commitd6e1b5a
seed bookedSlotsMap from server on page load, eliminating all client cache issues
The same anti-drift pattern as practiced in this portfolio — rules that survive future refactors, promoted from a journal only after proving themselves
Have a Noble-sized problem?
Send one paragraph about it. I reply within two business days with a scope and a fixed price.