skip to content
graham@mccarthy:~$

The Build Was Green and the Lick Was Unplayable

graham_m·2026-08-21·5 min read
The Build Was Green and the Lick Was Unplayable

I've been trying to get better at guitar the way most intermediate players do: a tab site in one tab, a scale diagram in another, a metronome app on my phone. What I wanted was one place that held all of it — scales, voicings, chords, the shapes and the reasons — plus enough gamification to turn twenty minutes a day into a habit.

So I built it. Fretstart is a daily practice coach for blues and rock lead: it tells you what to play today, shows it on the neck, runs a metronome and backing groove, records you, and keeps a streak. Empty folder to live domain in seven days, built the same way I built Multiplay — AI running the loop, me holding the merge button.

The interesting part isn't that week. It's a category of bug I'd never had to think about before.

The code was fine. The music was wrong.

A few days in, the build was green — and the app was teaching things that were false. A lick was written on the B string, putting it out of key, while the prose described the G string. A descending run chained pull-offs across strings, which no hand can play. The 12-bar page described a plain blues while the diagram above it rendered a quick change. All of it compiled perfectly and would have taught me something wrong for a week before I noticed.

Nothing in a JavaScript toolchain has an opinion about whether a claim about music is true. So the fix was to give it one: check:content treats the musical content as data with invariants and asserts them on every build:

phrases       284 notes, 14 legato, all in the key they declare
licks/solos   16 phrases: length, source weeks, box-1 claims
transposable  every phrase moves to another key
library       12 prose claims checked against the interval arrays
12-bar grid   I IV I I IV IV I I V IV I V
library prose 21 fret/note claims checked against the tuning
box windows   13 diagrams frame the shape their page describes

The line worth stealing for any content-heavy product is the fourth one: every factual claim in the prose is checked against the data structure the diagram is drawn from, so a page physically cannot contradict its own picture. Prose and facts live in separate modules — copy is authored, everything factual is derived, and the checker calls out drift.

Alongside it, 579 conventional tests cover the logic where a bug means someone loses a streak: day boundaries, the lapse rule, the guest-to-account handoff. House rule: every new test gets broken once to prove it can fail.

Gamification only works if the numbers are honest

The streak was the whole reason I built this, and it's also the thing most likely to make the app a liar. So: a stat from the prototype that tracked "clean tempo" progress got deleted — there was no honest way to measure it. A lapsed streak reads zero the moment it lapses, not whenever the database next gets written. Neither makes the app feel better; both make the number mean something, and a number that doesn't mean anything can't build a habit.

The bigger version: the plan originally ran sixteen weeks, and on day 113 it quietly served week 16 again, forever — the most committed user hits the wall first, which is exactly backwards. Fixing that honestly meant authoring weeks 17 through 32 across both tracks: 448 authored days.

The payoff mechanic I like most is the quietest. The curriculum asks for a recording on day 1 and the last day of every fourth week, those takes are never pruned, and the comparison view defaults to the widest span you have. Progress on an instrument is invisible day to day. Hearing day 1 played back against day 224 is the only proof that isn't a number I made up.

The difference between a planner and a coach

The feature I didn't plan is the one that justifies the app existing. The curriculum kept telling me to check my bends — "a bend is only in tune if you check it" — while leaving the checking to the ear that is itself the thing being trained.

The app already had pitch detection sitting in the tuner. Wiring it to the bend was a few hundred lines. It listens to several attempts and reports the pattern, not the last one, because one good bend proves nothing: "reliably 30 cents flat" is a fixable habit, "somewhere different every time" is a hand problem needing a different fix. That's the line between a practice planner and a practice coach — and it's the one thing a video course structurally can't do, because a video can't hear you.

What a green build still doesn't catch

The traps that survived longest were the silent kind. Postgres taught me the same lesson three times: revoke ... from public does not revoke from anon, because PUBLIC is its own grantee and Supabase grants new objects to each role by name — the grant line reads like the whole story and isn't. And the daily reminder shipped with renotify: false, so each day's notification silently replaced yesterday's in the tray: the push service accepts it, the subscription is healthy, and it never makes a sound. Neither shows up in a test run. Both show up in a query.

I built this to make myself practice, and I'm on the streak. Try it at fretstart.com — the 32-week path and the scale and chord library are readable without an account.