The three ways AI code suggestions fail — and how to catch them before they ship
Code that doesn't compile gets caught immediately — the build fails, the red squiggle shows up, nobody ships it by accident. The code worth worrying about is the kind that compiles, passes a quick read, and is wrong in a way that only shows up under a condition nobody tested. After a year of reviewing AI-suggested code daily, it breaks down into three repeatable failure modes, and none of them look like "the AI made a mistake."
1. Plausible-but-wrong boundary conditions
def get_page(items, page_number, page_size=20):
start = page_number * page_size
end = start + page_size
return items[start:end]This reads correctly and works for page_number=1, 2, 3.... It's wrong for the case nobody tests by hand: page_number=0. Depending on what "page 1" is supposed to mean in the calling code, this either silently skips the first page or double-counts one — and the function looks so obviously correct that it sails through review.
The pattern isn't specific to pagination. Off-by-one boundaries, inclusive-vs-exclusive ranges, and "what happens with zero items" are where suggested code is most likely to be confidently, silently wrong — because the common cases dominate the training data, and the common cases are rarely the edge case that breaks.
For any suggested function that takes a count, index, or range: mentally run it with 0, 1, and the maximum expected value before running it with anything typical. That's where this failure mode hides.
2. Confidently outdated APIs
useEffect(() => {
fetch("/api/data").then((res) => res.json()).then(setData);
}, []);Nothing here is syntactically wrong, and it would have been correct advice at some point. Whether it's still correct depends entirely on what version of the framework the project is actually on — and a suggestion has no way of knowing that unless it's explicitly told. The failure isn't a made-up method that throws immediately; it's a real method that used to be the recommended way to do something and quietly isn't anymore.
This is the failure mode most likely to pass code review, because it looks exactly like code a competent developer would have written eighteen months ago. The fix isn't smarter prompting — it's checking the suggestion against the actual package.json in front of you, not against what "usually" ships with a framework of that name.
3. Solving a bigger problem than you asked
def add(a, b):
if not isinstance(a, (int, float)):
raise TypeError(f"Expected number, got {type(a).__name__}")
if not isinstance(b, (int, float)):
raise TypeError(f"Expected number, got {type(b).__name__}")
return a + bAsked for a two-line function, and it's reasonable-looking type validation that nobody requested, for a function that's called exclusively with numbers already validated three layers up. It's not wrong, exactly — it's scope creep that reads as thoroughness, which makes it harder to push back on than code that's simply broken. Every added branch is a branch someone has to maintain, test, and re-read the next time this function changes.
If a suggestion is noticeably longer than the request implied, ask what problem the extra code solves — specifically, for this codebase, right now. "It's more robust" isn't an answer if nothing calls this function with anything other than numbers.
What actually helps
None of these three get better with a longer or more polite prompt. What helps is treating every suggestion the way you'd treat a pull request from a contributor who's never seen this specific codebase before: confident, capable, and missing exactly the context that lives in your head — the framework version, the calling convention, the case that already can't happen here. The code is rarely wrong in an obvious way. It's wrong in the specific, quiet way that only shows up if you already know what to check.