Building a full-stack app with AI pair programming
Real AI pair programming is about maintaining a shared state between your brain and the LLM. If you don't feed the AI the right context, you'll spend four hours debugging a hallucinated library version that hasn't existed since 2022.
I tried building a simple Task Tracker last Tuesday. I used Cursor (which is basically VS Code on steroids) and Claude 3.5 Sonnet. The goal was to move from a blank folder to a deployed app in under an hour.
Setting up the workspace for context
The biggest mistake beginners make is starting a chat without providing a roadmap. If you just say "Build me a task app," the AI will guess your tech stack. It might give you Tailwind CSS when you wanted Bootstrap, or use an outdated Next.js API route structure.
First, create a .cursorrules file (or a project-level instruction file) in your root directory. This forces the AI to stick to your specific stack.
// .cursorrules
You are an expert Full-stack TypeScript developer.
Tech Stack:
- Frontend: Next.js 14 (App Router), Tailwind CSS, Shadcn UI
- Backend: Supabase (Auth and Database)
- State Management: Zustand
Coding Style:
- Use functional components and arrow functions.
- Strictly use TypeScript interfaces; avoid 'any'.
- Implement error handling for all async calls using try/catch blocks.
- Keep components small and modular.With this file in place, the AI stops guessing. It knows exactly which version of Next.js to use.
The "Iterative Prompting" loop
Don't ask for the whole app at once. You'll get generic, buggy code. Instead, build in "vertical slices."
Step 1: The Schema
I started by defining the data. I didn't write the SQL; I asked the AI to generate the Supabase migration script.
Prompt: Based on the .cursorrules, generate a SQL migration for a 'tasks' table. I need id (uuid), created_at, title (text), is_completed (boolean), and user_id (uuid referencing auth.users).
Step 2: The Logic
Once the DB was live, I needed the fetch logic. Instead of writing the function, I highlighted the empty page.tsx file and used the "Composer" feature (Cmd+I in Cursor) to generate the server component.
// This is what the AI generated after I provided the Supabase schema
import { createClient } from '@/utils/supabase/server';
export default async function TasksPage() {
const supabase = createClient();
const { data: tasks, error } = await supabase.from('tasks').select('*');
if (error) return <div>Error loading tasks: {error.message}</div>;
return (
<div className="p-6 max-w-md mx-auto">
<h1 className="text-2xl font-bold mb-4">My Tasks</h1>
<ul className="space-y-2">
{tasks?.map(task => (
<li key={task.id} className="p-2 border rounded shadow-sm">
{task.title}
</li>
))}
</ul>
</div>
);
}
When the AI hits a wall
At 2:15 PM, I hit a snag. The AI kept trying to use a client-side hook in a server component. It kept suggesting useEffect, which threw a "useState/useEffect only works in Client Components" error.
This is where most beginners panic. They just paste the error and ask "Why isn't this working?"
The fix is to be prescriptive. I told it: "You are attempting to use a client hook in a Server Component. Refactor the list item into a separate Client Component called TaskItem.tsx and pass the task data as a prop."
Immediately, the AI restructured the folder:
app/page.tsx(Server) → fetches data.components/TaskItem.tsx(Client) → handles the checkbox toggle.
| Approach | Result | Dev Time |
| :--- | :--- | :--- |
| "Fix this error" | 3-4 hallucinations, repetitive bugs | 45 mins |
| "Refactor to Client Component" | Correct architecture on first try | 2 mins |
Scaling your skills through community
You can't learn this in a vacuum. You'll eventually run into a weird edge case—like a specific MCP (Model Context Protocol) server crashing or a prompt that works in Claude but fails in GPT-4o—and you'll feel like you're shouting into a void.
This is why joining an AI beginner community is non-negotiable. You need a place to see how other devs are structuring their prompts and which tools are actually delivering. For example, checking out Prompt Sharing can show you exactly how a senior dev prompts for a complex React hook, saving you the trial-and-error phase.
The "magic" isn't in the model; it's in the workflow.
Refining the prompt for production
Once the basic app worked, I wanted to add a "Priority" tag. If you just say "add priority," the AI might just add a text field. To get it right, I used a structured prompt:
Prompt: Update the 'tasks' table to include a 'priority' column (enum: low, medium, high). Update the UI to show a colored badge based on the priority: low = gray, medium = yellow, high = red. Use Shadcn UI's Badge component.
This is the difference between a prototype and a product. Specificity kills bugs.
If you're still struggling with the setup, browsing through established Resources can help you find the right boilerplates so you aren't starting from zero every single time.
The final reality check
AI pair programming doesn't replace the need to understand the code. If you blindly accept every suggestion, you're building a house of cards.
Last week, the AI suggested a library for date formatting that was deprecated. Because I didn't check the documentation, I spent twenty minutes wondering why the build was failing.
My advice? Treat the AI as a very fast, slightly overconfident junior developer. Review every line. Question the architecture. If it suggests a library you've never heard of, go check its GitHub stars and last commit date before you npm install.
That's how you actually ship.
All Replies (0)
No replies yet — be the first!
