AI Writes Half Our Code Now. Nobody Taught Us How to Review It. A developer, citing the State of AI 2026 survey of 7,258 developers, reports that AI now generates an average of 54% of respondents' code, up from 28% a year earlier, yet most engineers were trained to write code rather than review it. The writeup catalogs five recurring JavaScript and React bugs in AI-generated code—async callbacks inside forEach, missing await inside try/catch, comparator-less sort, and stale closures in useEffect—each verified by running the snippet, and pairs them with review rules to catch them. AI wrote this function. It passed linting. It got approved. It shipped. js 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 State of AI 2026 https://2026.stateofai.dev/ 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. 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. js 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. 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. js 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: js 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. function Timer { const seconds, setSeconds = useState 0 ; useEffect = { const id = setInterval = { setSeconds seconds + 1 ; }, 1000 ; return = clearInterval id ; }, ; return