PROMPT TO PRODUCTION
Chapter 6 of 19 · 13 min read

Chapter 6: AI-Assisted Coding Fundamentals

Quick Start (5 minutes)

Let’s generate a real function with AI and review it for correctness. Open Claude Code in any JavaScript project directory.

Step 1: Generate a function

You: Write a function called calculateShippingCost that takes an
     order object with items (array of {price, weight}) and a
     destination country code. Use tiered pricing: domestic (US)
     is $0.50/lb, Canada/Mexico is $2/lb, everywhere else is $5/lb.
     Minimum shipping is $4.99.

Claude generates:

export function calculateShippingCost(order, destinationCountry) {
  const totalWeight = order.items.reduce((sum, item) => sum + item.weight, 0);

  let ratePerLb;
  if (destinationCountry === 'US') {
    ratePerLb = 0.50;
  } else if (['CA', 'MX'].includes(destinationCountry)) {
    ratePerLb = 2.00;
  } else {
    ratePerLb = 5.00;
  }

  const cost = totalWeight * ratePerLb;
  return Math.max(cost, 4.99);
}

Step 2: Review it – spot the problems

Before accepting, ask yourself:

  1. What if order.items is empty? (Returns 4.99 – is that correct?)
  2. What if an item has no weight property? (undefined breaks the math)
  3. What if destinationCountry is lowercase "us"? (Misses the domestic rate)
  4. Is there input validation? (No)

Step 3: Request fixes

You: Good start, but add these fixes:
     1. Validate that order has items array and each item has a numeric weight
     2. Make country code case-insensitive
     3. Return 0 if the cart is empty
     4. Add JSDoc with parameter types

Claude updates the function with all fixes. You just completed the core AI coding loop: generate, review, refine.

This is the workflow for the entire chapter. AI generates code fast. Your job is to catch what it misses.


Core Concepts (20 minutes reading)

6.1 The Code Generation Workflow

With AI assistance, you shift from writing every line to directing and reviewing:

  1. Describe what you want (conversationally)
  2. AI generates an initial implementation
  3. You review the generated code
  4. You refine through conversation (“Change X to Y”)
  5. AI updates based on your feedback
  6. You validate the final result

Think of it like being a head chef: you design the menu and taste every dish, while the AI does the prep work based on your instructions.

Key Insight: AI-assisted coding isn’t about replacing your skills – it’s about amplifying them. You still need to understand what good code looks like, but you spend less time typing and more time thinking.

What Changes (and What Doesn’t)

What Changes What Doesn’t Change
More time on design, less on syntax You still need to understand the problem domain
Rapid prototyping and experimentation You still need to review all generated code
AI generates docs alongside code You still need to design good architecture
AI creates tests as you develop You still need to make technical decisions

6.2 Levels of Specificity

You can generate code at different levels of detail:

High-Level (Architecture):

You: Create a REST API for a task management system with CRUD operations
     for tasks, user authentication, and task assignment features.

Result: Claude creates multiple files with complete structure

Medium-Level (Feature):

You: Add a feature to mark tasks as complete and filter by completion status

Result: Claude updates existing files and adds new functions

Low-Level (Function):

You: Write a function to calculate task completion percentage for a user

Result: Claude generates a single, focused function

Tip: Start with high-level requests to establish architecture, then refine with medium and low-level requests.

6.3 Code Review: Your Most Important Skill

With AI handling generation, your most important skill becomes code review. You need to verify that generated code is correct, secure, performant, maintainable, and tested.

What to Look For

1. Security Issues

AI-generated code frequently contains security vulnerabilities. Missing input validation is the most common issue.

// BAD: SQL Injection vulnerability
export function getUser(userId) {
  const query = `SELECT * FROM users WHERE id = ${userId}`;
  return db.query(query);
}

// GOOD: Parameterized query
export function getUser(userId) {
  const query = 'SELECT * FROM users WHERE id = $1';
  return db.query(query, [userId]);
}

Always ask: “What if a malicious user sends bad data?” Check for injection, XSS, missing auth checks, and weak crypto.

2. Error Handling

// BAD: No error handling
export async function fetchUserData(userId) {
  const response = await fetch(`/api/users/${userId}`);
  const data = await response.json();
  return data;
}

// GOOD: Comprehensive error handling
export async function fetchUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return { success: true, data: await response.json() };
  } catch (error) {
    console.error('Failed to fetch user data:', error);
    return { success: false, error: error.message };
  }
}

Always ask: “What happens if this call fails?”

3. Edge Cases

// BAD: Doesn't handle empty arrays
export function calculateAverage(numbers) {
  const sum = numbers.reduce((a, b) => a + b, 0);
  return sum / numbers.length;  // NaN if empty!
}

// GOOD: Handles edge cases
export function calculateAverage(numbers) {
  if (!Array.isArray(numbers) || numbers.length === 0) {
    return 0;
  }
  const sum = numbers.reduce((a, b) => a + b, 0);
  return sum / numbers.length;
}

