Claude Code/
Lesson

The same bugs repeat across AI-generated code. Language models optimize for the happy path: code that works for the example case but breaks at the boundaries. Learn these categories and you can spot failure points before running the code.

Off-by-one and boundary errors

Code works for 100 items but breaks for 0, or processes 9 when there are 10.

AI routinely generates code that assumes a list has at least one item:

// AI generates:
function getNewestItem(items) {
  return items.sort((a, b) => b.date - a.date)[0];
}

// Bug: if items is an empty array, this returns undefined
// Then anything calling getNewestItem(items).title crashes

The fix:

function getNewestItem(items) {
  if (items.length === 0) return null;
  return items.sort((a, b) => b.date - a.date)[0];
}

Off-by-one in loops:

// AI generates:
for (let i = 0; i <= items.length; i++) {
  process(items[i]);  // Bug: when i === items.length, items[i] is undefined
}

// Should be:
for (let i = 0; i < items.length; i++) {
  process(items[i]);
}

PaginationWhat is pagination?Splitting a large set of results into smaller pages so the server and client only handle a manageable chunk at a time. math wrong on the last page:

// AI generates:
const totalPages = Math.floor(totalItems / pageSize);
// Bug: if totalItems is 25 and pageSize is 10, this gives 2 instead of 3

// Correct:
const totalPages = Math.ceil(totalItems / pageSize);
Boundary caseWhat AI typically missesHow to check
Empty array/objectNo null/empty check before accessing propertiesTest with [] or {}
Single itemLogic that assumes at least 2 items (comparison, sorting display)Test with a 1-element array
Zero or negative numbersDivision by zero, negative indicesTest with 0 and -1
Last item in a listOff-by-one in loop condition (<= vs <)Test with the exact boundary value
Very large inputsNo pagination, memory issues, timeoutsTest with 10,000+ items
AI pitfall
When you ask AI to fix a boundary bug, it sometimes adds a guard for that specific case but introduces a new boundary bug in the process. After any boundary fix, re-test all edge cases, not just the one that was broken.
02

Async/awaitWhat is async/await?A syntax that lets you write asynchronous code (like fetching data) in a readable, step-by-step style instead of chaining callbacks. mistakes

The code looks correct line by line, but the execution order is wrong because AI missed an await or mishandled a promiseWhat is promise?An object that represents a value you don't have yet but will get in the future, letting your code keep running while it waits. chain.

Missing await

// AI generates:
async function loadUser() {
  const response = fetch('/api/user');  // ← missing await
  const data = response.json();         // ← response is a Promise, not a Response
  return data;                          // ← data is also a Promise
}

// The caller gets a Promise instead of user data
// No error is thrown - the code just silently doesn't work

This doesn't always crash, the Promise object is truthy, so if (data) checks pass. You get [object Promise] in the UI instead of actual data.

Unhandled promise rejections

// AI generates:
async function saveData(data) {
  const response = await fetch('/api/save', {
    method: 'POST',
    body: JSON.stringify(data),
  });
  const result = await response.json();
  return result;
}

// What's missing:
// - No check for response.ok (status might be 400 or 500)
// - No try/catch for network errors
// - No handling when the response isn't valid JSON

A more resilient version:

