Beginner22 min

Master effective workflows for generating high-quality code with AI—from single functions to complex features.

Code Generation Workflows

Code generation is where AI shines, but the quality of output depends heavily on your workflow. This lesson covers proven patterns for generating code that works correctly the first time.

The Staged Generation Approach

Don't generate everything at once. Use stages:

Terminal
Stage 1: Types/Interfaces
Stage 2: Core Logic
Stage 3: Integration
Stage 4: Error Handling
Stage 5: Tests

Each stage builds on the previous, with review between stages.

Stage 1: Types First

Always start with types and interfaces. They serve as contracts for the rest of the code.

Terminal
Define TypeScript interfaces for a shopping cart feature.

Requirements:
- Cart contains items with product, quantity, and price
- Support for discount codes
- Track subtotal, tax, and total
- Handle multiple shipping addresses

Output:
1. All necessary interfaces
2. Type unions for states (empty, has-items, checked-out)
3. Utility types if needed

Result:

Terminal
interface CartItem {
  productId: string;
  name: string;
  quantity: number;
  unitPrice: number;
  totalPrice: number;
}

interface DiscountCode {
  code: string;
  type: 'percentage' | 'fixed';
  value: number;
}

interface CartTotals {
  subtotal: number;
  discount: number;
  tax: number;
  shipping: number;
  total: number;
}

type CartState =
  | { status: 'empty' }
  | { status: 'has-items'; items: CartItem[]; totals: CartTotals }
  | { status: 'checked-out'; orderId: string };

Stage 2: Core Logic

With types defined, generate the core business logic:

Terminal
Using these types:
[paste types from Stage 1]

Generate the cart calculation functions:
1. calculateItemTotal(item: CartItem): number
2. calculateSubtotal(items: CartItem[]): number
3. applyDiscount(subtotal: number, discount: DiscountCode): number
4. calculateTax(amount: number, taxRate: number): number
5. calculateCartTotals(items: CartItem[], discount?: DiscountCode): CartTotals

Requirements:
- Pure functions, no side effects
- Handle edge cases (empty cart, invalid discount)
- Round to 2 decimal places

Stage 3: Integration Layer

Connect the pure logic to your application:

Terminal
Using these types and functions:
[paste types and functions]

Create a React hook useCart that:
- Manages cart state
- Provides addItem, removeItem, updateQuantity
- Applies discount codes
- Persists to localStorage
- Syncs with server on changes

Use this pattern from our codebase:
[paste example hook]

Stage 4: Error Handling

Add comprehensive error handling:

Terminal
Add error handling to this cart implementation:
[paste code]

Handle:
- Network failures when syncing
- Invalid quantities (negative, non-integer)
- Out-of-stock items
- Expired discount codes
- Concurrent modification conflicts

Use our error handling pattern:
- Custom error classes
- User-friendly error messages
- Error recovery where possible

Stage 5: Tests

Generate tests for each layer:

Terminal
Generate tests for the cart calculation functions:
[paste functions]

Include:
- Happy path tests
- Edge cases (empty arrays, zero values)
- Error conditions
- Boundary values (max quantities, min prices)

Use Jest with our test patterns:
[paste example test]

The Skeleton Approach

For complex features, generate a skeleton first:

Step 1: Request Skeleton

Terminal
Create a skeleton for a REST API file upload service.

Include:
- Express router structure
- Endpoint stubs (no implementation)
- Middleware placeholders
- Error handling structure
- Type definitions

Don't implement the actual logic yet—just the structure.

Step 2: Fill In One Section

Terminal
Here's the skeleton:
[paste skeleton]

Now implement only the upload endpoint with:
- Multer for file handling
- File validation (size, type)
- Storage to local filesystem
- Return file metadata on success

Step 3: Continue Section by Section

Terminal
Now implement the download endpoint using the same patterns.
[continue for each section]

Pattern-Based Generation

Leverage existing patterns in your codebase:

Example prompt:

Here's an existing component that follows our patterns:

