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:
- What if
order.itemsis empty? (Returns 4.99 â is that correct?) - What if an item has no
weightproperty? (undefinedbreaks the math) - What if
destinationCountryis lowercase"us"? (Misses the domestic rate) - 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:
- Describe what you want (conversationally)
- AI generates an initial implementation
- You review the generated code
- You refine through conversation (âChange X to Yâ)
- AI updates based on your feedback
- 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:
- Do I understand what Iâm asking for? (No -> learn first)
- Can I review and validate the result? (No -> break it down)
- Is this repetitive/boilerplate work? (Yes -> ideal for AI)
- 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:
- âSupport other currencies (EUR, GBP, JPY)â
- âHandle edge cases: negative numbers, zero, very large numbersâ
- â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):
- Build model -> Build controller -> Build routes -> Build tests (one at a time)
- If context gets full, save progress and start a new conversation
- 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
PROMPT TO PRODUCTION