The mental model that's served me best with AI-generated code is treating it exactly like a pull request from a capable but unfamiliar junior developer: confident, often correct, and occasionally wrong in ways that require you to actually read the diff rather than skim it and hit approve. Some mistakes announce themselves; others don't.

An easy-to-catch mistake usually breaks something you'd notice on a normal read-through or a quick test run — a function that returns the wrong type, an off-by-one in a loop bound, an import for a package that was never added. These surface the same way a junior's obvious mistake would: something visibly doesn't work, or a type checker flags it immediately.

A harder one takes more attention because it's locally correct but wrong in context — code that handles the happy path exactly right but silently drops an edge case the rest of the codebase already handles elsewhere, like a currency conversion that works for the sale amount but doesn't apply the same rounding rule the rest of the app uses for anything money-related. Nothing crashes; the output is just quietly inconsistent with a convention the model had no way to know about.

The genuinely hard one is a security or correctness issue that reads as clean, idiomatic code — a database query built with string formatting instead of a parameterized query, written in a style that looks like every other query in a tutorial the model was likely trained on. It's not obviously wrong; it's wrong in a way that only shows up under adversarial input, which is exactly why automated tests and a quick skim both tend to miss it.

The fix for all three is the same discipline good code review already asks for: read the actual diff, not just the description of what it's supposed to do; run it against inputs the happy path wouldn't cover; and hold it to the same convention checks you'd apply to any contributor who doesn't yet know your codebase's unwritten rules — because functionally, that's exactly what it is.

Easy to catch
def total_items(cart):
    return len(cart.items)  # cart.items is a dict; len() gives key count, not quantity

A quick test with a multi-quantity cart line fails immediately — the bug is visible the moment you exercise it.

Takes a careful read
def apply_discount(price, percent):
    return round(price - (price * percent / 100), 2)

# Elsewhere in the codebase, every other money calculation rounds
# using ROUND_HALF_UP via Decimal, not float rounding.

Nothing crashes and the happy-path numbers look right — the bug is inconsistency with a convention the model couldn't see.

Genuinely hard to spot
def find_user(username):
    query = f"SELECT * FROM users WHERE username = '{username}'"
    return db.execute(query)

Idiomatic-looking and correct for any normal username — it's a SQL injection vulnerability that only surfaces under adversarial input.