Terminal
// ExistingComponent.tsx
export const UserCard: FC<UserCardProps> = ({ user, onEdit }) => {
  // ... implementation
};

Generate a similar component for products:

  • Same styling approach
  • Same prop patterns
  • Same state management approach

Product-specific requirements:

  • Show image, name, price
  • Add to cart button
  • Quantity selector

Incremental Enhancement

Build up complexity gradually:

Version 1: Basic

Terminal
Create a basic search input that:
- Accepts user input
- Calls onSearch prop with value
- Basic styling

Version 2: Add Features

Terminal
Enhance this search input:
[paste v1 code]

Add:
- Debouncing (300ms)
- Loading indicator
- Clear button

Version 3: Polish

Terminal
Finalize this search input:
[paste v2 code]

Add:
- Keyboard shortcuts (Escape to clear)
- Accessibility (ARIA labels)
- Focus styles
- Placeholder text customization

Context-Rich Generation

Provide rich context for better results:

Example prompt:

Generate a data fetching hook for user profiles.

Project Context:

  • React 18 with TypeScript
  • React Query for data fetching
  • Zod for validation
  • API returns: { user: User, permissions: string[] }

Existing Pattern to follow:

Terminal
// How we structure hooks
export const useUsers = (filters: UserFilters) => {
  return useQuery({
    queryKey: ['users', filters],
    queryFn: () => api.getUsers(filters),
    select: (data) => data.users,
  });
};

Requirements:

  • Fetch profile by userId
  • Include error handling
  • Support refetching
  • Return loading/error states

Handling Large Features

Chunk the Work

Terminal
I need to build a complete comment system.

Instead of generating everything, let's chunk it:

Chunk 1: Database schema and Prisma models
Chunk 2: API endpoints (CRUD)
Chunk 3: React components (list, form, item)
Chunk 4: Real-time updates (WebSocket)

Let's start with Chunk 1. Generate the Prisma schema for comments:
- Supports nested replies (2 levels max)
- Author relationship
- Soft delete
- Timestamps

Track Dependencies

Terminal
We've completed:
- [x] Prisma schema (generates Comment, Reply)
- [x] API endpoints (GET, POST, DELETE)

Now generate Chunk 3: React components.

These components should use:
- The types generated from Prisma
- The API endpoints we created
- Our existing component patterns

Generate:
1. CommentList - displays all comments
2. CommentForm - new comment form
3. CommentItem - single comment display
4. ReplyForm - reply to comment form

Quality Checks

After generating code, always verify:

Type Safety Check

Terminal
Review this generated code for type safety:
[paste code]

Check for:
- Any use of 'any' type
- Missing type annotations
- Possible null/undefined issues
- Type assertions that could fail

Pattern Consistency Check

Terminal
Does this code follow our project patterns?

Our patterns:
- Error handling: [pattern]
- Naming: [conventions]
- File structure: [structure]

Generated code:
[paste code]

What deviates from our patterns?

Security Check

Terminal
Review this generated code for security:
[paste code]

Check for:
- Input validation gaps
- Injection vulnerabilities
- Authentication/authorization issues
- Data exposure risks

Common Generation Pitfalls

1. Generating Without Context

Always provide project context and existing patterns.

2. Accepting Code Blindly

Review and test before integrating.

3. Overriding Good Code

Don't regenerate working code—enhance it.

4. Generating Too Much

Generate in stages, not all at once.

Practice Exercise

Generate a feature using the staged approach:

Feature: Email notification preferences

  1. Stage 1: Define types for notification settings
  2. Stage 2: Create validation functions
  3. Stage 3: Build the settings form component
  4. Stage 4: Add error handling and loading states
  5. Stage 5: Generate tests

Document your prompts and review after each stage.

Summary

  • Use staged generation: types → logic → integration → errors → tests
  • Generate skeletons first for complex features
  • Provide rich context and existing patterns
  • Chunk large features into manageable pieces
  • Always verify generated code before using

Next Steps

Next, let's explore Test-Driven Development with AI—using tests to guide code generation.

Mark this lesson as complete to track your progress