PLAINFINCH learn by building

Reading AI-generated code

You will read code you did not write by following one action through it and spotting common problems.

How do you start reading code you did not write?

Reading a whole file from the top is the slowest way to understand it. The top of a script is usually set-up: constants, element lookups, helper functions. None of it makes sense until you know what it is for. Start from the outside instead. Use the page first. Click every button, type in every field, and write one sentence describing what the code is supposed to do: "Typing in the box filters the menu and says how many items matched."

Next, find the entry points. In browser code those are the places where something starts: addEventListener calls, code that runs straight away at the bottom of the file, fetch calls, and timers. Search the file for addEventListener and you have a list of everything the page reacts to. Each one names an element and an event, which links the code back to what you clicked.

Then trace one action, start to finish. Pick the search box. The input listener calls render with the trimmed value. render clears the list, filters items, adds one li per match, and writes a message. Now you understand a quarter of the file in detail, and the rest reads much faster because you know the names.

  • Use the page and describe it in one sentence.
  • List the entry points: listeners, code that runs on load, fetches, timers.
  • Trace one action from event to screen.
  • Note what data it reads and changes.
  • Only then read the helpers you have not met yet.
FigureA reading map for unfamiliar code: start from what the page does, find the entry point, trace one action through the listener and function, see what data changes, and see what the page shows. Red flags are checked along the way.A reading map for unfamiliar code: start from what the page does, find the entry point, trace one action through the listener and function, see what data changes, and see what the page shows. Red flags are checked along the way.
A reading map for code you did not write. Go left to right through one real action, and check for red flags as you pass each part instead of reading the file top to bottom.

Which PlainFinch lenses help with unfamiliar code?

The Events lens answers "what runs when I do this?" Open it, type one letter into the search box, and it shows the input event and the function that handled it. That is the entry point found for you, without reading anything.

The Step lens runs the code line by line with every variable's value shown. Step through render once and you see term, matches and the loop in action. It is the quickest way to check your reading: if you predicted matches would hold two items and it holds one, you have learned something about the code, or found a bug.

The Changes lens outlines every element the script added, removed or changed. Type <b>rye</b> into the demo and it shows a new b element inside the message. That is the injection problem made visible. Use the three lenses together: Events to find where to start, Step to follow the logic, Changes to see the effect.

What are the red flags in AI-generated JavaScript?

AI tools write plausible code quickly, and plausible is the problem: it looks right at a glance. These are the faults that show up most often. None of them means the whole file is bad; each one is a place to stop and check.

  • Invented functions and APIs: methods that sound real but do not exist, such as array.sortBy(), element.setText() or a made-up option on fetch. Check anything unfamiliar in MDN or by running it in the Console.
  • innerHTML with user input: anything a visitor types, or that comes from a URL or an API, should go in with textContent.
  • Old patterns: var, onclick="" attributes in HTML, document.write, and keyCode. They usually come from old examples in the training data.
  • Huge functions that do five jobs, and the same block copied three times with small changes.
  • Swallowed errors: catch (e) {} with nothing inside, which turns a crash into silence.
  • Secrets in the front end: API keys, tokens and passwords in the script are public the moment the page loads.
  • Magic numbers: if (total > 37.5) with no name or comment. Ask what the number means.
  • Missing states: no loading message while data arrives, no error message when it fails, nothing shown for an empty result.

Why is innerHTML with user input so dangerous?

innerHTML reads its string as HTML and builds real elements from it. With text you wrote yourself, that is fine. With text a visitor typed, they decide what elements appear. In the demo, typing <b>rye</b> makes the message bold. Typing <img src=x onerror="..."> would run their JavaScript inside your page. This is cross-site scripting, and if the text is ever saved and shown to other visitors, one person's input runs in everyone's browser.

The fix is almost always textContent, which treats the string as plain characters. If you need some structure, build it with createElement and set each piece's textContent. When an AI writes innerHTML with a template literal that contains a variable, ask where that variable came from. If the answer includes "the user", "the URL" or "an API", change it.

// Builds elements from the visitor's text
message.innerHTML = `Results for <strong>${term}</strong>`;

