# Reviewing AI-Written Code — A Checklist and 6 Real Bugs That Look Correct

Source: https://skills101.in/learn/ai-coding-assistants/reviewing-ai-generated-code — Skills101 free tutorial (AI Coding Assistants). Updated 2026-11-09.

AI-generated code is usually **well-formatted, well-named and confidently commented** — which makes bugs harder to spot, not easier. Reviewing it is now one of the most valuable skills a developer has. Here's how.

## 1. The review checklist

![Code review checklist for AI output: does it solve the right problem, edge cases, correctness, security, fits the codebase, performance, tests, and do I understand every line](https://skills101.in/learn-img/ai-coding-assistants/review-checklist.svg)

1. **Right problem?** Re-read your requirement. Did it solve that — or an easier version?
2. **Edge cases:** empty, zero, negative, very large, duplicates, missing fields, unicode, time zones, concurrency.
3. **Correctness details:** off-by-one, integer vs float, rounding, `==` vs `is`, mutation of inputs.
4. **Errors:** are exceptions handled sensibly — not swallowed, not crashing the whole request?
5. **Security:** input validation, SQL/command injection, secrets, permissions (Web Security track).
6. **Fits the codebase:** existing helpers reused? naming and patterns consistent? no surprise new dependency?
7. **Performance:** nested loops over big data, N+1 database queries, loading everything into memory.
8. **Tests:** do they exist, and do they test the edge cases — or only the happy path?
9. **Do I understand every line?** If not, ask the AI to explain, or rewrite it until you do.

Now let's train your eye on six bugs that AI tools commonly produce because they're common in the code they learned from.

## 2. Bug 1: the mutable default argument

```python
def add_score(score, scores=[]):      # looks harmless
    scores.append(score)
    return scores

print(add_score(90))   # student A
print(add_score(75))   # student B — should be [75]!
```

```text
[90]
[90, 75]
```

The default list is created **once** and shared between calls, so student B inherits student A's score. Fix: `scores=None` then `scores = [] if scores is None else scores`.

## 3. Bug 2: money in floating point

```python
price = 0.1 + 0.2
print(price == 0.3, price)

from decimal import Decimal
fee = Decimal("0.10") + Decimal("0.20")
print(fee == Decimal("0.30"), fee)

gst = round(Decimal("1999") * Decimal("0.18"), 2)
print("GST on ₹1999:", gst)
```

```text
False 0.30000000000000004
True 0.30
GST on ₹1999: 359.82
```

Binary floats can't represent most decimals exactly. For money, use integer **paise** or `Decimal` — never `float` comparisons.

## 4. Bug 3: off-by-one in pagination

An AI-written page function, "20 items per page, pages start at 1":

```python
items = list(range(1, 46))   # 45 courses

def page_buggy(n, size=20):
    return items[n * size:(n + 1) * size]

def page_fixed(n, size=20):
    return items[(n - 1) * size:n * size]

print("buggy page 1:", page_buggy(1)[:3], "... first item should be 1")
print("fixed page 1:", page_fixed(1)[:3])
print("fixed page 3:", page_fixed(3))
```

```text
buggy page 1: [21, 22, 23] ... first item should be 1
fixed page 1: [1, 2, 3]
fixed page 3: [41, 42, 43, 44, 45]
```

The buggy version silently skips the first 20 items on page 1. No error, no crash — just missing data. These are the bugs users report weeks later.

## 5. Bug 4: timezone-naive dates

A contest that "closes at 9 PM" — but the server runs in UTC:

```python
from datetime import datetime, timezone, timedelta

IST = timezone(timedelta(hours=5, minutes=30))
closes = datetime(2026, 11, 15, 21, 0, tzinfo=IST)         # 9 PM India time

server_now_naive = datetime(2026, 11, 15, 16, 0)            # what datetime.utcnow() might give: 4 PM, no zone
print("naive 4 PM UTC treated as local < 9 PM? ", server_now_naive < closes.replace(tzinfo=None))

real_now = datetime(2026, 11, 15, 16, 0, tzinfo=timezone.utc)   # same instant, with its zone
print("aware: is the contest still open?", real_now < closes)
print("9 PM IST in UTC is", closes.astimezone(timezone.utc).strftime("%H:%M"))
```

```text
naive 4 PM UTC treated as local < 9 PM?  True
aware: is the contest still open? False
9 PM IST in UTC is 15:30
```

4 PM UTC **is** 9:30 PM in India, so the contest should already be **closed**. The naive comparison compared "16:00" with "21:00" as plain numbers and said it's still open — students could submit late. The timezone-aware comparison gets it right. Rule: store and compare times **with time zones** (usually in UTC), and convert to IST only for display.

## 6. Bug 5: the swallowed exception

```python
def get_discount(code):
    try:
        return {"FEST50": 50, "EARLY20": 20}[code.upper()]
    except Exception:
        return 0            # hides EVERY error, including bugs

print(get_discount("fest50"))
print(get_discount(None))   # a real bug (None passed in) silently becomes "no discount"
```

```text
50
0
```

`except Exception: return default` makes bugs invisible. Catch the **specific** exception you expect (`KeyError` here) and let real bugs surface (or log them).

## 7. Bug 6: quadratic performance

```python
import time

registered = [f"user{i}@example.com" for i in range(20000)]
incoming = [f"user{i}@example.com" for i in range(19000, 21000)]

t = time.perf_counter()
dupes_slow = [e for e in incoming if e in registered]          # list search: O(n) each time
slow = time.perf_counter() - t

t = time.perf_counter()
registered_set = set(registered)
dupes_fast = [e for e in incoming if e in registered_set]      # set lookup: O(1) average
fast = time.perf_counter() - t

print(len(dupes_slow), len(dupes_fast), "duplicates found by both")
print("set version faster:", fast < slow)
```

```text
1000 1000 duplicates found by both
set version faster: True
```

Fine on 20 test rows, painfully slow on 2 lakh real rows. Watch for `in list` inside loops and database queries inside loops (the **N+1** problem).

## 8. How to review fast

- **Read the diff, not the chat.** Review what will actually change in your repo (`git diff`).
- **Run it** with the edge cases from section 1 before reading deeply — failures point you to the bug.
- **Ask the AI to review its own code** against your checklist ("list edge cases this doesn't handle") — it often finds real problems, but you decide.
- **Keep changes small** so reviews stay possible.

## Common mistakes

- Trusting code because it's well-commented.
- Reviewing only the happy path.
- Accepting a new dependency without checking it exists, is maintained and is needed.
- Merging AI code you can't explain in an interview.

## Quick recap

- Use the checklist: right problem, edge cases, correctness, errors, security, fit, performance, tests, understanding.
- Classic AI-plausible bugs: **mutable defaults, float money, off-by-one, naive datetimes, swallowed exceptions, O(n²) loops**.
- Run edge cases first; review the diff; keep changes small; you own the result.


Practice quiz and free certificate: https://skills101.in/learn/ai-coding-assistants/reviewing-ai-generated-code