How to Read and Verify Code an AI Wrote for You
To verify AI-generated code, check it against five criteria: correctness (does the logic actually work?), security (are there injection points or leaked secrets?), performance (will it scale?), dependencies (do the APIs and libraries actually exist?), and readability (will your team understand it in six months?). Never ship AI code you have not read line by line.
Why You Cannot Just Trust AI-Generated Code
AI coding tools are genuinely useful. Claude, Copilot, ChatGPT, and their successors can write working code in seconds that would take you twenty minutes. That speed is real, and it changes how productive you can be.
But there is a trap. The code that comes back looks professional. It has clean variable names, reasonable structure, and often includes comments. It looks like something a competent developer wrote. That surface-level polish makes it easy to skim, nod, and paste it into your project. And that is exactly where things go wrong.
AI-generated code can contain bugs that are surprisingly hard to spot. The model might call an API method that was renamed two versions ago. It might implement a sorting algorithm that works on small inputs but blows up on large datasets. It might store a password in plain text because you did not specify "hash the password." These are not rare edge cases. They happen regularly, and the better the AI gets at producing clean-looking code, the more dangerous these hidden issues become.
The developers who get the most value from AI tools are not the ones who accept every output. They are the ones who review efficiently and catch problems before they hit production. That is the skill this guide is about.
The Most Common Mistakes AI Makes in Code
After reviewing thousands of lines of AI-generated code (both from our own work and from McTaba Labs cohort members), certain patterns show up again and again. Knowing what to look for makes your reviews faster and more effective.
Hallucinated APIs and Methods
This is the most notorious AI coding mistake. The model generates code that calls a function or method that does not exist. It might reference stripe.customers.fetchAll() when the actual method is stripe.customers.list(). Or it might import a package that sounds plausible but was never published to npm. These hallucinations are especially common with less popular libraries. The model has seen fewer examples, so it "guesses" at the API surface. Always verify imports and method calls against the official documentation.
Outdated Patterns and Deprecated Methods
AI models are trained on data that has a cutoff date, and even within that training data, they have seen more old code than new code (simply because older code has been online longer). You will regularly see patterns like class components in React instead of hooks, callback-based Node.js code instead of async/await, or deprecated Express middleware. The code works, technically, but it is using patterns your team abandoned years ago.
Security Vulnerabilities
AI rarely thinks about security unless you explicitly ask. Common issues include: SQL queries built with string concatenation instead of parameterized queries, missing input validation on user-submitted data, API keys hardcoded in the source, CORS configured to allow all origins, and missing authentication checks on protected routes. The model optimizes for "code that works," not "code that is secure."
Missing Edge Cases
AI tends to write code for the happy path. It will handle the case where the user submits a valid form but skip the case where the input is empty, null, undefined, or a 50MB string. It will process a list of items but not handle an empty list. It will parse a date but not check if the date is in the future. Edge case handling is where AI code breaks in production.
Over-Engineering
Sometimes the opposite problem shows up. You ask for a simple utility function and get back an abstract factory pattern with generics, three interfaces, and a plugin system. AI models have been trained on enterprise codebases, and they sometimes produce unnecessarily complex solutions for simple problems. If the code is harder to understand than the problem it solves, simplify it.
The AI Code Review Checklist
Use this checklist every time you review a non-trivial piece of AI-generated code. It is not exhaustive, but it catches the majority of issues we see in practice.
1. Does it actually run?
Before you read a single line, paste it into your editor and try to run it. Check for syntax errors, missing imports, and type errors. About 10-15% of AI-generated code snippets will not compile or run on the first try. [TODO: verify percentage with broader data] Catching this early saves you from analyzing code that was broken from the start.
2. Do the dependencies exist?
Check every import statement. Does the package exist? Is the version compatible? Does the specific function or class you are importing actually exist in that package? Run npm info <package> or check the package's documentation. This catches hallucinated packages and renamed exports.
3. Is the logic correct?
Trace through the code mentally with a few example inputs. Does it produce the right output? Pay special attention to: off-by-one errors in loops, incorrect comparison operators (using = instead of ===), wrong variable references in closures, and conditions that are inverted (checking if (isValid) when it should be if (!isValid)).
4. What happens at the edges?
Test with: empty input, null or undefined, extremely large input, special characters, concurrent calls, and negative numbers. If the code does not handle these cases, add the handling yourself or ask the AI to revise with specific edge cases in mind.
5. Is it secure?
Check for: user input used directly in SQL or shell commands, missing authentication or authorization, secrets in the source code, overly permissive CORS or file access, and data being logged that should not be (passwords, tokens, personal information).
6. Will it perform at scale?
Look for: nested loops on large data sets (O(n^2) or worse), database queries inside loops (the N+1 problem), missing pagination on list endpoints, large objects held in memory unnecessarily, and synchronous operations that should be async.
7. Is it readable?
Could a teammate understand this code without explanation? Are the variable names clear? Is the code more complex than it needs to be? If the AI produced a clever one-liner, consider whether a straightforward three-line version would be easier to maintain.
Checking Logic: A Walkthrough
Let us walk through a real example. Say you asked an AI to write a function that calculates the average rating from a list of product reviews. The AI returns this:
function getAverageRating(reviews: Review[]): number {
let total = 0;
for (const review of reviews) {
total += review.rating;
}
return total / reviews.length;
}
At first glance, this looks correct. And for the happy path, it is. But let us apply the checklist.
What happens with an empty array? If reviews is empty, reviews.length is 0, and you get 0 / 0 which is NaN in JavaScript. That NaN will propagate through your UI and show up as "NaN stars" to your users. The fix is a guard clause at the top: if (reviews.length === 0) return 0;
What if a review has no rating? If review.rating is undefined, adding it to total makes total become NaN. You need to either filter out reviews without ratings or default to 0.
What about the return type? This returns a raw float like 3.666666666667. Most applications want a rounded value. Should it be rounded to one decimal? Two? The AI did not ask, so it did not handle it.
None of these bugs would show up in a quick manual test with three reviews that all have ratings. They only surface in production, with real data, when a new product has zero reviews or when a legacy review record is missing the rating field. This is exactly the kind of issue that AI misses and a careful reviewer catches.
The lesson: trace through the code with both normal and abnormal inputs. The happy path is usually fine. The edges are where the bugs live.
Security Review: What AI Gets Wrong Most Often
Security is where AI-generated code is most consistently dangerous, because security issues are invisible during normal usage. The code works perfectly until someone exploits it.
SQL Injection
AI frequently generates code like this:
// Dangerous: AI-generated query with string interpolation
const result = await db.query(
`SELECT * FROM users WHERE email = '${userEmail}'`
);
This works fine for jane@example.com. It also works fine for ' OR '1'='1' --, which returns every user in the database. Always use parameterized queries:
// Safe: parameterized query
const result = await db.query(
'SELECT * FROM users WHERE email = $1',
[userEmail]
);
Missing Authorization Checks
AI will often generate API endpoints that authenticate the user (confirm they are logged in) but skip authorization (confirm they have permission to access this specific resource). You will get an endpoint where any authenticated user can view any other user's data just by changing the ID in the URL. Always check that the requesting user has permission to access the specific resource, not just that they are logged in.
Exposed Secrets
When AI writes example code for API integrations, it sometimes includes placeholder API keys directly in the source code. Even if the key is fake, the pattern is dangerous because a developer might replace it with a real key and forget to move it to an environment variable. Worse, some AI tools have been known to reproduce real API keys from their training data. Always use environment variables for secrets, and check that the AI did not hardcode any.
Overly Permissive CORS
This one is extremely common. AI almost always sets CORS to origin: '*' because it "just works" during development. In production, this means any website can make requests to your API, which is a serious problem if your API handles sensitive data or financial transactions. Set your CORS origin to your actual domain.
Security review should be non-negotiable for any AI-generated code that handles user data, authentication, payments, or file access. If you are not sure whether a pattern is secure, look it up. Do not trust the AI's judgment on security.
Performance Red Flags in AI-Generated Code
AI tools optimize for correctness (does it produce the right answer?), not for performance (does it produce the right answer fast enough at scale?). When you are building a side project, this does not matter much. When you are building a production app that serves real users, it matters a lot.
The N+1 Query Problem
This is the most common performance issue in AI-generated backend code. The AI writes a loop that executes a database query for each item in a list:
// Bad: one query per order (N+1 problem)
const orders = await db.query('SELECT * FROM orders WHERE user_id = $1', [userId]);
for (const order of orders) {
order.items = await db.query('SELECT * FROM order_items WHERE order_id = $1', [order.id]);
}
If a user has 100 orders, this executes 101 database queries. The fix is a single query with a JOIN or an IN clause:
// Good: two queries total, regardless of order count
const orders = await db.query('SELECT * FROM orders WHERE user_id = $1', [userId]);
const orderIds = orders.map(o => o.id);
const items = await db.query('SELECT * FROM order_items WHERE order_id = ANY($1)', [orderIds]);
Loading Entire Datasets Into Memory
AI often writes code that fetches all records from a database table and then filters or processes them in application code. This works during development with 50 records. It crashes in production with 500,000 records. Always push filtering, sorting, and aggregation to the database layer where possible.
Missing Pagination
API endpoints that return lists without pagination are a ticking time bomb. The AI will happily generate an endpoint that returns every product in your database in a single response. Add limit/offset or cursor-based pagination from the start.
Synchronous Bottlenecks
Watch for code that makes multiple independent API calls sequentially when they could be parallel. If you need data from three separate services, use Promise.all() instead of three sequential await calls. The difference can be 3x faster response times.
Building the Review Habit Without Slowing Down
The whole point of AI coding tools is speed. If your review process takes longer than writing the code yourself, you have lost the benefit. The goal is a review practice that is fast, reliable, and becomes second nature.
Scale your review to the risk. Not every piece of AI-generated code needs the same level of scrutiny. A utility function that formats a date? A quick skim and a test. An authentication middleware that guards your API? Line-by-line review with security focus. A database migration that modifies production data? Multiple reviewers plus a dry run. Match the review intensity to the consequence of a bug.
Use the AI to help review its own code. After generating code, ask the AI: "What are the potential bugs in this code? What edge cases am I missing? Are there any security concerns?" The AI is surprisingly good at finding issues in code when you ask it to look critically. This is not a substitute for your own review, but it catches things you might miss and surfaces questions worth investigating.
Write a test before you trust it. The fastest way to verify that AI-generated code works is to write a test for it. Write the test yourself (or ask the AI to generate tests, then review those). If the code passes your tests, including edge case tests, you can be much more confident in it. If writing the test feels harder than writing the code, that is a sign you do not fully understand what the code does, which means you should read it more carefully.
Keep a "gotcha" list. Every time you catch a bug in AI-generated code, write it down. After a few weeks, you will have a personalized list of the mistakes your AI tool makes most often. This makes future reviews faster because you know exactly where to look. Our cohort members at McTaba Labs keep shared "gotcha" docs, and the pattern recognition speeds up noticeably within the first month.
The real skill is not accepting or rejecting AI code. It is developing the judgment to know, quickly, how much trust a given piece of output deserves. That judgment comes from practice, and every review you do builds it.
Key Takeaways
- ✓AI coding tools produce code that looks right but may contain subtle logic errors, hallucinated APIs, or security vulnerabilities.
- ✓Always run AI-generated code before trusting it. If it does not have tests, write them yourself.
- ✓The most dangerous AI mistakes are the ones that look correct at first glance: outdated library methods, subtly wrong business logic, and missing edge cases.
- ✓A structured review checklist catches more bugs than a casual skim. Use one every time.
- ✓Reviewing AI code well is itself a high-value skill. It is faster than writing from scratch and more reliable than blind acceptance.
Frequently Asked Questions
- How often does AI-generated code contain bugs?
- It depends on the complexity of the task and the model used. For simple, well-defined tasks (sorting a list, formatting a string), AI code is correct most of the time. For complex business logic, API integrations, or security-sensitive code, bugs are common. The key issue is not frequency but severity: AI bugs tend to be the subtle kind that pass a quick glance but break in production.
- Should I review AI-generated code differently from human-written code?
- Yes, because the failure modes are different. Human developers rarely invent APIs that do not exist, but AI does this regularly. Humans are more likely to catch edge cases from experience, while AI consistently misses them. Focus your AI code reviews on: verifying that dependencies and methods actually exist, checking edge cases explicitly, and looking for security patterns the AI tends to skip.
- What is the best way to catch hallucinated APIs?
- Run the code. If it does not compile or throws an import error, that is your first signal. For more subtle hallucinations (a method that exists but with a different signature), check the official documentation for every external API call or library method you are not already familiar with. TypeScript helps here because the compiler will catch incorrect method signatures if you have type definitions installed.
- Is it faster to review AI code or just write it myself?
- For most tasks, reviewing AI code is significantly faster than writing from scratch. The AI handles the boilerplate, structure, and initial logic. Your job is to verify correctness and handle the cases it missed. The exception is highly domain-specific code where explaining the problem to the AI takes longer than just writing the solution. With practice, you develop a sense for which tasks to delegate and which to write yourself.
- How do I get better at reviewing AI-generated code?
- Practice deliberately. After each review, note what you caught and what you missed. Keep a list of common AI mistakes specific to the tools and languages you use. Pair with other developers who review AI code differently than you do. And build the habit of always running the code and writing at least one test before accepting it. The skill improves quickly with consistent practice.
Ready to build real-world apps?
Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.
Apply to the McTaba Marathon