This is the companion piece to my RunMirror launch post. I wanted to write down what actually happened during the build, not the polished version. Because most AI-building posts are theater. This one isn't.
What I built
A product that takes 14 years of Strava data and turns it into a soul-portrait of who you are as a runner. Not a dashboard — a mirror. 2,382 activities, 13,050 miles, 32 named dedications, 13 consecutive Thanksgiving mornings, 7 ultras, 11 marathons, 17°F to 104°F, 9 countries. All surfaced as a 60-second story you've never seen about yourself.
What it took
Approximately 60–70 hours of my time, spread over multiple weekends, mostly during the kids' nap times and after they went to bed. My collaborator was Claude. I did not write the code. I directed the building.
Six specific bugs we hunted together
I want to share these because the wins-only narrative of AI development is misleading. Real builders fight real bugs. Here are six from one weekend:
1. The Supabase 1,000-row silent killer
My persona showed "5 years active" when I have 14 years of data. After three wrong hypotheses, we discovered Supabase's PostgREST silently caps responses at 1,000 rows regardless of .limit(). Fix: paginate with .range() in a while loop.
// Before — silently capped at 1000 rows
const { data } = await db.from('activities')
.select('*')
.eq('athlete_strava_id', stravaId)
.limit(10000) // ← ignored by PostgREST
// After — paginated
let all = [], from = 0
while (true) {
const { data } = await db.from('activities')
.select('*')
.range(from, from + 999)
if (!data?.length) break
all = all.concat(data)
if (data.length < 1000) break
from += 1000
}
Lesson: AI doesn't know your specific infrastructure quirks. You do.
2. The CSV import cascade
Five sequential schema errors, each one revealed only after fixing the previous: workout_type missing → strava_id null → calories missing → manual missing → finally working. We could have done a full schema audit upfront. We didn't. We caught each one as it crashed.
Lesson: AI debugs reactively; humans should plan defensively.
3. The React prop chain phantom
The Thousands chart wouldn't render despite the API returning correct data. Six iterations. Eventually: the MilestoneWall component was being called without its persona prop, so persona?.layers?.thousands was always undefined.
Lesson: AI and human both pattern-matched to "caching issue" when it was a prop issue. We were both wrong, in the same way, three times in a row. Confidence cuts both ways.
4. The file-that-didn't-land
Five separate times, Claude generated a fix and I copied it locally, and the bug persisted. Each time, the file on my machine wasn't actually the file Claude had written. The patch logic ran successfully on Claude's end but the replacement didn't match my actual file content exactly.
Lesson: Verification is non-negotiable. "I changed it" is not the same as "it is changed."
5. The undefined color constant
Claude added a new chart component that used G2 = '#0F6E56' (dark green). The constant was defined in the design system but not in the component file. Hot reload threw G2 is not defined. A one-line fix, but it took two rounds because the first patch went to the wrong section.
Lesson: AI doesn't have a global view of your codebase the way a human engineer holds one in their head. Imports and constants slip through.
6. The infinite recompute loop
At one point, every persona recompute triggered a geocode loop that triggered a recompute that triggered a geocode loop. I caught it because Supabase emailed me a quota warning, not because the code surfaced the issue.
Lesson: AI builds confident systems. Confidence systems sometimes need humans to notice the smoke before the fire.
Three specific moments Claude was extraordinary
1. The pipeline diagram
When I described v3.0 — "unified auto-sync after every upload, three tiers of enrichment, quiet progress indicator" — Claude produced the entire architecture diagram in 90 seconds: tier 1 (synchronous: webhook + CSV ingest), tier 2 (background async: routes + weather + geocoding), tier 3 (background async: PR splits). With failure handling. With cache invalidation logic. With a decision on which UI affordances to keep and which to retire.
I have run product orgs that take a week to produce that.
2. The builder's portrait
I asked Claude to evaluate me as a builder, on a 10-dimension scoring framework, as if I were a candidate for an SVP-Builder role. It scored me honestly: 9/10 on Vision and User Empathy, 6/10 on Technical Independence, 7/10 on Scope Discipline, with the verdict: "Product-first builder. Needs a technical co-founder for deep debugging. Don't build alone past v3.0."
I've never had a coach be that direct in 20 years.
3. The chart funnel
For the Milestone Wall's centerpiece visualization, I asked for "a graph of the thousands." Claude generated three concepts (cumulative miles river / days per block bars / run-count milestones). I picked one. Claude generated three sub-variations of that one (dual-axis with runs / miles-per-run vs days-per-run / stacked composition). I picked one. We built it.
From abstract concept to working chart: 35 minutes. From data fetched to the final visual showing my 14-year running arc in one image: 35 minutes.
Where this leaves the role of a product builder
"For those who really know what Product Discovery is, this is an amazing era. It has never been better." — Marty Cagan
The role isn't gone. It's distilled.
What I did this weekend was almost entirely:
- Decide what the product is for
- Decide what it must never become
- Notice when generated work was off-soul and reject it
- Pattern-match bugs that AI couldn't see
- Hold the through-line across 60 hours of building
What I didn't do:
- Write JavaScript
- Write SQL
- Write CSS
- Write React
- Write regex
Both are real work. The first kind didn't get faster. The second kind got 100x faster. The ratio of value between them has flipped, and most people in our industry haven't adjusted yet.
What's next
RunMirror v3.0 will ship the unified auto-sync pipeline this month. v4.0 will introduce a memoir tier — an AI-generated chapter for each milestone group, written in the runner's own voice. I'll deploy to Vercel this week, apply for Strava Partner status, and open up beta access.
The product cost me $20 to build. It would have cost six figures the old way. That's not the story though. The story is what I learned about my own data:
13 consecutive Thanksgiving mornings. A 42-mile prayer titled "Govinda Govinda." 32 runs dedicated to 8 named souls. 14 years of waking up at 4 AM in St. Louis to be a runner before being a father, a husband, a leader.
I would never have seen any of that without sitting down with the data. And I would never have had time to build the thing that lets me sit down with the data, without AI.
That's the actual story. Tools matter because they make the unseen seeable. AI is the most powerful tool we've ever built for that. Use it on something that matters to you.
Balaji Mudduluri leads technology at a $2B company. He's been running since 2013, building products since 2005, and waking up at 4 AM since he had kids. RunMirror is his weekend project.