Skip to main content

Good AI Workflow Example

This page walks through a complete example of using AI responsibly to build a feature. It shows the difference between "vibe coding" (prompting randomly until something works) and AI-assisted engineering (structured, spec-driven development).

We'll build a simple feature: a contact form that sends email notifications.


The Bad Approach (Vibe Coding)

Prompt: "Make me a contact form"

The AI generates a form. It works. You ship it.

Problems you won't discover until later:

  • The form doesn't validate email addresses
  • Spam bots can submit unlimited forms
  • The form exposes your server's email configuration
  • There's no confirmation message for users
  • Submitted data isn't stored anywhere
  • The form breaks if the email service is down

The Good Approach (AI-Assisted Engineering)

Step 1: Write a Spec

Before opening any AI tool, write down what you need:

💡 New to Markdown? The examples on this page use Markdown — a simple way to format text with symbols like # for headings and - for bullet lists. If any of this looks unfamiliar, check out our Markdown Crash Course — it covers everything you need in 2 minutes.

# Contact Form Requirements

## What It Should Do
- Visitors can send a message through a contact form
- The message is emailed to admin@myapp.com
- The visitor sees a confirmation after sending
- Submitted messages are saved in the database

## Form Fields
- Name (required, text, max 100 characters)
- Email (required, valid email format)
- Subject (required, text, max 200 characters)
- Message (required, textarea, max 5000 characters)

## Security Requirements
- Rate limiting: max 3 submissions per IP per hour
- CAPTCHA or honeypot field to prevent spam
- Input sanitization to prevent XSS attacks
- Email validation on client and server side

## Error Handling
- If email service is down → save to database, retry later
- If validation fails → show specific error messages
- If rate limit exceeded → show "Too many submissions. Please try again later."

## What Should Happen
- On success → show "Thank you! We'll get back to you within 24 hours."
- On failure → show "Something went wrong. Please try again or email us directly at admin@myapp.com."

This spec takes 15 minutes to write. It will save hours of back-and-forth with the AI.


Step 2: Plan the Architecture

Draw a simple diagram of how the feature works:

User fills form → Form validates input → Save to database → Send email → Show confirmation
↓ ↓
If save fails If email fails
↓ ↓
Show error Queue for retry

You don't need technical diagrams. A simple text description is enough.

Data flow:

  1. User submits form
  2. Server validates all fields
  3. If valid: save message to database
  4. Try to send email notification
  5. If email succeeds: mark message as "sent"
  6. If email fails: mark message as "pending" (retry later)
  7. Show confirmation to user

Step 3: Break Into Tasks

Break the feature into small, independent pieces:

## Task 1: Database Table
- Create a "contact_messages" table with fields: id, name, email, subject, message, status, created_at

## Task 2: Form HTML
- Create the contact form with all 4 fields
- Add client-side validation (required fields, email format)
- Add honeypot field for spam prevention

## Task 3: Form Processing
- Handle form submission
- Validate input on server side
- Save to database
- Handle validation errors

## Task 4: Email Notification
- Send email to admin when form is submitted
- Handle email service failures gracefully
- Queue failed emails for retry

## Task 5: Rate Limiting
- Track submissions by IP address
- Block submissions after 3 per hour
- Show appropriate error message

## Task 6: Confirmation Page
- Show success message after submission
- Show error message if something fails
- Link back to home page

Step 4: Implement Each Task With AI

Now feed each task to your AI tool, one at a time.

Task 1 prompt:

Create a database table called "contact_messages" with the following fields:
- id: auto-incrementing primary key
- name: string, max 100 characters, required
- email: string, max 255 characters, required
- subject: string, max 200 characters, required
- message: text, max 5000 characters, required
- status: string, default 'unread' (values: unread, read, sent, failed, pending)
- created_at: timestamp, auto-set on creation
- ip_address: string, max 45 characters (for rate limiting)

Use [your database technology, e.g., Prisma schema / SQL migration / Django model].

Task 2 prompt:

Create a contact form HTML with:
- Fields: name, email, subject, message
- Client-side validation: all fields required, email must be valid format
- A hidden honeypot field (a text field hidden with CSS, named "website" — if filled, it's a bot)
- Responsive design (works on mobile)
- Clean, minimal styling
- Error messages shown next to each field
- Submit button with loading state

The form should POST to /api/contact

Task 3 prompt:

Create a server-side endpoint that handles contact form submission at POST /api/contact.

Requirements from the spec:
- Validate all fields (name required, email valid format, subject required, message required)
- Sanitize input to prevent XSS
- Save to the contact_messages table
- Return JSON response: { success: true } or { success: false, errors: [...] }
- Never expose technical error details to the user

Continue this pattern for each task.


Step 5: Review Each Output

After the AI generates code for each task, review it:

Checklist for each task:

  • Does it do what the spec says?
  • Does it handle errors gracefully?
  • Are there any hardcoded values that should be configurable?
  • Is the code consistent with the rest of the project?
  • Are there any obvious security issues?
  • Does it follow the project's coding patterns?

Step 6: Test Everything

Test the complete feature:

Functional tests:

  • Submit the form with valid data → should succeed
  • Submit with missing fields → should show validation errors
  • Submit with invalid email → should show email error
  • Submit with empty honeypot → should succeed
  • Submit with filled honeypot → should silently fail (bot detected)

Security tests:

  • Submit 4 times in one hour → 4th should be rate-limited
  • Try SQL injection in form fields → should be blocked
  • Try XSS in form fields → should be sanitized
  • Check that no API keys are exposed in the code

Error handling tests:

  • Disconnect the email service → form should still save to database
  • Check that failed emails are queued for retry

Step 7: Deploy

Before deploying:

  • All tests pass
  • Rate limiting is configured for production
  • Email service credentials are in environment variables
  • Database migration is ready
  • Error monitoring is set up
  • Rollback plan is documented

The Comparison

AspectBad Workflow (Vibe Coding)Good Workflow (AI-Assisted Engineering)
PlanningNone15-minute spec
TasksOne big prompt6 small, focused tasks
SecurityHoped for the bestExplicitly specified
Error handlingNoneEvery failure mode covered
ReviewAssumed it was correctChecked each piece
Testing"It runs, ship it"Systematic testing
ResultFragile, insecureRobust, maintainable

The Bottom Line

The good workflow takes longer upfront but saves time overall. The bad workflow is fast at first but creates problems that take longer to fix than they would have to prevent.

The structured approach — spec, architecture, tasks, implementation, review, test, deploy — is not bureaucracy. It's efficiency. Each step catches problems when they're cheap to fix, instead of discovering them in production when they're expensive and urgent.


Start Using This Workflow Today

Download the AI Project Template — it's designed to guide you through exactly this workflow, from planning to deployment.

Download the Starter Template →