跳到正文
DEV Community· JavaScript Mastery·· 3 小時前AI 評分67

AI 如今撰寫逾半程式碼,開發者卻未學會如何審查

AI Writes Half Our Code Now. Nobody Taught Us How to Review It.

AI 導讀

文章指出,受訪開發者估計 AI 生成程式碼平均佔其程式碼 54%,但審查訓練並未隨之跟上。State of AI 2026 開發者調查涵蓋 7,258 人,受訪者的平均回答由一年前的 28% 升至 54%。文章列出 JavaScript 和 React 中 5 類常見錯誤,並提供涵蓋 Promise、錯誤處理、狀態更新、閉包及空值的審查清單。

正文

AI wrote this function. It passed linting. It got approved. It shipped.

async function getUsers(ids) {
  const users = [];

  ids.forEach(async (id) => {
    const res = await fetch(`/api/users/${id}`);
    users.push(await res.json());
  });

  return users;
}

It returned an empty array. Every. Single. Time.

No error. No warning. Just [].

If you spotted it in 5 seconds, nice. If you didn't, you're not alone, and that's exactly the problem we want to talk about.

The numbers changed. Our habits didn't.

The State of AI 2026 developer survey collected answers from 7,258 developers, including how much of their code is AI-generated. The average answer was 54%. A year earlier, it was 28%.

(Fair caveat: an AI survey probably attracts people who use AI a lot. But even if the real number is lower, the direction is obvious.)

In one year, AI went from "helps with boilerplate" to "writes more than half the code."

But think about how most of us learned to code. We learned to write it. Tutorials, courses, bootcamps, LeetCode: almost all of it trains the writing muscle.

Almost nobody trained us to review code we didn't write, at speed, all day. And that's now the main job.

AI-generated code has a specific personality. It looks clean, it's confidently formatted, it has nice variable names, and it's wrong in subtle ways that don't throw errors.

Here are 5 bugs we see AI write over and over in JavaScript and React. Every snippet below has been run, and the output is real.


Bug #1: forEach doesn't wait for async

That's our opening example. forEach calls your async callback, gets back a promise, and ignores it. It doesn't wait. So return users runs before a single fetch finishes.

What you get: []

The fix: use map to collect the promises, then Promise.all to wait for them.

async function getUsers(ids) {
  return Promise.all(
    ids.map(async (id) => {
      const res = await fetch(`/api/users/${id}`);
      return res.json();
    })
  );
}

🔍 Review rule: If you see async inside forEach, stop. It's almost always a bug.


Bug #2: The try/catch that catches nothing

async function saveUser(user) {
  try {
    db.insert(user);
    return { ok: true };
  } catch (err) {
    console.error("Save failed:", err);
    return { ok: false };
  }
}

Looks safe, right? There's error handling and everything.

But db.insert() returns a promise, and there's no await. So the function returns { ok: true } immediately. When the insert fails later, the error is outside the try block. Your catch never runs.

What you get: { ok: true }, even when the save failed. The real error shows up as an unhandled promise rejection, which in modern Node.js crashes the process by default.

The fix: one word.

await db.insert(user);

🔍 Review rule: Any call that returns a promise inside a try block needs an await, or the catch is just decoration.


Bug #3: Sorting numbers like they're words

const prices = [100, 25, 9, 1200, 50];
prices.sort();

What you get: [100, 1200, 25, 50, 9]

By default, .sort() converts everything to strings and sorts alphabetically. "1200" comes before "25" because "1" comes before "2".

AI loves this one because it reads perfectly in plain English. "Sort the prices." Done. Except it isn't.

The fix:

prices.sort((a, b) => a - b);
// [9, 25, 50, 100, 1200]

🔍 Review rule: .sort() with no comparator on anything that isn't a plain string array is a red flag.


Bug #4: The timer that's stuck on 1

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      setSeconds(seconds + 1);
    }, 1000);
    return () => clearInterval(id);
  }, []);

  return <p>{seconds}</p>;
}

It even cleans up the interval. Very responsible. Very wrong.

The empty dependency array means the effect runs once, so the interval callback captures seconds from the first render, when it was 0. Every tick sets it to 0 + 1.

What you get: 1. Forever.

The fix: use the functional updater so you always get the latest value.

setSeconds((s) => s + 1);

🔍 Review rule: A state variable used inside setInterval, setTimeout, or an event listener in a useEffect with [] deps? Check for a stale closure.


Bug #5: The list that never updates

function TodoList() {
  const [todos, setTodos] = useState([]);

  function addTodo(text) {
    todos.push(text);
    setTodos(todos);
  }

  // ...
}

todos.push() mutates the existing array. Then setTodos(todos) passes React the same array reference. React compares the old and new state, sees they're the same object, and skips the re-render.

What you get: You click "Add". Nothing appears on screen.

The fix: create a new array.

setTodos([...todos, text]);

🔍 Review rule: .push(), .splice(), or direct property assignment on something that came from useState means the UI won't update.


The 30-second review checklist

Save this. Run through it every time AI hands you code.

  • [ ] Async: Is every promise actually awaited? Any async inside forEach?
  • [ ] Errors: Will the catch actually catch what you think it catches?
  • [ ] Defaults: Is it relying on a default behavior (like .sort(), ==, or parseInt) that might not do what the name suggests?
  • [ ] State: Is anything from useState being mutated directly?
  • [ ] Closures: Is a callback reading a value that might be stale?
  • [ ] The empty case: What happens with [], null, "", or 0?
  • [ ] Can you explain it? If you can't explain every line to a teammate, you're not done reviewing.

That last one matters most.


The real skill in 2026 isn't prompting

Here's our honest take.

Prompting is getting easier every month. The tools are getting better at understanding what you mean. That's great, but it also means prompting is becoming less of a differentiator.

What's getting more valuable is the thing AI is still bad at: knowing when the confident, clean, well-formatted code in front of you is wrong.

Every one of the bugs above:

  • passes a linter
  • throws no error on the line where the bug is
  • looks like something a senior dev would write

You can only catch them if you actually understand how JavaScript and React work under the hood. Event loop. Closures. References. Promises.

The fundamentals didn't become less important when AI showed up. They became the whole job.


Your turn 👇

What's the sneakiest bug AI has ever shipped into your codebase?

Drop it in the comments. Bonus points for a snippet. Let's build the ultimate list of "AI code that looked fine."


If you want to go deeper on building with AI agents without losing your engineering skills, we cover exactly this in our Agentic Engineering Course.

來源:DEV Community · dev.to