async function saveData(data) {
  try {
    const response = await fetch('/api/save', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    });
    if (!response.ok) {
      throw new Error(`Server error: ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    console.error('Failed to save:', error);
    throw error;  // Re-throw so the caller knows it failed
  }
}

Race conditions in useEffect

AI generates effects that don't account for component unmounting:

// AI generates:
useEffect(() => {
  async function load() {
    const data = await fetchProducts();
    setProducts(data);  // ← Bug: component might have unmounted
  }
  load();
}, []);

// Better:
useEffect(() => {
  let cancelled = false;
  async function load() {
    const data = await fetchProducts();
    if (!cancelled) setProducts(data);
  }
  load();
  return () => { cancelled = true; };
}, []);
03

State management bugs

Code reads correctly but behaves wrong due to how JavaScript handles closures and references.

Stale closures

// AI generates:
function Counter() {
  const [count, setCount] = useState(0);

  function handleClick() {
    // This will always set count to 1, no matter how many times you click
    setCount(count + 1);  // ← count is always 0 in this closure
    setCount(count + 1);  // ← still 0
    setCount(count + 1);  // ← still 0
  }

  return <button onClick={handleClick}>Count: {count}</button>;
}

// Fix: use the functional form of setState
function handleClick() {
  setCount(prev => prev + 1);
  setCount(prev => prev + 1);
  setCount(prev => prev + 1);
}

Mutation vs immutabilityWhat is immutability?A coding practice where you never change existing data directly, but instead create a new copy with the changes applied.

// AI generates:
function addItem(item) {
  items.push(item);       // ← Mutates the existing array
  setItems(items);        // ← React sees the same reference, doesn't re-render
}

// Fix: create a new array
function addItem(item) {
  setItems([...items, item]);  // ← New array reference triggers re-render
}

Same for objects, use spread syntax ({ ...user, name: 'New Name' }) instead of modifying the original.

AI pitfall
AI sometimes appears to copy objects with spread but still mutates nested objects. { ...user, address: user.address } copies the reference, not the object. Deep nesting requires deep copying.
04

Import and dependencyWhat is dependency?A piece of code written by someone else that your project needs to work. Think of it as a building block you import instead of writing yourself. errors

AI trains on many library versions and sometimes generates code for an APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses. that doesn't match your installed version.

Wrong package version

// AI generates code for React Router v6:
import { useNavigate } from 'react-router-dom';
const navigate = useNavigate();
navigate('/dashboard');

// But you have React Router v5, which uses:
import { useHistory } from 'react-router-dom';
const history = useHistory();
history.push('/dashboard');

Non-existent packages

AI occasionally references packages that don't exist or are deprecated. Run npm info package-name to verify before installing.

Incorrect import paths

// AI writes:
import { Button } from '../components/ui/Button';
// But your actual file is at:
// ../components/UI/button.tsx
// Wrong case, wrong extension - works on Mac, fails on Linux
Dependency bugHow to spot itFix
Wrong library versionError mentions a function that "doesn't exist"Check your package.json version, read the docs for your version
Non-existent packagenpm install fails or package has 0 weekly downloadsSearch npm for the correct package name
Wrong import path"Module not found" error with a file pathCheck the actual file path (case-sensitive on Linux)
Outdated APITypeScript error: "property X does not exist on type Y"Check the library's migration guide or changelog
05

Security blindspots

AI almost never generates secure code unless explicitly prompted.

No input validation

// AI generates an API endpoint:
app.post('/api/user', (req, res) => {
  const { name, email, role } = req.body;
  db.createUser({ name, email, role });  // ← No validation at all
  res.json({ success: true });
});

// Problems:
// - No check that name and email are strings
// - No check that email is a valid email format
// - A user can set their own role to "admin"
// - No length limits (someone could send a 10MB name)

Exposed secrets

// AI generates:
const apiKey = 'sk-abc123def456';  // ← Exposed in client-side code
const response = await fetch('https://api.openai.com/v1/chat', {
  headers: { 'Authorization': `Bearer ${apiKey}` }
});

APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses. keys in client-side code are visible to anyone who opens DevTools. Always use a backend proxy.

SQL injectionWhat is sql injection?An attack where user input is inserted directly into a database query, letting the attacker read, modify, or delete data. Parameterized queries prevent it. and XSSWhat is xss?Cross-Site Scripting - an attack where malicious JavaScript is injected into a web page and runs in other users' browsers, stealing data or hijacking sessions.

// AI generates:
const query = `SELECT * FROM users WHERE name = '${userInput}'`;
// SQL injection vulnerability - userInput could be: ' OR 1=1 --

// Fix: use parameterized queries
const query = `SELECT * FROM users WHERE name = ?`;
db.execute(query, [userInput]);
// AI generates:
element.innerHTML = `<div>${userComment}</div>`;
// XSS vulnerability - userComment could contain <script> tags

// Fix: use textContent for user-generated content
element.textContent = userComment;
06

Review checklist

Scan AI-generated code for these patterns before testing:

CheckWhat to look forTime needed
Empty/null guardsDoes the code handle empty arrays, null values, missing props?30 seconds
Async correctnessDoes every async function call have await? Are errors caught?30 seconds
State immutabilityAre arrays and objects copied (spread) instead of mutated (push/direct assignment)?30 seconds
Import verificationDo imports match your installed package versions? Do file paths exist?30 seconds
Input validationFor API endpoints: is user input validated before processing?15 seconds
Secret exposureAre API keys hardcoded? Are they in frontend code?15 seconds
Error handlingWhat happens when the network fails? When the API returns an error?30 seconds

This takes about three minutes and catches most AI-generated bugs before they reach your browser console.