Workshop AI-Driven Development — Session 1
4 hours
From idea to working prototype with a rigorous methodology
2026 WayUp
4 intensive hours
The difference between “it works” and “it’s well built”
| Vibe Coding | AIDD | |
|---|---|---|
| Approach | “Build me an app” | Specs → Plan → Execute → Test |
| Prompts | Vague, one-shot | Structured, contextual, iterative |
| Quality | It compiles = good enough | Tests, review, architecture |
| Debugging | “Fix this” in a loop | Root cause analysis, stack traces |
| Result | Throwaway prototype | Maintainable & deployable code |
Workshop goal: give you a methodology so that AI makes you 10x more productive, not 10x more dangerous.
6 steps for each feature — repeat for every issue
Golden rule: never merge code you don’t understand. The AI proposes, the engineer decides.
The vision.md: your contract with the AI
The spec IS the prompt — vague specs = vague code
Each issue is a self-contained instruction for the AI
## Create task with title, priority, due date
**As a** logged-in user
**I want to** create a task with details
**So that** I can track my work
### Acceptance Criteria
- [ ] Form: title (required, min 3 chars),
description (optional), priority
(high/medium/low select), due date (future)
- [ ] Save to Firestore: `tasks/{taskId}`
with userId from AuthContext
- [ ] Show success toast, redirect to /dashboard
- [ ] On validation error: red text below field
- [ ] On Firestore error: generic error message
### Technical Notes
- File: `src/app/dashboard/new/page.tsx`
- "use client", Tailwind, existing Task type
- Use `addDoc` from firebase/firestore
- Priority badge colors: high=red, med=yellow, low=green
Rule of thumb: if the AI needs to ask “what should happen when...”, your spec is incomplete.
Break features into atomic, AI-sized tasks
The CRAFT framework
Stack, project state, existing files, conventions. The richer the context, the better the result.
What you want precisely. Paste the GitHub issue, acceptance criteria, mockups.
The concrete action: create a file, modify a function, add a component. One verb, one object.
The expected output format: TypeScript, React component, API route, Jest test. Be explicit.
How to verify: expected behavior, edge cases, what must NOT happen.
Structured prompt vs vague prompt
Context: Next.js 14 + TypeScript + Tailwind project.
Auth via Firebase.
Existing file: src/lib/firebase.ts (config initialized).
Layout: src/app/layout.tsx with AuthProvider.
Requirement: Create an email/password login page
with:
- Email + password fields with validation
- "Sign in" button + "Create an account" link
- Firebase error handling
(wrong-password, user-not-found)
Action: Create src/app/login/page.tsx
Format: React client component ("use client"),
Tailwind for styling,
signInWithEmailAndPassword from Firebase.
Test: The user can sign in and is redirected
to /dashboard. On error, a red message appears
below the form.
Bad: “Add a login form”
Good: structured CRAFT prompt →
Each section of the prompt has a role:
Choosing the right tool for the right task
| Tool | Strengths | Ideal Use |
|---|---|---|
| Claude Code | Long context, reasoning, refactoring | Architecture, complex features, debugging |
| Cursor | IDE-integrated, file context, fast | Continuous editing, completion, inline changes |
| GitHub Copilot | Fast autocompletion, patterns | Boilerplate, standard implementations |
| ChatGPT / Claude.ai | Free conversation, exploration | Design, brainstorming, concept explanation |
| v0 / Bolt | Full UI generation | Rapid component prototyping |
For this workshop: we will mainly use Cursor or Claude Code for development.
The tools we will use today
The lifecycle of a feature with AIDD
feature/feature-namecloses #3)Discipline: 1 feature = 1 branch = 1 PR. No catch-all commits.
Start a project in 5 minutes
npx create-next-app@latest mon-projet --typescript --tailwind --app --eslint
cd mon-projet
git init && git add . && git commit -m "initial scaffold"
This command creates a complete Next.js 14 project with TypeScript, Tailwind CSS, ESLint, and the App Router. Everything is ready to go.
Ask the AI to create the structure
Here is my vision.md: [paste contents]
Create the following file structure:
- src/app/page.tsx (home page)
- src/app/layout.tsx (header/footer layout)
- src/components/Header.tsx
- src/components/Footer.tsx
- src/lib/firebase.ts (Firebase config)
Use strict TypeScript, Tailwind CSS,
and Next.js 14 App Router conventions.
Tip: including the vision.md gives the AI a big-picture view for consistent decisions.
Step 1: Configuration
// src/lib/firebase.ts
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
const app = initializeApp({
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_...,
projectId: process.env.NEXT_PUBLIC_...,
// ...
});
export const auth = getAuth(app);
export const db = getFirestore(app);
All Firebase config in src/lib/firebase.ts. Other files import auth and db from here.
Security: all keys in .env.local, never hardcoded. File in .gitignore by default.
Step 2: AuthProvider & hook
// src/contexts/AuthContext.tsx
"use client";
import { createContext, useContext,
useEffect, useState } from 'react';
import { onAuthStateChanged, User }
from 'firebase/auth';
import { auth } from '@/lib/firebase';
const AuthContext = createContext<{
user: User | null;
loading: boolean;
}>({ user: null, loading: true });
"use client" — required for React hooksonAuthStateChanged — Firebase listener that tracks auth stateuser and loadingPrompt tip: ask the AI to create the complete AuthProvider with the useAuth() hook and route protection.
From commit to live site in 60 seconds
git push origin mainEngineer advantage: every push creates a preview deployment. You can showcase your progress at any time.
Commit messages that make sense
feat: add login page with Firebase auth
fix: handle wrong-password error on login
refactor: extract AuthProvider to context
docs: add API documentation to README
test: add unit tests for auth helpers
type: short imperative description
Forbidden: git add . without reviewing, git push --force, “WIP” commits.
1 feature = 1 branch = 1 PR — never commit directly to main
The art of technical conversation
Initial structured request using CRAFT. The AI generates a first version.
“The code works but I want the form to validate the email client-side before submit. Also add a loading state on the button.”
“The loading state is good. However, the error message should disappear when the user starts typing again.”
Rule: each iteration must be specific. No “improve the code.” Say exactly what is missing or wrong.
Recognizing and correcting AI errors
Recommended project structure
mon-projet/
├── src/
│ ├── app/ # App Router (pages & API)
│ │ ├── layout.tsx # Main layout
│ │ ├── page.tsx # Home page
│ │ ├── login/page.tsx # Login page
│ │ ├── dashboard/page.tsx # Protected page
│ │ └── api/ # API routes
│ │ └── tasks/route.ts # CRUD tasks
│ ├── components/ # Reusable components
│ │ ├── Header.tsx
│ │ ├── Footer.tsx
│ │ └── TaskCard.tsx
│ ├── contexts/ # React Contexts
│ │ └── AuthContext.tsx
│ ├── lib/ # Utilities
│ │ ├── firebase.ts
│ │ └── utils.ts
│ └── types/ # TypeScript types
│ └── index.ts
├── public/ # Static assets
├── .env.local # Environment variables
├── vision.md # Project specifications
└── package.json
The right mindset for AI-assisted development
Classic trap: letting the AI make architecture decisions. That is your responsibility as an engineer, not the AI’s.
Engineer checklist before each commit
Checklist before getting started
Head to the lab → practical-work-1-guided-project.html