A coding prompt is a spec. The model has not seen your project, does not know which version of the language you run, and cannot ask what should happen when the input is empty. Whatever the prompt leaves out, it fills with the most common choice from its training data, and the most common choice is often not yours. The prompts below give the model fewer things to guess.
Write the spec before the code
The block below asks for a small Python function. Each part of the prompt answers a question the model would otherwise answer for you. Switch the parts off one at a time and imagine the reply without them: without the constraints you may get a third-party library, without the context the model has to guess what counts as valid input, without the format you may get no tests.
The function matches the three optional parts in order and rejects a match where all three are empty.
import re
_PATTERN = re.compile(r"(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?")
def parse_duration(text: str) -> int:
match = _PATTERN.fullmatch(text)
if not match or not any(match.groups()):
raise ValueError(f"invalid duration: {text!r}")
hours, minutes, seconds = (int(g) if g else 0 for g in match.groups())
return hours * 3600 + minutes * 60 + seconds
import pytest
from duration import parse_duration
@pytest.mark.parametrize("text, expected", [
("1h30m", 5400), ("45m", 2700), ("2h", 7200), ("90s", 90),
])
def test_valid(text, expected):
assert parse_duration(text) == expected
@pytest.mark.parametrize("text", ["", "1m1h", "1.5h"])
def test_invalid(text):
with pytest.raises(ValueError):
parse_duration(text)
Four details in that prompt do most of the work:
- Examples with their answers. "1h30m returns 5400" is a test the model can check its own code against, and it removes any doubt about the unit.
- The language version and the allowed libraries. Without them you may get a library you have not installed or syntax newer than your interpreter.
- What counts as invalid, and what should happen then. Error handling is easy for a model to leave out when nobody asks for it.
- Tests in the answer. They turn "looks right" into something you can run. If a test fails, you paste the failure back, which is a much better follow-up than "it doesn't work".
The any(match.groups()) check is worth noticing too: the pattern alone matches an empty string, because every part is optional. The prompt's line about the empty string is what makes that case show up in the code and in the tests.
Name the version, the stack and what already exists
Models lean toward the style that was most common in their training data. For JavaScript that can mean CommonJS require in a project that uses ES modules, for Python it can mean a library API that has since changed (Pydantic 1 versus 2 is a common case), and for any fast-moving framework it can mean the pattern from two major versions ago. One line usually fixes it: "Node 22, ES modules, no TypeScript" or "React 19, function components, plain CSS modules".
When you are adding to an existing project, the model needs to see the parts the new code will touch. Paste the function signature it must call, the data shape it will receive, and one existing file that shows your conventions. "Use the db.query(sql, params) helper from the file below" gets code that fits; without it you may get a new database connection written from scratch. Leave out files that have nothing to do with the change, because every unrelated line is something the model may try to reuse.
Ask for one small step at a time
The most common failure in "vibe coding", building an app by describing it to an AI, is asking for the whole app at once. The model has to choose a framework, a database, a folder structure and a dozen features in one reply, and a single reply rarely holds working code for all of it, so what often comes back is an outline. Compare the two tabs.
Here is a full-stack todo app using React, Node.js with Express and MongoDB.
Project structure
todo-app/
client/ (React front end)
server/ (Express API, auth, reminders)
server/index.js
const express = require("express");
const mongoose = require("mongoose");
const app = express();
app.use(express.json());
// ... auth routes, todo routes and the reminder scheduler go here
For authentication you can add JWT with jsonwebtoken, and for reminders a scheduler such as node-cron. Let me know if you want me to fill in any of these parts.
The first reply is not wrong, but it is a skeleton: it chose three technologies for you and left the real work as comments. The second reply is short enough to read, runs as soon as you open the file, and gives you a working base for step 2 ("now save the list in localStorage so it survives a reload"). Each step is small enough that when something breaks, you know which change broke it.
This is prompt chaining done by hand: the output of one request becomes the starting point of the next. Paste the current version of the file into each new step, so the model edits the code you actually have rather than the one it remembers writing.
Ask for a plan before a large change
For anything bigger than one function, ask for the plan first and the code second: "List the files you would change and what each change does. Do not write code yet." A plan is quick to read and quick to correct. If it proposes a new dependency you do not want or misses a file you know is involved, you fix that in one sentence instead of discovering it across three hundred lines of code.
Check what comes back
Generated code fails in a few predictable ways, and each one has a prompt habit that catches it:
- Invented APIs. A model can call a function or import a package that does not exist, because the name sounds plausible. Look up unfamiliar imports before you install them; AI hallucination explains why this happens.
- Silent edge cases. Code that works on the happy path and crashes on an empty list. Listing the edge cases in the prompt, and asking for tests, is the cheapest fix.
- Quiet changes. When you ask for a fix in a long file, the model may also rename things or reorganize code you did not ask about. Add "change only what is needed and list every change you made".
When the code runs but misbehaves, switch to a debugging prompt: prompts for debugging covers what to paste. Before you merge anything important, a second pass with a code review prompt can catch problems the writing prompt did not think to ask about.
Frequently Asked Questions
What is the best prompt for coding with ChatGPT or Claude?
There is no single magic prompt. The prompts that work read like a short spec: the language and version, what the code receives and returns, two or three example inputs with their outputs, the edge cases, and anything the code must not use. Ending with "also write tests for these cases" gives you a way to check the answer instead of trusting it.
What are vibe coding prompts?
"Vibe coding" describes building software mostly by describing what you want to an AI and accepting the code it writes, often without reading it closely. The prompts that keep a vibe coding project working are small ones: one feature per request, a clear statement of what already exists, and a request to run or test the result before moving on. Large all-at-once requests are where these projects tend to break.
Should I tell the AI which programming language version to use?
Yes. Languages and libraries change between versions, and a model will otherwise write whatever style was most common in its training data, which can be older than your setup. Naming the version ("Python 3.12", "React 19 with function components", "Node 22, ES modules") avoids answers built on APIs you do not have.
Can I trust code written by AI?
Treat it like code from a new colleague: probably close, sometimes wrong in ways that look right. Run it, test it with the edge cases you care about, and read any part that touches money, security or user data. Models can also invent functions or packages that do not exist, so check unfamiliar imports before installing anything.
Why does AI-generated code break when my project gets bigger?
The model only sees what is in the conversation. As a project grows, it stops seeing the files it is not shown, and it fills the gaps with guesses about names, structure and earlier decisions. Paste the relevant files, state the conventions the project follows, and keep each request to one change.