跪拜 Guibai
← Back to the summary

AI Code Review Catches Nulls and XSS Instantly — and Still Can't Tell You a Discount Makes Prices Negative

I Had AI Do a Complete Code Review of a Frontend Project — The Gap Between It and Humans Is Much Larger Than I Thought

Writing code with AI is 10x faster, but what happens after the code is written? Review becomes the new bottleneck. I had Claude Code do a full round of Code Review on a React project, and the result: on some issues it's far sharper than humans, and on others it makes mistakes more ridiculous than an intern's.

The Cause: A Mountain of Reviews Piling Up

After the team started using AI to write code, the number of PRs doubled. Code gets written fast, but the review speed hasn't kept up. Every day I open GitLab, and a dozen MRs awaiting review stare at me — each one hundreds of lines of AI-generated code.

A statistic from GitLab's AI Accountability Report: 85% of surveyed developers believe AI has already shifted the bottleneck from "writing code" to "reviewing code."

Since AI created this problem, can AI solve it itself?

I ran an experiment: I had Claude Code do a complete Code Review on a medium-sized React frontend project (about 200 components, 30,000 lines of code) to see what it could actually find and what it would miss.

5 Types of Problems AI Review Can Catch — Things Humans Really Tend to Miss

Let's start with the conclusion: in certain dimensions, AI Review is indeed better than humans. Not just a little better — a lot better.

1. Unhandled Edge Cases

This is AI's strongest area. It tirelessly checks every variable for the possibility of being null.

// AI flagged issue: data could be undefined
function UserProfile({ userId }) {
  const { data } = useQuery(['user', userId], fetchUser);
  
  return (
    <div>
      <h1>{data.name}</h1>  {/* 💥 Explodes directly when data hasn't loaded */}
      <p>{data.email}</p>
    </div>
  );
}

// AI suggested fix
function UserProfile({ userId }) {
  const { data, isLoading, error } = useQuery(['user', userId], fetchUser);
  
  if (isLoading) return <Skeleton />;
  if (error) return <ErrorFallback error={error} />;
  if (!data) return null;
  
  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.email}</p>
    </div>
  );
}

When humans review, seeing useQuery makes them default to "there must be loading state handling" and they often just glance over it. AI doesn't. It checks line by line whether every property access is safe.

2. Performance Anti-Patterns

AI is particularly sensitive to React re-rendering issues.

// AI flagged issue: creates a new object reference on every render
function Dashboard() {
  const filters = { status: 'active', role: 'admin' }; // New object every render
  
  return <UserList filters={filters} />;  // UserList re-renders every time
}

// AI flagged issue: inline function defined inside map
function TodoList({ todos, onToggle }) {
  return todos.map(todo => (
    <TodoItem 
      key={todo.id}
      todo={todo}
      onToggle={() => onToggle(todo.id)}  // New function every render
    />
  ));
}

These issues don't matter in small projects, but with many components they can make the page unbearably slow. Human reviewers rarely have the patience to check object references and callback function stability one by one; AI can do it.

3. Security Vulnerabilities

This is the part that gave me chills.

// AI flagged XSS risk
function Comment({ content }) {
  return <div dangerouslySetInnerHTML={{ __html: content }} />;
}

// AI flagged issue: URL parameter directly concatenated, injection risk exists
function SearchPage() {
  const query = new URLSearchParams(window.location.search).get('q');
  
  fetch(`/api/search?q=${query}`)  // Not encoded, injectable
    .then(res => res.json())
    .then(setResults);
}

There's a pretty frightening statistic: research shows that in AI-generated code, 61% is functionally correct, but only 10.5% is secure. In other words, the code runs, but it's full of holes.

AI Review is actually quite good at checking the security of code it wrote itself — it knows what mistakes it tends to make.

4. Naming and Consistency Issues

// AI flagged naming inconsistency
const getUserInfo = async (id) => { ... }    // Uses Info
const fetchUserData = async (id) => { ... }  // Uses Data  
const loadUserDetail = async (id) => { ... } // Uses Detail

// AI flagged style inconsistency
const isActive = user.status === 'active';     // Boolean uses is prefix
const hasPermission = checkPermission(user);    // Boolean uses has prefix
const canEdit = user.role === 'admin';          // Boolean uses can prefix
const userLoggedIn = !!token;                   // 💥 This one forgot the prefix

When humans review these issues, they often think "forget it, it runs." AI doesn't let them slide.

5. Duplicate Code and Extractable Common Logic

// AI found this loading + error handling pattern repeated in 14 components
function OrderList() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    setLoading(true);
    fetchOrders()
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false));
  }, []);
  
  if (loading) return <Spinner />;
  if (error) return <ErrorMessage error={error} />;
  // ...
}

// AI suggested extracting a custom Hook
function useAsync(asyncFn, deps = []) {
  const [state, setState] = useState({
    data: null, loading: true, error: null
  });
  
  useEffect(() => {
    setState(prev => ({ ...prev, loading: true }));
    asyncFn()
      .then(data => setState({ data, loading: false, error: null }))
      .catch(error => setState({ data: null, loading: false, error }));
  }, deps);
  
  return state;
}