// Shows the text as text
const strong = document.createElement("strong");
strong.textContent = term;
message.replaceChildren("Results for ", strong);

What is wrong with an empty catch block?

try and catch exist so your code can respond to a failure: show a message, try again, or fall back to a default. An empty catch responds by doing nothing, which hides the error from you as well as from the visitor. The page just stops working, the Console stays quiet, and you start debugging with no clue at all.

AI tools add empty catches to make code look safe, and sometimes to hide a bug in their own code. In the Break It exercise, an invented sortBy method throws a TypeError and the empty catch keeps it secret. At a minimum, log the error with console.error(error). Better, tell the visitor something useful and keep the page in a sensible state.

try {
  const saved = JSON.parse(localStorage.getItem("basket"));
  showBasket(saved ?? []);
} catch (error) {
  console.error("Could not read the saved basket", error);
  showBasket([]);
}

Why must keys and secrets stay out of front-end code?

Every byte of a page's JavaScript is downloaded to the visitor's computer. Anyone can open DevTools, look in the Sources or Network panel, and read it. Minifying or hiding it in a variable called config changes nothing. A payment key, a database password or a private AI API key in the script is effectively published.

When an AI writes code that calls a paid service straight from the browser with a key, the correct shape is different: the browser calls your own server, and the server holds the key and calls the service. Some keys are designed to be public, such as a maps key locked to your domain; the service's docs say so. If a secret has already been committed, deleting the line is not enough. Rotate the key in the service's dashboard, because old copies live on in git history.

How do you ask an AI good follow-up questions?

Treat the AI like a fast colleague whose work you are reviewing. Ask it to explain before you ask it to change. Good questions are specific and point at a line: "Where does term come from, and can a visitor control it?" "What happens on line 14 if the fetch fails?" "Is sortBy a real array method? Link the documentation." "What does 37.5 mean here?"

Ask for the smallest change that fixes one problem, and say what must stay the same. "Rewrite this" invites a new file full of new mistakes. When it answers, check claims you can check: run the method in the Console, read the documentation it names, or test with the input you were worried about. An AI will sometimes confirm whatever you suggested, so ask neutral questions, such as "is there a problem here?" in place of "this is fine, right?"

How do you test small changes safely?

Change one thing, run the page, and compare with what happened before. If something breaks you know exactly which change caused it. Before a change, write down the input you will test with and what you expect to see. For the demo: type rye, expect one result and a message saying 1 result; type <b>rye</b>, expect the characters to appear as typed.

Try the awkward inputs AI code rarely handles: an empty field, spaces only, a very long string, the button pressed twice quickly, the network failing. Use the Console to call functions directly, for example render("bun"), which tests one function without clicking through the page. When the change works, read the whole diff once more before you keep it.

Field notes

  • Start from something the page does and trace that one action.
  • The Events lens shows which function runs when you interact.
  • AI code often looks right at a glance while hiding common faults.
  • Putting visitor text into innerHTML lets them inject markup or scripts.

Easy to get wrong

Visitor text in innerHTML becomes real HTML, including scripts in event attributes. textContent keeps it as text.

An empty catch hides bugs from you and failures from visitors. Log the error and show a state the visitor can understand.

A named constant explains the number and gives you one place to change it.

Front-end code is public. Any key in it can be copied and used by anyone.

Questions people ask

Should I rewrite AI code from scratch if I find problems?

Usually not. Fix the specific problems one at a time and test after each. A full rewrite brings a fresh set of unknowns.

How can I tell whether a method really exists?

Type it into the Console on a real value, for example [1, 2].toSorted, and see whether it returns a function or undefined. Or search MDN for the exact name.

Is innerHTML ever fine?

Yes, for fixed strings you wrote yourself, such as a template with no variables. Once a variable from a visitor, the URL or an API goes in, use textContent or build elements.

Why does AI code still use var?

Models learned from years of older code. var still runs, but its scoping rules cause bugs that let and const avoid, so update it when you see it.

What should I check first in a long AI file?

Search for innerHTML, catch, key, token and var. Those five searches find most of the serious problems in a minute.

Keep going