Human bugs and model bugs are shaped differently
After a few years of reviewing people's work, you build a model of how humans get things wrong. Off-by-one at a boundary. Forgot the null case. Misread the ticket. Copy-pasted and changed one of the three places. Ran out of energy at the end of the function, so the error handling is thinner than the happy path.
That model no longer covers what you are reading. Generated code fails in a different distribution, and the difference matters more than the rate.
Three shapes worth recognising
The API that does not exist. You get a call to a function or an argument that was never in the library:
df = df.drop_duplicates(subset=["invoice_id"], keep="latest")The real values are "first", "last", and False. This one is friendly, because it fails loudly:
ValueError: keep must be either "first", "last" or FalseYou find it the first time the line runs. The danger is only that it may not run until the monthly reconciliation job, at 02:00, in production.
The right shape with wrong semantics. This one does not fail loudly:
@retry(attempts=3, backoff=2.0)
def charge_customer(customer_id, amount_paise):
return payments.post("/v1/charges", ...)That is textbook resilient engineering. It looks like something a thoughtful senior engineer wrote. It is also retrying a non-idempotent POST, so a network timeout after the charge succeeded will bill the customer twice. Nothing in the code looks wrong, because nothing in the code *is* wrong locally. It is wrong about the world.
Others in this family: timezone-naive datetimes in a system with users in Lagos and Auckland, float arithmetic on money, a LIMIT 1000 quietly added to a query that a batch job depends on for completeness, a sort that is stable in the language it was pattern-matched from and unstable in yours.
The invented dependency. A suggested import for a package that does not exist. If you install it without checking, you may find that someone else registered that name first. Attackers watch which package names models commonly invent and publish malware under them — the practice has picked up the name slopsquatting. Any pip install, npm i, or go get you were prompted into is a supply-chain decision, and it deserves ten seconds of looking at the actual registry page: who publishes it, since when, how many real dependents.
Why this defeats an old heuristic
Here is the part to keep.
Experienced reviewers use a fast, mostly unconscious check: *does this look like something a competent person wrote?* Sensible names, consistent structure, comments that match, sane error handling. For thirty years that was a decent proxy for correctness, because producing code that looked careful required someone to be careful.
That proxy is now broken. Appearance and correctness came apart. Polish is free and correctness is not.
So the code that most deserves your suspicion is often the code that reads most smoothly — the confident, idiomatic, well-structured block that matches your house style exactly, because it was pattern-matched from a million examples of code that was right in a slightly different situation.
What to do instead
Verify every external call against the actual documentation. Not from memory, and not by asking the same model. Open the docs. This costs about thirty seconds per unfamiliar call and catches the entire first category.
Compute one case by hand. Take one realistic input, work out the expected output on paper, run it. This is old-fashioned and it catches semantic errors that no amount of reading catches, because reading is exactly the channel that has been compromised.
Ask the four boundary questions. What does this do when the input is empty? When there are duplicates? When it arrives out of order? When it is a thousand times bigger than expected? Most silent semantic bugs live in one of those four.
Be most careful where the stakes are asymmetric. Money, authentication, permissions, deletion, anything that sends a message to a human, anything that writes to another company's system. In those places, insist on understanding the code line by line, or do not ship it.
One reframe
Stop thinking of the output as a draft from a colleague and start thinking of it as a draft from a very well-read stranger who has never seen your system, cannot run it, will not be paged when it breaks, and will agree with you enthusiastically if you suggest something wrong. That framing gets your review posture roughly right.
Before you move on