Always ask: “What happens with empty input, null, or unexpected types?”

4. Performance

// BAD: O(n^2) -- includes() loops for each element
export function findDuplicates(arr) {
  const duplicates = [];
  for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[i] === arr[j] && !duplicates.includes(arr[i])) {
        duplicates.push(arr[i]);
      }
    }
  }
  return duplicates;
}

// GOOD: O(n) using Set
export function findDuplicates(arr) {
  const seen = new Set();
  const duplicates = new Set();
  for (const item of arr) {
    if (seen.has(item)) duplicates.add(item);
    else seen.add(item);
  }
  return Array.from(duplicates);
}

Always ask: “Will this perform well with large datasets?”

The Code Review Checklist

Use this for every AI-generated code block:

[ ] Functionality: Does it solve the stated problem?
[ ] Security: Are inputs validated? Injection/XSS risks?
[ ] Error Handling: Are errors caught and handled gracefully?
[ ] Edge Cases: Null, empty, boundary conditions?
[ ] Performance: Efficient algorithm? Unnecessary loops?
[ ] Maintainability: Readable? Well-named? Follows project conventions?

Rule: Never blindly accept generated code. Review it as if you’re reviewing a junior developer’s work. The AI is fast but not infallible.


6.4 Iterative Refinement: The Conversation Loop

The real power of AI-assisted coding comes from iterative refinement – having a conversation to perfect the code.

Example: Refining a Search Function

Round 1: Initial generation

You: Create a function to search products by name

Claude: [Creates basic search function]
        export function searchProducts(products, searchTerm) {
          return products.filter(p => p.name.includes(searchTerm));
        }

Round 2: Case sensitivity

You: Make it case-insensitive

Claude: [Updates function]
        export function searchProducts(products, searchTerm) {
          const lowerSearch = searchTerm.toLowerCase();
          return products.filter(p =>
            p.name.toLowerCase().includes(lowerSearch)
          );
        }

Round 3: Multiple fields + fuzzy matching

You: Also search descriptions, and add fuzzy matching for typos

Claude: [Enhances with fuse.js]
        import Fuse from 'fuse.js';

        export function searchProducts(products, searchTerm) {
          const fuse = new Fuse(products, {
            keys: ['name', 'description'],
            threshold: 0.3
          });
          return fuse.search(searchTerm).map(result => result.item);
        }

Refinement Best Practices

Be specific:

Bad:  "Make this better"
Good: "Add input validation to check that email is properly formatted"

One change at a time:

Bad:  "Add error handling, make it faster, and use TypeScript"
Good: "Add error handling for network failures"
      [Wait for change]
      "Now optimize for large datasets"

Explain the why:

Bad:  "Use bcrypt instead"
Good: "Use bcrypt instead of SHA-256 because we need proper password
       hashing with salt"

6.5 When to Use AI Coding (and When Not To)

Best use cases (green light):

  • Boilerplate and scaffolding – React components, Express routes, Mongoose schemas
  • Common algorithms – binary search, debounce, validation
  • Data transformations – CSV to JSON, API response mapping
  • Test generation – unit tests, edge case identification
  • Documentation – JSDoc comments, API docs, READMEs
  • Refactoring – extracting functions, simplifying logic, code translation

Use with caution (yellow light):

  • Complex business logic – requires deep domain knowledge you must verify
  • Performance-critical code – needs profiling, not just generation
  • Security-sensitive operations – OAuth flows, encryption; subtle bugs are critical

Avoid (red light):

  • Code you don’t understand – you can’t maintain or debug it
  • Learning fundamentals – don’t short-circuit your learning process
  • Making architecture decisions – strategic decisions require human judgment

The decision framework:

  1. Do I understand what I’m asking for? (No -> learn first)
  2. Can I review and validate the result? (No -> break it down)
  3. Is this repetitive/boilerplate work? (Yes -> ideal for AI)
  4. Is there a well-established solution? (Yes -> good for AI)

Golden Rule: Use AI to accelerate what you understand, not to bypass what you should learn.


6.6 Building a Complete Feature

Let’s see the full workflow: building a comment system for a blog app.

Step 1: Data model

You: I'm building a comment system for my blog. Create a Comment
     Mongoose schema with: content, author reference, blogPostId,
     timestamps, and ability to track edits.

Step 2: API controller

You: Now create the controller with functions for creating a comment
     (with rate limiting), getting comments for a post (paginated),
     and updating/deleting a comment (only by author).

Step 3: Review and refine

You: Read the controller file. I see issues:
     1. The spam protection is too simple
     2. Error messages are too generic
     3. Missing validation for empty content
     Fix these.

Step 4: Routes and tests

You: Create Express routes for these endpoints with auth middleware,
     then generate unit tests covering success cases, auth failures,
     invalid input, and pagination edge cases.

In about 60-90 minutes of conversational development (with thorough review), you’ve created a data model, controller, routes, and tests. The time saved comes from AI handling the mechanical work while you focus on architecture and review.

