AI refactors are fast and confidently wrong in small ways. The fix isn't to stop using them. It's to make "did the behaviour change?" something you can see, then read the diff like a reviewer.
Step 1: pin the behaviour first
Here's some old code that sums paid orders by region:
function summarize(orders) {
var result = {};
for (var i = 0; i < orders.length; i++) {
var o = orders[i];
if (o.paid == true) {
if (result[o.region] == undefined) {
result[o.region] = 0;
}
result[o.region] = result[o.region] + o.total;
}
}
return result;
}
Before anyone touches it, write down what it does. Not a test suite, just a few assertions under it:
const orders = [
{ region: "EU", total: 120, paid: true },
{ region: "US", total: 80, paid: false },
{ region: "EU", total: 45.5, paid: true },
{ region: "APAC", total: 210, paid: 1 },
];
const out = summarize(orders);
console.assert(out.EU === 165.5, "EU total");
console.assert(out.US === undefined, "unpaid orders are skipped");
console.assert(out.APAC === 210, "paid: 1 counts as paid");
out;
Note the last order: paid: 1. Real data is messy, and 1 == true is true, so the old code counts it. That's the behaviour we've pinned.
Step 2: ask for the refactor
The prompt matters less than the guardrail. Something like: "Refactor summarize to modern JavaScript. Don't change behaviour."
A typical answer looks like this (your model's will differ, which is the point of reviewing it):
function summarize(orders) {
const result = {};
for (const { region, total, paid } of orders) {
if (paid !== true) continue;
result[region] = (result[region] ?? 0) + total;
}
return result;
}
Cleaner. Also wrong. paid !== true is strict, so paid: 1 is now skipped and out.APAC is undefined. The third assertion fails.
Step 3: read the diff, not the answer
That bug hides in a block of nice-looking code. It doesn't hide in a diff: the line if (o.paid == true) turning into if (paid !== true) continue is exactly where your eye should stop. == to === (or !==) is never "just style" on data you don't control.
The fix is a one-word review comment: keep the loose check, or normalise the data first and say so.
Doing this with a local model
I ran this in JSLab, a JavaScript & TypeScript scratchpad for macOS. Its Ask AI panel works with your own key, or Ollama locally.
- Install and start Ollama (a separate, free program), then choose Ollama (Local) under Provider in Settings → AI. No key needed. With Ollama at its standard local address, nothing leaves your Mac.
- Open AI Chat with ⌃⌘I. It already sees the tab's code and the last run's output, so there's nothing to paste.
- On the code block in the reply, click Preview Changes. The removed and added lines show as an inline diff in the editor, and your code isn't touched until you accept something.
- Apply accepts one change, Apply all accepts every change, Discard leaves your code as it was.
Here's a smaller example in a real JSLab tab, with Ollama as the provider: tabWidth throws on a config with no editor section, and the fix comes back as an inline diff, with settings.editor.tabWidth out, settings?.editor?.tabWidth ?? 4 in, and Apply and Discard under it.
When you apply, the output reruns, and the assertions from step 1 tell you right away. A failed console.assert counts as an error, so it shows up under the Errors chip with its line.
The takeaway
- Pin behaviour with a few assertions before the refactor, including the weird inputs.
- Review AI changes as a diff, not as a finished file.
- Stop on every
==/===,||/??and truthiness change. That's where "same behaviour" quietly isn't.
JSLab launches Nov 15. Ask AI and the provider setup are documented here: https://js-lab.app/docs/ask-ai

Top comments (0)