Prompting in Small Steps
Why one change per prompt works better than a wish list: saying what must stay the same, asking for whole files or clearly marked changes, checking each result in the preview, and keeping the last working version.
Why small steps work
When Andrej Karpathy named vibe coding in February 2025, he described a way of working that has no steps at all: accept every change, paste every error back, and when the model cannot fix a bug, "ask for random changes until it goes away" (his post; see What Is Vibe Coding?). That can be fun for a weekend toy. It stops working as soon as the program is bigger than one screen, because nobody knows any more which change broke what.
Small-step prompting is the alternative: building a program with an AI assistant as a series of small, separately checked changes, each starting from a version that works. It is task decomposition applied to code, and the build pages of this topic follow it: eight steps, each adding one feature to a game that worked a step earlier. The reasons are practical rather than scientific:
- A small change is easy to check. If the prompt asked for one thing, you know what to look for in the preview and in the code.
- A failure points to its cause. If the game worked before and not after, the last change is the suspect. After five changes at once there are five suspects, and they can interact.
- Less to keep in mind, for you and for the model. A short, focused request leaves less room for the model to lose track of details in a long conversation (see context rot).
- A broken step is cheap to undo. You paste back the previous version and ask differently.
One change per prompt
One change per prompt is the rule of asking an assistant for a single, clearly described change to existing code in each request, instead of a list of wishes. "One change" means one thing you can check in the result, not one line of code: "add a ship that follows the mouse" is one change, even if it needs eighty lines.
Make my space game better: add a ship, asteroids, shooting, a score, sound effects, explosions and a high score, and make it look more retro.
Everything in that prompt is reasonable, and an assistant will happily produce all of it. But the answer will be several hundred new lines, and the model chooses every detail you did not specify: how the ship is steered, how fast the rocks fly, what "more retro" means. If the result does not run, you cannot tell which of the seven features is to blame. If it runs but the ship feels wrong, the fix touches code you have never looked at.
Here is my CodePen game so far (three files, Three.js through import * as THREE from "three"). Change one thing: the stars should stream toward me twice as fast as now. Keep everything else exactly as it is: same names, same comments, same structure.
Give me only the lines that change, and say where they go. Then one line: what you changed.
[paste script.js here]
The second prompt gets a short answer you can read in a minute and check in a second: the stars stream faster, and nothing else changed. The build steps on this site are larger than that (each adds a feature), but every one of them asks for one feature, describes it precisely, and says "keep everything that already works". How to describe a feature precisely is covered on Prompting for Code and Clear and Specific Instructions.
Try it yourself
Open a new chat in an AI assistant (Claude, ChatGPT, Copilot, Gemini) and paste the first prompt. Then start another new chat and paste the second. A new chat for each prompt keeps the first answer from influencing the second. The code in prompt 2 is taken from this topic's game; you need no Pen to try it.
Write a complete 3D space shooter in one HTML file using Three.js: a ship that follows the mouse, asteroids, shooting, explosions, sound, a score, a high score and levels that get harder.
Below is part of a small JavaScript game. Change only one thing: no star may be placed closer than 5 units to the flight path (now the minimum is 3). Keep all names, comments and other values exactly as they are.
Give me the complete code back, then one line saying what you changed.
const STAR_FAR = -190;
const STAR_NEAR = 12;
const STAR_MIN_RADIUS = 3; // an empty tube around the flight path
const STAR_MAX_RADIUS = 70;
// One star [x, y, z] somewhere in a long tube around the flight path.
// random() returns a number from 0 to 1.
function randomStar(random) {
const angle = random() * Math.PI * 2;
// Math.sqrt spreads the stars evenly over the ring.
const spread = Math.sqrt(random()) * (STAR_MAX_RADIUS - STAR_MIN_RADIUS);
const radius = STAR_MIN_RADIUS + spread;
const z = STAR_FAR + random() * (STAR_NEAR - STAR_FAR);
return [Math.cos(angle) * radius, Math.sin(angle) * radius, z];
}
What to look for: in the first answer, how many
lines you got, how many decisions the model made for you
(controls, speeds, colors, how many lives), whether it uses
THREE.Clock (deprecated since Three.js r183) or an
old way of loading Three.js, and whether you could check each
feature separately. In the second answer, whether only
STAR_MIN_RADIUS changed from 3 to 5 (that is the
whole correct change), whether anything else was renamed,
reformatted or "improved", and whether the one-line summary
matches what really changed.
Start from a working baseline
A working baseline is the last version of your code that you have checked and know works, kept somewhere you can return to; each next change starts from it. Small steps only pay off if you can step back. When an answer breaks the game, the fastest fix is rarely another prompt: it is pasting the baseline back and asking again, more precisely.
In CodePen this needs a little care. On a free account, as of
October 2026, a Pen's version history is a paid (PRO) feature, so
you cannot roll back to an earlier save. Before each step, keep the
working version yourself: copy script.js into a text
file, or fork the Pen. The options are compared on
Saving Versions and Going Back. The same idea, done properly with tools, is
version control.
Say what must stay the same
Asked to change one thing, assistants regularly change more: they rename variables, tidy up functions that worked, swap a technique for one they prefer, or drop a feature they did not notice. Call this scope drift: an AI's change spreading beyond what was asked, so that one answer contains unrequested edits elsewhere in the code. (It is not the same as scope creep, where a project's requirements grow over time.) Each unrequested edit is a change you did not check and may not even notice.
The defense is to say explicitly what stays. Useful phrases (the first and third appear in this topic's build prompts):
- "Keep everything that already works."
- "Keep the names, comments and structure as they are."
- "Do not use any other library."
- "Do not change the camera" (or whatever you care about).
Assistants also differ in how readily they act and how much they add; why "make this change and nothing else" helps is explained on Explain, suggest or change.
Whole files or marked changes
An assistant can give you its change in two forms, and it is worth saying which one you want.
A full-file request asks the assistant to return the complete file with the change already applied, so you can replace the whole file in one paste. That is the simplest for beginners, and the build pages use it ("give me all three files complete again"). The cost: you cannot see at a glance what changed, the answer is long, and an unrequested edit hides easily among hundreds of unchanged lines.
A
diff request asks only for the
parts that change, each clearly marked with where it goes ("replace
the function updateShip with this", "add after the line
scene.add(ship)"). It is easy to review, and it is how
changes are usually shown in
version control. The cost: you must paste each part in the right place, and a part
pasted in the wrong place produces errors that have nothing to do
with the change.
Ask for a change summary
A change summary is a short list, written by the assistant after its code, of what it changed and what it left alone. Asking for one costs a line in the prompt ("then list what you changed, in at most three lines") and gives you a checklist for reading the code:
Changed: - STAR_SPEED is now 60 instead of 30. - STAR_COUNT is now 2400 instead of 1800, so the faster stream does not look thinner. Unchanged: everything else.
The summary is the model's own description, not proof. Use it as a list of places to look, and treat any difference between the summary and the code as a reason to slow down. Here, for example, the prompt asked for faster stars; adding more stars is a decision the model made on its own, and the summary is what shows you it did.
Check each result
CodePen's preview updates by itself while you edit (on by default), so you see the effect of a paste immediately. Before you write the next prompt, check three things:
- The change you asked for is there and behaves as described. Each build page has a "What you should see" list for this.
- Everything else still works: the features from earlier steps, a resize, a reload. In testing terms this is a small regression test.
- The console is clean: no new errors or warnings (see Debugging with AI).
Some mistakes cannot be seen in the preview at all. A game that moves its stars by a fixed amount per frame looks perfect on a 60 Hz screen and runs more than twice as fast on a 144 Hz one. That is why the step 1 prompt asks for the pure logic "in small functions without Three.js or the DOM": a function that only does arithmetic can be run and checked anywhere. The example below takes two such functions from the step 1 reference version, unchanged, and plays one second at two screen rates. Next to them is a per-frame version, as an assistant might write it when nobody asked for delta time:
// From the GAME RULES section of step 1 (script.js), copied unchanged:
const STAR_FAR = -190; // stars start this far ahead (negative z = ahead)...
const STAR_NEAR = 12; // ...and wrap around once they pass the camera
const MAX_STEP = 0.05; // longest time step we accept, in seconds
function streamStar(z, dt, speed) {
const next = z + speed * dt;
return next > STAR_NEAR ? next - (STAR_NEAR - STAR_FAR) : next;
}
function clampStep(seconds) {
return Math.min(Math.max(seconds, 0), MAX_STEP);
}
// What an answer without delta time might do instead: a fixed step per frame.
function moveStarPerFrame(z) {
return z + 0.5;
}
// One second of play on a 60 Hz and on a 144 Hz screen.
for (const fps of [60, 144]) {
let perFrame = -100;
let withDelta = -100;
for (let frame = 0; frame < fps; frame++) {
perFrame = moveStarPerFrame(perFrame);
withDelta = streamStar(withDelta, clampStep(1 / fps), 30);
}
console.log(
`${fps} Hz: per frame ${perFrame + 100} units, speed x dt ${(withDelta + 100).toFixed(1)} units`,
);
}
// A star just in front of the camera wraps back to the far end.
console.log(streamStar(11.9, 0.01, 30).toFixed(1));
// A tab that was hidden for 2.5 seconds: one small step, not a jump.
console.log(clampStep(2.5));
60 Hz: per frame 30 units, speed x dt 30.0 units 144 Hz: per frame 72 units, speed x dt 30.0 units -189.8 0.05
Moving by "speed times the seconds since the last frame" (delta time, explained on Build 2 · A Ship You Can Fly) gives 30 units per second on both screens; the per-frame version gives 30 and 72. The last two lines check the edge cases: a star that passes the camera reappears at the far end, and a long pause is cut down to one 0.05-second step. Checks like these are the start of real unit tests, and asking for testable functions is one of the cheapest ways to keep AI-written code checkable.
When the conversation gets long
After many steps in one chat, answers tend to get worse: the model mixes up old and new versions of a function, or reintroduces a bug you fixed earlier. A long conversation is a lot of context, and models use long context less reliably (see context rot and lost in the middle). The remedy fits small steps naturally: start a new chat, describe the project in a few lines and paste the current working files. The build prompts from step 2 on already work this way: each one starts with "Here is my CodePen game so far" and ends with the pasted files. How to write such a restart prompt yourself is covered on Writing Your Own Prompt.