Important: Never skip the review process to chase speed gains. The AI generates code fast; your job is catching what it gets wrong.


Try This Now

Exercise 1: Generate and Review

Ask Claude Code to generate a validatePassword function that checks: minimum 8 characters, at least one uppercase, one lowercase, one digit, one special character. Review the generated code for:

  • Does it handle null/undefined input?
  • Does it return useful error messages (not just true/false)?
  • Is the regex correct? Test it mentally with “Password1!” and “password”.

Exercise 2: Iterative Refinement

Start with: “Create a function to format a price in USD.” Then refine:

  1. “Support other currencies (EUR, GBP, JPY)”
  2. “Handle edge cases: negative numbers, zero, very large numbers”
  3. “Add locale-aware formatting using Intl.NumberFormat”

Track how the function evolves through each round.


Deep Dive (optional, for mastery)

When Things Go Wrong

AI coding assistants are powerful but not perfect. Here are the most common failure modes and how to catch them.

1. Hallucinated Dependencies

AI may suggest packages that don’t exist, creating security risks.

// AI might suggest:
import { validateEmail } from 'express-validator-plus'; // Doesn't exist!

Why this is dangerous: Attackers register fake package names and fill them with malicious code. If you install a hallucinated dependency, you may be installing malware.

How to catch it:

  • Always verify package names on npmjs.com before installing
  • Check for low download counts or recent creation dates
  • Run npm search [package-name] to verify existence

2. Outdated or Deprecated APIs

AI training data has a cutoff, so it may suggest deprecated APIs.

// AI might suggest (outdated):
const bodyParser = require('body-parser'); // Built into Express since 4.16

// Modern approach:
app.use(express.json());

How to catch it: Check library docs for deprecation notices. Look for warnings in npm install output.

3. Plausible But Wrong Logic

Code that looks correct but has subtle logical errors.

// Looks right but returns the discount AMOUNT, not the final price:
export function calculateDiscount(price, discountPercent) {
  return price * discountPercent / 100;
}

// What you probably wanted:
export function calculateFinalPrice(price, discountPercent) {
  return price - (price * discountPercent / 100);
}

How to catch it: Write test cases with known inputs/outputs. Manually trace through the logic.

4. Tests That Pass but Test the Wrong Thing

This is one of the hardest bugs to catch. AI can generate tests that all pass but don’t actually verify the correct behavior.

// AI-generated test that passes but tests nothing useful:
test('calculateShipping returns a number', () => {
  const result = calculateShipping({items: [{weight: 5}]}, 'US');
  expect(typeof result).toBe('number'); // Always passes, even if wrong number
});

// Better test that verifies actual behavior:
test('calculateShipping charges $0.50/lb for domestic', () => {
  const result = calculateShipping({items: [{weight: 10}]}, 'US');
  expect(result).toBe(5.00); // 10 lbs * $0.50
});

How to catch it: Read the assertions, not just the test names. Ask “would this test fail if the function returned the wrong value?”

5. Copied Patterns from Wrong Framework Version

AI may mix patterns from different versions of a framework.

// React class component pattern applied to a functional component:
function UserProfile() {
  this.state = { user: null }; // Wrong! Functional components use hooks

  // Correct:
  const [user, setUser] = useState(null);
}

How to catch it: Know which version of your framework you’re using. If a pattern looks unfamiliar, check the docs for your specific version.

The “Trust But Verify” Checklist

Before accepting AI-generated code:

[ ] Dependencies: Do all imported packages actually exist on npm?
[ ] APIs: Are all APIs current (not deprecated)?
[ ] Logic: Does it return what I expect with known inputs?
[ ] Security: What if a malicious user sends bad data?
[ ] Tests: Do the test assertions actually verify correct behavior?
[ ] Framework: Are patterns correct for my framework version?

Advanced Refinement Patterns

Adapting to your codebase:

You: Make this work with our existing User model in src/models/User.ts
You: Follow our project's naming conventions (camelCase, no default exports)
You: Use our custom error handler from src/utils/AppError.ts

Building features in phases (for large context management):

  1. Build model -> Build controller -> Build routes -> Build tests (one at a time)
  2. If context gets full, save progress and start a new conversation
  3. Tell the new conversation: “I’m building [feature]. I’ve created [files]. Now I need [next step].”

Chapter Checkpoint

What you learned:

  • The AI coding loop: generate -> review -> refine -> validate
  • How to review generated code for security, edge cases, and performance
  • Iterative refinement through specific, focused requests
  • When AI coding helps and when it hurts
  • Common AI mistakes: hallucinated packages, outdated APIs, subtle logic bugs

You can now:

  • Generate complete functions and features using conversational prompts
  • Catch security vulnerabilities, missing error handling, and edge cases in AI code
  • Refine code through iterative conversation (one change at a time, with context)
  • Make informed decisions about when to use AI assistance vs. coding manually
  • Build multi-file features using the step-by-step conversational workflow