AI can scan the entire project to find similar code snippets — something human reviewers can hardly do. When reviewing a single PR, you won't go flipping through other files to compare.

5 Fatal Blind Spots of AI Review — Things It Will Never Get Right

After the strengths, let's talk about where AI falls flat. Not minor slip-ups — the kind where following its advice would cause major trouble.

1. Whether Business Logic Is Correct — AI Simply Doesn't Understand Your Product

// AI thought this code was "fine"
function PriceDisplay({ price, discount }) {
  const finalPrice = price - discount;
  return <span>¥{finalPrice.toFixed(2)}</span>;
}

AI couldn't see the problem. But anyone who's worked on e-commerce knows immediately — finalPrice could be negative. When the discount is greater than the original price, the user sees ¥-15.00.

AI doesn't know business-level constraints like "discount cannot exceed original price" or "a negative price should display as 0." It only checks code logic, not business rules.

2. Architectural Decisions — Local Optimum ≠ Global Optimum

AI's suggestions for each file are correct individually, but together they can be a disaster.

AI's suggestions:
✅ "This component should use React.memo for optimization"
✅ "This data should be shared via Context"  
✅ "This list should use virtual scrolling"

Actual situation:
If all three suggestions are implemented simultaneously, Context value changes → all memo components re-render 
→ virtual scrolling state fully resets → user experience is worse than before optimization

AI can't see the dependency relationships and data flow between components. It finds the optimal solution for each part, but they don't add up to an optimal system.

3. User Experience and Interaction Details

// AI thought this code was fine
function DeleteButton({ onDelete }) {
  return <button onClick={onDelete}>Delete</button>;
}

Problems AI can't detect:

These are all problems only someone who has used the product would know. AI hasn't used your product; it only sees the code.

4. Cross-Module Side Effects

// File A: User module
export function updateUserRole(userId, newRole) {
  return api.patch(`/users/${userId}`, { role: newRole });
}

// File B: Permission module (AI can't see this when reviewing File A)
function PermissionGuard({ children, requiredRole }) {
  const { user } = useAuth();  // Cached user data, won't auto-update
  
  if (user.role !== requiredRole) return <Forbidden />;
  return children;
}

User role is changed, but the permission guard uses cached data that won't refresh automatically. After the role change, the user still sees a "Forbidden" page until they refresh.

AI Review looks fine when reviewing a single file. But it won't tell you "changing this here will break that there" — because its context window can't fit the entire system.

5. "Correct but Harmful" Refactoring Suggestions

This is the most insidious trap. AI often suggests writing things in ways that "look better":

// Original code (AI thought it was "not elegant enough")
if (type === 'admin') {
  return <AdminPanel />;
} else if (type === 'editor') {
  return <EditorPanel />;
} else if (type === 'viewer') {
  return <ViewerPanel />;
}

// AI's suggested "optimization"
const PANEL_MAP = {
  admin: AdminPanel,
  editor: EditorPanel,
  viewer: ViewerPanel,
};

const Panel = PANEL_MAP[type];
return Panel ? <Panel /> : null;

Looks more elegant, right? But three months later, a new colleague needs to add a moderator role. They won't go digging through the PANEL_MAP constant — they'll try to add code where the else if originally was, then find they can't because the if-else has been "optimized" away.

Not all refactoring is good. Sometimes "dumb code" is easier to maintain than "clever code."

Practical Quick Reference: Which Reviews to Hand to AI, Which Must Be Manual

Review Dimension AI Capability Human Capability Verdict
Null/Boundary Checks ⭐⭐⭐⭐⭐ ⭐⭐ Hand to AI
Performance Anti-Patterns ⭐⭐⭐⭐ ⭐⭐ Hand to AI
Security Vulnerabilities (XSS/Injection) ⭐⭐⭐⭐ ⭐⭐⭐ AI scans first, human reviews
Naming/Style Consistency ⭐⭐⭐⭐⭐ ⭐⭐ Hand to AI
Duplicate Code Detection ⭐⭐⭐⭐⭐ Hand to AI
Business Logic Correctness ⭐⭐⭐⭐⭐ Must be manual
Architectural Soundness ⭐⭐ ⭐⭐⭐⭐ Must be manual
User Experience Impact ⭐⭐⭐⭐⭐ Must be manual
Cross-Module Side Effects ⭐⭐⭐⭐ Must be manual
Whether Refactoring Is Worth It ⭐⭐ ⭐⭐⭐⭐ Must be manual

One-sentence summary: AI handles "is it correct," humans handle "should we do it this way."

My Current Review Process

After the experiment, I adjusted my review approach:

First pass: AI scan (done in 5 minutes)

Second pass: Manual review (only looking at what AI can't handle)

Before, a PR took half an hour to review. Now, after AI does the first pass, I only need 10 minutes to look at business logic and architectural decisions. Efficiency improved, and quality is actually higher — because AI handles all the mechanical checks that "get missed when attention slips."

But there's one iron rule: code that AI marks as "no problem" doesn't mean there really is no problem. It only means "no problem within what I can see." Whether the business logic is right, whether the architecture makes sense, whether the user experience is good — these will always be human work.


Has your team used AI for Code Review? Have you found any scenarios where AI is particularly good or particularly absurd? Let's chat in the comments.