Picture this: I’m staring at a CSV file containing metrics from 492 episodes of my podcast, Merge Conflict. Years of data—listen counts, retention rates, performance metrics—all sitting there, waiting to tell a story. But I’m not in the mood to spend days building dashboards and wrangling data.
So I did what any modern developer would do: I opened VS Code, fired up GitHub Copilot with Claude Sonnet 4.5 as my reasoning model, and decided to vibe code my way to a beautiful analytics platform.
Spoiler: It took 5 minutes to get the first version running. Then another 10 to make it perfect.
The Opening Prompt: Dream Big, Start Simple
Here’s literally what I typed into VS Code’s agent mode chat:
“In the attached file is all of our podcast metrics for every episode. Create a beautiful website that helps visualize, search, finds topics, and more. Come up with a bunch of ideas for what a podcast creator would want and build it out. Use a vite based app that i can use and publish on github pages.”
That’s it. No technical specs. No wireframes. No “please use React 18.2 with TypeScript 5.3 and Tailwind 3.4.” Just pure vibes.
Why Claude Sonnet 4.5?
I chose Sonnet 4.5 as my reasoning model in GitHub Copilot because this wasn’t just about code completion—I needed deep architectural reasoning. Sonnet 4.5 excels at:
- Understanding complex requirements from natural language
- Making smart technology choices based on context
- Architecting entire systems, not just functions
- Anticipating edge cases (like the CSV parsing issue we’ll see later)
For a full-stack project like this, I needed a model that could think through the entire system design, not just autocomplete the next line.
Why This Works
The key to vibe coding isn’t being vague—it’s being outcome-focused. I told GitHub Copilot:
- What I have: CSV with podcast metrics
- What I want: A beautiful, searchable website
- How I want it: Vite-based, deployable to GitHub Pages
Copilot (powered by Sonnet 4.5) filled in all the technical decisions. It chose:
- React + TypeScript (type safety + modern UI)
- Tailwind CSS (rapid styling)
- Recharts (beautiful data visualization)
- Lucide React (clean icons)
All of this happened in seconds. No analysis paralysis. No bikeshedding about whether to use styled-components or emotion.
The Build: Watch the Magic Happen
Within literally 2 minutes, GitHub Copilot had scaffolded:
// Project structure appears
├── src/
│ ├── App.tsx // Main app with routing
│ ├── components/
│ │ ├── Dashboard.tsx // Overview metrics
│ │ ├── EpisodeList.tsx // Search & filter
│ │ ├── TopicAnalysis.tsx // AI-extracted topics
│ │ ├── PerformanceCharts.tsx // Analytics deep-dive
│ │ └── EpisodeDetail.tsx // Individual episode view
│ ├── utils.ts // Data parsing & helpers
│ └── types.ts // TypeScript definitions
├── package.json
├── vite.config.ts
└── tailwind.config.js
The AI didn’t just create files—it architected a solution:
Smart Design Decisions I Didn’t Have to Make
- Component Architecture: Separated concerns into Dashboard, List, Analysis, Charts, and Detail views
- State Management: Used React hooks (no Redux overhead for this use case)
- Data Flow: Single
loadEpisodes()
function, memoized calculations - Styling System: Tailwind utility classes with custom theme colors
- Type Safety: Full TypeScript coverage with proper interfaces
Here’s a snippet that shows the elegance:
export interface Episode {
slug: string;
title: string;
published: Date;
day1: number;
day7: number;
day14: number;
day30: number;
day90: number;
spotify: number;
allTime: number;
}
Clean. Simple. Exactly what I needed.
The First Bug: CSV Parsing Drama
I ran npm run dev
, opened the browser, and… the numbers looked wrong. Way wrong.
This is where vibe coding gets interesting. I could have:
- Dug into the code myself
- Googled “how to parse CSV with commas in quotes”
- Installed a CSV parsing library
Instead, I typed into Copilot Chat:
“double check the CSV parsing, doesn’t seem correct here as the numbers aren’t accurate”
The Problem: Titles with Commas
My CSV had entries like:
477,"477: From Spark, To Blazor, To Mobile, To Production in 1 Day",2025-08-25,"1,781"
The original regex parser choked on commas inside quoted fields. GitHub Copilot immediately understood the issue and rewrote the parser:
const parseCSVLine = (line: string): string[] => {
const result: string[] = [];
let current = '';
let inQuotes = false;
for (let i = 0; i < line.length; i++) {
const char = line[i];
if (char === '"') {
inQuotes = !inQuotes;
} else if (char === ',' && !inQuotes) {
result.push(current.trim());
current = '';
} else {
current += char;
}
}
result.push(current.trim());
return result;
};
This is the beauty of vibe coding: I didn’t need to know how to write a CSV parser. I just needed to recognize that the output was wrong. The AI handled the implementation.
Hot reload kicked in. Numbers fixed. Moving on.
The Deployment Challenge: GitHub Actions
Now I needed to deploy this to GitHub Pages. I had a working example in another repo, so I asked:
“can you create a github pages deployment for this. see: https://github.com/jamesmontemagno/PinkPuffQuest/blob/main/.github/workflows/deploy.yml as a reference”
GitHub Copilot fetched the workflow, understood the pattern, and created:
name: Deploy to GitHub Pages
on:
push:
branches: ['main']
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Set up Node
uses: actions/setup-node@v5
with:
node-version: lts/*
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: './dist'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
Perfect. But…
The TypeScript Tantrum
First deployment failed:
Error: src/App.tsx(4,3): error TS6133: 'LineChart' is declared but its value is never read.
Error: src/App.tsx(5,3): error TS6133: 'PieChart' is declared but its value is never read.
Classic case of over-importing. The AI had scaffolded with some components in mind, then refactored, leaving unused imports.
My prompt to Copilot:
“when the action runs i get: [pasted error]”
GitHub Copilot’s response was surgical:
// Before
import { BarChart, LineChart, PieChart, TrendingUp, TrendingDown, ... } from 'lucide-react';
// After
import { BarChart, Radio, Calendar, Search, Tag, Activity } from 'lucide-react';
It also fixed the unused variable patterns:
// Changed this anti-pattern
performanceDistribution.map((entry, index) => (
<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
))
// To this
performanceDistribution.map((_entry, index) => (
<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
))
Green build. Deployed. Live at https://jamesmontemagno.github.io/podstats/
What I Actually Built (Without Knowing I Was Building It)
Let’s talk about what ended up in this app, because it’s honestly impressive:
1. Dashboard with Smart Analytics
- Total episodes, listens, averages, growth rates
- Performance timeline with Recharts
- Top 10 episodes ranked by performance
- Best episode spotlight
2. Advanced Search & Filtering
const filteredAndSortedEpisodes = useMemo(() => {
let filtered = episodes.filter(ep => {
const matchesSearch = ep.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
ep.slug.toLowerCase().includes(searchTerm.toLowerCase());
const matchesMin = !minListens || ep.allTime >= parseInt(minListens);
const matchesMax = !maxListens || ep.allTime <= parseInt(maxListens);
return matchesSearch && matchesMin && matchesMax;
});
filtered.sort((a, b) => {
// Dynamic sorting logic
});
return filtered;
}, [episodes, searchTerm, sortField, sortOrder, minListens, maxListens]);
Memoized for performance. Reactive. Elegant.
3. Topic Analysis with AI Extraction
This was my favorite feature. The AI extracted topics from episode titles:
export const extractTopics = (episodes: Episode[]): Map<string, Episode[]> => {
const topics = new Map<string, Episode[]>();
const keywords = [
'AI', 'iOS', 'Android', 'macOS', 'Swift', 'Kotlin', 'C#', '.NET', 'MAUI',
'Blazor', 'React', 'Azure', 'GitHub', 'VS Code', 'Xcode', 'Apple', 'Microsoft',
// ... and 30+ more
];
episodes.forEach(episode => {
const title = episode.title.toLowerCase();
keywords.forEach(keyword => {
if (title.includes(keyword.toLowerCase())) {
if (!topics.has(keyword)) {
topics.set(keyword, []);
}
topics.get(keyword)!.push(episode);
}
});
});
return topics;
};
Now I can see:
- Which topics I cover most
- Which topics get the most listens
- Episode clusters around technologies
4. Retention Analysis
The app calculates retention curves automatically:
export const calculateRetention = (episode: Episode): {
day1: number;
day7: number;
day30: number
} => {
if (episode.allTime === 0) {
return { day1: 0, day7: 0, day30: 0 };
}
return {
day1: (episode.day1 / episode.allTime) * 100,
day7: (episode.day7 / episode.allTime) * 100,
day30: (episode.day30 / episode.allTime) * 100,
};
};
I can now see that on average:
- ~58% of all-time listens happen in day 1
- ~85% happen in week 1
- ~92% happen in month 1
That’s actionable insight I didn’t have before.
5. Beautiful Charts
Recharts made this trivial:
<ResponsiveContainer width="100%" height={300}>
<LineChart data={timelineData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" angle={-45} textAnchor="end" height={80} />
<YAxis />
<Tooltip formatter={(value) => formatNumber(value as number)} />
<Line type="monotone" dataKey="listens" stroke="#0ea5e9" strokeWidth={2} />
<Line type="monotone" dataKey="day7" stroke="#10b981" strokeWidth={2} />
</LineChart>
</ResponsiveContainer>
Responsive. Interactive. Professional.
The Vibe Coding Methodology
Let me break down what made this work:
1. Outcome-Driven Prompts
Don’t say: “Create a React component with props for data” Say: “Build a dashboard showing my podcast metrics”
2. Let AI Make Technical Decisions
The AI chose:
- Component architecture
- State management patterns
- Styling approach
- Chart library
- Build tooling
All good choices. All things I didn’t have to research.
3. Iterate on Bugs, Not Features
When something broke, I described the symptom:
- “numbers aren’t accurate” → Fixed CSV parser
- “build fails with errors” → Cleaned up imports
I didn’t need to know the solution. I just needed to recognize the problem.
4. Reference Your Own Work
I pointed Claude at my other GitHub repo for the deployment workflow. It understood the pattern and adapted it. This is huge for maintaining consistency.
5. Trust the Types
TypeScript caught issues before runtime:
interface Episode {
slug: string;
title: string;
published: Date; // Date type, not string
day1: number; // number type, handles parsing
// ...
}
The AI used proper types throughout. This prevented an entire class of bugs.
How GitHub Copilot Worked Its Magic
GitHub Copilot wasn’t just suggesting code—with Sonnet 4.5 as the reasoning model, it became my architect and pair programmer:
Architecture Level:
- Designed the entire component structure
- Made technology stack decisions
- Planned data flow and state management
- Anticipated edge cases and failure modes
Implementation Level:
- Auto-completed repetitive patterns
- Suggested sensible variable names
- Filled in utility functions
- Wrote test data transformations
This dual-mode operation—thinking deeply about architecture while still handling the minutiae—is what makes modern AI-assisted development so powerful.
The Results: 5 Minutes to Production
Timeline:
- Minute 1: Wrote initial prompt
- Minute 2: Claude scaffolds entire project
- Minute 3:
npm install && npm run dev
- Minute 4: See working dashboard, spot CSV bug
- Minute 5: Fix CSV parser, admire the result
Follow-up:
- Minute 10: Add GitHub Actions workflow
- Minute 12: Fix TypeScript errors
- Minute 15: Live on GitHub Pages
What I Learned About Vibe Coding
It’s Not About Laziness
I didn’t avoid learning. I avoided re-learning things I already know. I know how to:
- Parse CSV files
- Build React apps
- Configure Vite
- Write TypeScript
- Deploy to GitHub Pages
But why spend an hour doing what AI can do in seconds?
It’s About Leverage
My job was:
- Define the outcome
- Provide the data
- Recognize when something was wrong
- Ship it
The AI’s job was everything else.
It’s About Flow
No context switching to:
- Read documentation
- Search Stack Overflow
- Debug cryptic errors
- Remember Recharts API syntax
Just pure creative flow from idea to implementation.
The Code That Surprised Me
Here’s something Claude wrote that I thought was clever:
const getEpisodePerformance = (
episode: Episode,
avgAllTime: number
): 'excellent' | 'good' | 'average' | 'below-average' => {
const ratio = episode.allTime / avgAllTime;
if (ratio >= 1.5) return 'excellent';
if (ratio >= 1.1) return 'good';
if (ratio >= 0.9) return 'average';
return 'below-average';
};
It created a performance classification system I hadn’t even asked for. But it made perfect sense—now every episode gets a badge showing how it compares to the average.
GitHub Copilot anticipated a need I had but hadn’t articulated. This is where Sonnet 4.5’s deep reasoning shines—it doesn’t just respond to what you ask, it thinks about what you’ll need.
Final Thoughts: The Future is Vibes
This isn’t the future—it’s now. GitHub Copilot with advanced reasoning models like Sonnet 4.5 has fundamentally changed how we build software.
The question isn’t “Can AI write code?” (yes, obviously). The question is: “How do I collaborate with GitHub Copilot to build better software faster?”
Here’s my framework:
- You own the vision – What are we building and why?
- Copilot owns the implementation – How do we build it?
- You own the quality – Does this actually work?
- Together you own the outcome – Did we solve the problem?
Try It Yourself
Want to vibe code your own project? Here’s the recipe:
- Start with VS Code + GitHub Copilot (choose Sonnet 4.5 for complex projects requiring deep reasoning)
- Write an outcome-focused prompt: “Build me X that does Y”
- Let Copilot make technical choices: Don’t over-specify unless you have strong opinions
- Iterate on problems, not solutions: Describe what’s wrong, let Copilot fix it
- Ship fast, iterate faster: Get to production quickly, improve based on real use
📦 Project Links:
- Source Code: github.com/jamesmontemagno/podstats
- Live Demo: jamesmontemagno.github.io/podstats
- Podcast: mergeconflict.fm
Clone it. Modify it. Vibe with it.
I have read DevBlogs daily for the past few years but I have never created an account/left a comment. Signed up just now to comment that this was a really nice piece that was explained well in technical layman terms. I have a few concerns with vibe coding approaches in general, but this article showed me some of the (big) positives too. Thanks!
Thank you for the article.