Building Apps with AILesson 23 of 87

Contents

Building Apps with AI

Lesson 23 of 87. Sign in to keep your place.

Module 3 · Reading and checking what came back

Enough JavaScript to review a change

9 min read

Twenty constructs cover nearly everything

You can read most generated JavaScript with a small vocabulary. Here is essentially all of it.

JavaScript
const total = 500          // a value that will not be reassigned
let count = 0              // one that will

function slotIsFree(a, b) { return a.end <= b.start }
const slotIsFree = (a, b) => a.end <= b.start   // the same thing

const booking = { id: 71, name: 'Anita' }   // an object: named fields
const bookings = [b1, b2, b3]               // an array: an ordered list

bookings.map(b => b.name)          // transform each, same length
bookings.filter(b => b.status === 'confirmed')   // keep some
bookings.find(b => b.id === 71)    // first match, or undefined

if (!booking) return                // guard clause: leave early
booking?.customer?.name             // safe if either is missing
name ?? 'Guest'                     // fallback if null or undefined

=== 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.

Async is where the real bugs live

Anything touching a network or a database takes time, and JavaScript does not wait unless told.

JavaScript
const rows = await db.select().from(bookings)   // waits
const rows = db.select().from(bookings)         // does not

The 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.

The common shapes to recognise:

JavaScript
async function getBookings() {        // any function using await
  const rows = await db.select()      // must be marked async
  return rows
}

await Promise.all([a(), b(), c()])    // run three, wait for all

try {
  await save(booking)
} catch (e) {
  // handle it, or the request dies silently
}

A 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.

Where the code runs

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.

JavaScript
'use client'   // this file runs in the browser

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.

So 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.

Six smells worth stopping on

catch (e) {}: a failure discarded. The user sees success.

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.

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.

// TODO or // In a real app you would…: the model marking work it did not do. Search for both after any large generated change.

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.

Mutation of something shared. bookings.push(x) inside a function that was supposed to only read is how state changes in places nobody expects.

The four questions again, applied

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.

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 insert, update, delete, fetch, writeFile, and any await at all: those are the lines with consequences.

Learn by tracing, not by tutorials

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.

Every lesson here is open. Your progress is recorded once you pass the Building with AI exam.

Check your understanding

A query works locally but returns undefined in production. Which explanation fits the pattern best?

Pick the one you would defend. Your answer stays private.

Next lesson · 9 minReading the query behind the page

Stuck on something?Ask Beatriz, your AI tutor

How to 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).