{"course":{"slug":"building-apps-with-ai","title":"Building Apps with AI","url":"https://addaly.com/learn/building-apps-with-ai","level":"beginner","track":"build"},"lesson":{"slug":"enough-javascript-to-review","title":"Enough JavaScript to review a change","url":"https://addaly.com/learn/building-apps-with-ai/enough-javascript-to-review","position":23,"of":87,"minutes":9,"answer":"A missing await, a swallowed catch, an any, and a use client file that touches secrets account for most reviewable JavaScript defects in generated code.","keyIdea":"A missing await, a swallowed catch, an `any`, and a `use client` file that touches secrets account for most reviewable JavaScript defects in generated code."},"body":{"format":"markdown","text":"## Twenty constructs cover nearly everything\n\nYou can read most generated JavaScript with a small vocabulary. Here is essentially all of it.\n\n```js\nconst total = 500          // a value that will not be reassigned\nlet count = 0              // one that will\n\nfunction slotIsFree(a, b) { return a.end <= b.start }\nconst slotIsFree = (a, b) => a.end <= b.start   // the same thing\n\nconst booking = { id: 71, name: 'Anita' }   // an object: named fields\nconst bookings = [b1, b2, b3]               // an array: an ordered list\n\nbookings.map(b => b.name)          // transform each, same length\nbookings.filter(b => b.status === 'confirmed')   // keep some\nbookings.find(b => b.id === 71)    // first match, or undefined\n\nif (!booking) return                // guard clause: leave early\nbooking?.customer?.name             // safe if either is missing\nname ?? 'Guest'                     // fallback if null or undefined\n```\n\n`===` compares without type coercion and `==` does not. `'1' == 1` is true and `'1' === 1` is false. Always use the triple form, and treat a double one in a diff as something to look at.\n\n## Async is where the real bugs live\n\nAnything touching a network or a database takes time, and JavaScript does not wait unless told.\n\n```js\nconst rows = await db.select().from(bookings)   // waits\nconst rows = db.select().from(bookings)         // does not\n```\n\nThe second gives you a Promise: an object representing work in progress. Print it and you get `Promise { <pending> }`. Treat it as an array and you get nothing, or `undefined` where a value should be, usually with no error at all.\n\nThe common shapes to recognise:\n\n```js\nasync function getBookings() {        // any function using await\n  const rows = await db.select()      // must be marked async\n  return rows\n}\n\nawait Promise.all([a(), b(), c()])    // run three, wait for all\n\ntry {\n  await save(booking)\n} catch (e) {\n  // handle it, or the request dies silently\n}\n```\n\nA missing `await` produces a specific and confusing symptom: it works on your fast local database and fails on the slower production one, because the timing changed.\n\n## Where the code runs\n\nIn a modern framework the same-looking file may run on the server, in the browser, or both. This is the distinction that decides whether a secret is exposed.\n\n```js\n'use client'   // this file runs in the browser\n```\n\nA file with that line at the top is shipped to the user's device. Anything it imports is shipped too. An API key referenced anywhere in that import chain is public.\n\nSo when reviewing, ask of any file touching secrets or the database: does this run on the server? If it says `use client`, or it lives in a component that one imports, the answer is no.\n\n## Six smells worth stopping on\n\n**`catch (e) {}`**: a failure discarded. The user sees success.\n\n**`console.log` left in production code.** Harmless until it prints a user's data into your host's logs, which is a real privacy problem and a very common one.\n\n**`any` in TypeScript.** It disables checking for that value. A model that could not work out a type will reach for it, and every guarantee downstream is now unenforced.\n\n**`// TODO` or `// In a real app you would…`**: the model marking work it did not do. Search for both after any large generated change.\n\n**`setTimeout` used to wait for something.** A one-second delay hoping data has arrived is not synchronisation; it fails on a slow connection and looks fine on yours.\n\n**Mutation of something shared.** `bookings.push(x)` inside a function that was supposed to only read is how state changes in places nobody expects.\n\n## The four questions again, applied\n\nFor any function in a diff: what goes in, what comes out, what does it change that outlives the request, and what happens when it fails.\n\nThat third question is the one JavaScript makes easy to miss, because a database write and a variable assignment look almost identical on the page. Look for `insert`, `update`, `delete`, `fetch`, `writeFile`, and any `await` at all: those are the lines with consequences.\n\n## Learn by tracing, not by tutorials\n\nThe fastest way to reach the level this lesson describes is to take one feature in your own app and read every line of it, asking the model to explain anything you cannot follow. Twenty minutes on real code you care about beats a week of exercises about shopping carts, because you already know what the code is supposed to do, and that is what makes reading it possible."},"check":{"question":"A query works locally but returns undefined in production. Which explanation fits the pattern best?","options":[{"text":"The production database has different data, so the query matches nothing.","correct":false,"tempting":"Would return an empty array rather than undefined, and would not depend on machine speed."},{"text":"The environment variable holding the connection string is missing in production.","correct":false,"tempting":"Plausible and would normally throw a connection error rather than silently yielding undefined."},{"text":"An `await` is missing, so the code reads a pending Promise; the slower production database makes the timing gap visible.","correct":true},{"text":"The production build strips TypeScript types, so the value loses its shape.","correct":false,"tempting":"Misunderstands compilation; types are erased at build time in every environment, including the one where it worked."}]},"prev":{"slug":"enough-html-and-css-to-audit","title":"Enough HTML and CSS to audit the screen","url":"https://addaly.com/learn/building-apps-with-ai/enough-html-and-css-to-audit"},"next":{"slug":"reading-the-query-behind-the-page","title":"Reading the query behind the page","url":"https://addaly.com/learn/building-apps-with-ai/reading-the-query-behind-the-page"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'Enough JavaScript to review a change', in Building Apps with AI. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/building-apps-with-ai/enough-javascript-to-review (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Enough JavaScript to review a change","courseTitle":"Building Apps with AI","module":{"index":3,"slug":"reading-and-checking","title":"Reading and checking what came back"},"answer":null,"keyIdea":"A missing await, a swallowed catch, an `any`, and a `use client` file that touches secrets account for most reviewable JavaScript defects in generated code.","hero":{"kind":"plate","image":null,"alt":null,"caption":null},"blocks":[{"t":"h2","id":"twenty-constructs-cover-nearly-everything","runs":[{"text":"Twenty constructs cover nearly everything"}]},{"t":"p","runs":[{"text":"You can read most generated JavaScript with a small vocabulary. Here is essentially all of it."}]},{"t":"code","lang":"js","label":"JavaScript","text":"const total = 500          // a value that will not be reassigned\nlet count = 0              // one that will\n\nfunction slotIsFree(a, b) { return a.end <= b.start }\nconst slotIsFree = (a, b) => a.end <= b.start   // the same thing\n\nconst booking = { id: 71, name: 'Anita' }   // an object: named fields\nconst bookings = [b1, b2, b3]               // an array: an ordered list\n\nbookings.map(b => b.name)          // transform each, same length\nbookings.filter(b => b.status === 'confirmed')   // keep some\nbookings.find(b => b.id === 71)    // first match, or undefined\n\nif (!booking) return                // guard clause: leave early\nbooking?.customer?.name             // safe if either is missing\nname ?? 'Guest'                     // fallback if null or undefined"},{"t":"p","runs":[{"text":"===","code":true},{"text":" compares without type coercion and "},{"text":"==","code":true},{"text":" does not. "},{"text":"'1' == 1","code":true},{"text":" is true and "},{"text":"'1' === 1","code":true},{"text":" is false. Always use the triple form, and treat a double one in a diff as something to look at."}]},{"t":"h2","id":"async-is-where-the-real-bugs-live","runs":[{"text":"Async is where the real bugs live"}]},{"t":"p","runs":[{"text":"Anything touching a network or a database takes time, and JavaScript does not wait unless told."}]},{"t":"code","lang":"js","label":"JavaScript","text":"const rows = await db.select().from(bookings)   // waits\nconst rows = db.select().from(bookings)         // does not"},{"t":"p","runs":[{"text":"The second gives you a Promise: an object representing work in progress. Print it and you get "},{"text":"Promise { <pending> }","code":true},{"text":". Treat it as an array and you get nothing, or "},{"text":"undefined","code":true},{"text":" where a value should be, usually with no error at all."}]},{"t":"p","runs":[{"text":"The common shapes to recognise:"}]},{"t":"code","lang":"js","label":"JavaScript","text":"async function getBookings() {        // any function using await\n  const rows = await db.select()      // must be marked async\n  return rows\n}\n\nawait Promise.all([a(), b(), c()])    // run three, wait for all\n\ntry {\n  await save(booking)\n} catch (e) {\n  // handle it, or the request dies silently\n}"},{"t":"p","runs":[{"text":"A missing "},{"text":"await","code":true},{"text":" produces a specific and confusing symptom: it works on your fast local database and fails on the slower production one, because the timing changed."}]},{"t":"h2","id":"where-the-code-runs","runs":[{"text":"Where the code runs"}]},{"t":"p","runs":[{"text":"In a modern framework the same-looking file may run on the server, in the browser, or both. This is the distinction that decides whether a secret is exposed."}]},{"t":"code","lang":"js","label":"JavaScript","text":"'use client'   // this file runs in the browser"},{"t":"p","runs":[{"text":"A file with that line at the top is shipped to the user’s device. Anything it imports is shipped too. An API key referenced anywhere in that import chain is public."}]},{"t":"p","runs":[{"text":"So when reviewing, ask of any file touching secrets or the database: does this run on the server? If it says "},{"text":"use client","code":true},{"text":", or it lives in a component that one imports, the answer is no."}]},{"t":"h2","id":"six-smells-worth-stopping-on","runs":[{"text":"Six smells worth stopping on"}]},{"t":"p","runs":[{"text":"catch (e) {}","b":true,"code":true},{"text":": a failure discarded. The user sees success."}]},{"t":"p","runs":[{"text":"console.log","b":true,"code":true},{"text":" left in production code.","b":true},{"text":" Harmless until it prints a user’s data into your host’s logs, which is a real privacy problem and a very common one."}]},{"t":"p","runs":[{"text":"any","b":true,"code":true},{"text":" in TypeScript.","b":true},{"text":" It disables checking for that value. A model that could not work out a type will reach for it, and every guarantee downstream is now unenforced."}]},{"t":"p","runs":[{"text":"// TODO","b":true,"code":true},{"text":" or ","b":true},{"text":"// In a real app you would…","b":true,"code":true},{"text":": the model marking work it did not do. Search for both after any large generated change."}]},{"t":"p","runs":[{"text":"setTimeout","b":true,"code":true},{"text":" used to wait for something.","b":true},{"text":" A one-second delay hoping data has arrived is not synchronisation; it fails on a slow connection and looks fine on yours."}]},{"t":"p","runs":[{"text":"Mutation of something shared.","b":true},{"text":" "},{"text":"bookings.push(x)","code":true},{"text":" inside a function that was supposed to only read is how state changes in places nobody expects."}]},{"t":"h2","id":"the-four-questions-again-applied","runs":[{"text":"The four questions again, applied"}]},{"t":"p","runs":[{"text":"For any function in a diff: what goes in, what comes out, what does it change that outlives the request, and what happens when it fails."}]},{"t":"p","runs":[{"text":"That third question is the one JavaScript makes easy to miss, because a database write and a variable assignment look almost identical on the page. Look for "},{"text":"insert","code":true},{"text":", "},{"text":"update","code":true},{"text":", "},{"text":"delete","code":true},{"text":", "},{"text":"fetch","code":true},{"text":", "},{"text":"writeFile","code":true},{"text":", and any "},{"text":"await","code":true},{"text":" at all: those are the lines with consequences."}]},{"t":"h2","id":"learn-by-tracing-not-by-tutorials","runs":[{"text":"Learn by tracing, not by tutorials"}]},{"t":"p","runs":[{"text":"The fastest way to reach the level this lesson describes is to take one feature in your own app and read every line of it, asking the model to explain anything you cannot follow. Twenty minutes on real code you care about beats a week of exercises about shopping carts, because you already know what the code is supposed to do, and that is what makes reading it possible."}]}],"check":{"question":[{"text":"A query works locally but returns undefined in production. Which explanation fits the pattern best?"}],"options":[{"text":[{"text":"The production database has different data, so the query matches nothing."}],"correct":false,"tempting":[{"text":"Would return an empty array rather than undefined, and would not depend on machine speed."}]},{"text":[{"text":"The environment variable holding the connection string is missing in production."}],"correct":false,"tempting":[{"text":"Plausible and would normally throw a connection error rather than silently yielding undefined."}]},{"text":[{"text":"An "},{"text":"await","code":true},{"text":" is missing, so the code reads a pending Promise; the slower production database makes the timing gap visible."}],"correct":true,"tempting":null},{"text":[{"text":"The production build strips TypeScript types, so the value loses its shape."}],"correct":false,"tempting":[{"text":"Misunderstands compilation; types are erased at build time in every environment, including the one where it worked."}]}],"order":[1,0,2,3],"why":null},"prev":{"slug":"enough-html-and-css-to-audit","title":"Enough HTML and CSS to audit the screen","minutes":8},"next":{"slug":"reading-the-query-behind-the-page","title":"Reading the query behind the page","minutes":9}}}