Part 2 — Building · 22 min read
Prompting for software
Why 'build me an app' fails, and the four-part prompt shape that works.
Everyone's first instinct is to type "build me an app that does X" and be disappointed. The gap between that and a prompt that actually works isn't magic words or "acting as a senior engineer" — it's information. A good prompt gives the AI enough to succeed, and a boundary so it doesn't wander.
Why "build me an app" fails
It's not that the request is too big. It's that it's underdetermined: there are a million valid interpretations, and the model has to pick one. It'll pick the statistically average one — which is why underspecified prompts produce output that feels weirdly generic. It isn't being lazy. You genuinely didn't say.
The four-part shape
Nearly every good prompt has these four parts. You won't always need all of them, but when a prompt fails, the missing piece is almost always one of these.
1. Context — what world are we in?
What the project is, who it's for, what already exists, what stage you're at. Much of this can live permanently in a CLAUDE.md file so you stop retyping it.
2. Task — what exactly should happen?
The specific outcome, described in behaviour. Not "add auth" but "a user can sign up with email and password, gets an error if the email is already taken, and lands on /dashboard after signing up".
3. Constraints — what are the rules and boundaries?
Which files may be touched. Which libraries to use or avoid. Patterns to follow. Things explicitly out of scope. This is the part beginners leave out, and it's the one that prevents the most damage.
4. Success criteria — how will we both know it's done?
What you'll click to verify. What should happen in the failure cases. If you can't describe how you'd check it, the AI can't tell when it's finished either.
The same request, three ways
| Level | Prompt | What you'll get |
|---|---|---|
| Poor | "Add a contact form" | A form that looks fine and does nothing. No validation, no error states, possibly a new library you didn't want. |
| Better | "Add a contact form to the homepage with name, email and message fields that sends to my email" | Something roughly right, but it'll invent an email approach — maybe one needing an account you don't have. |
| Good | See the prompt below | Something you can actually ship. |
Context: Next.js marketing site, App Router, Tailwind. Deployed on
Vercel. No backend yet. Existing components are in src/components
and use the patterns in Button.tsx.
Task: Add a contact form to the bottom of the homepage.
Fields: name (required), email (required, must be valid),
message (required, min 20 characters). On submit, send the message
to [email protected] using Resend — the API key will be in
RESEND_API_KEY in .env.local.
Constraints:
- Only touch src/app/page.tsx, a new ContactForm component, and a
new API route. Don't modify anything else.
- Match the styling of the existing Button and Input components.
- Don't add any form library — use a plain React form.
- Never expose the API key to the browser.
Done when:
- Submitting a valid form shows a success message and I receive
the email
- Submitting with an invalid email shows an inline error under
the field, and does not send
- Submitting twice quickly doesn't send two emails
- The form works at 375px wide
That's four times as long, and it'll save you an hour. Notice how much of it is constraints and failure cases.
Six techniques worth having
Plan before you build
Always, for anything bigger than a tweak. Shift+Tab in Claude Code, or "don't write any code yet — tell me your plan first" anywhere else. Reading a plan takes two minutes. Undoing a wrong implementation takes thirty.
Make it ask you questions
End with: "Before you start, ask me any questions where you'd otherwise have to guess." You'll routinely get three questions you hadn't considered. Answering them is faster than fixing the wrong guess. Done properly this has a name — the Socratic interview, below.
Give it an escape hatch
Add: "If you're not confident about something, say so rather than guessing." Imperfect, but it measurably reduces confident invention.
One thing at a time
A prompt containing "and also" is two prompts. Batched requests produce batched failures that are much harder to untangle.
Show, don't describe
Point at an existing file: "Build this the same way PricingCard.tsx works." One example beats three paragraphs, and it keeps your codebase consistent — which matters enormously later.
Ask for the reasoning, not just the code
"Explain what you changed and why, and tell me what could break" turns every task into a lesson. At your stage this is worth the extra tokens.
Three techniques worth knowing by name
Everything above is about getting the AI to do what you meant. These three are about something else: getting it to tell you the truth. They have names because they're the standard answers to the standard failure — an assistant that agrees with you, builds what you asked for, and never mentions that you asked for the wrong thing.
You'll hit this constantly. It doesn't feel like a problem, which is exactly why it is one.
Socratic interviewing — make it question you
Named after Socrates, who taught by asking rather than telling. You flip the direction of the conversation: instead of you describing the feature, the AI interviews you about it, one question at a time, until the requirements fall out of your own answers.
This works because you know more about your product than you can spell out unprompted. Everything obvious to you goes unsaid — and unsaid is exactly what the AI has to guess at. A good interview drags it into the open before it becomes a wrong guess you paid for in code.
The one rule that makes the difference: one question at a time. Ask for a list of questions and you'll get twelve, answer them in a blur, and skim the three that mattered. Ask one at a time and each answer shapes the next question — which is the whole point.
There are two versions of this prompt below. The first lets the questions follow wherever your answers lead, which is better when you don't yet know what you're building. The second walks a fixed checklist — states, permissions, failure cases — which is better when you do, and just need to be sure nothing's been skipped.
Before we build [feature], interview me about it. Use the Socratic method: ask me ONE question at a time, wait for my answer, and let my answer decide your next question. Do not give me a list of questions, and do not start building.
Dig at the things I'm most likely to be taking for granted — especially anything I describe as "obvious", "simple" or "just".
Keep going until you could write the spec yourself with no guesses left. When you get there, say so, then write it up as numbered acceptance criteria I can test by clicking.
If I give you a vague answer, don't accept it — ask again, more specifically.
Non-sycophantic prompting — tell it to disagree
Sycophantic means flattering, telling people what they want to hear. It's a well-documented tendency in AI models: they're trained on human feedback, humans rate agreeable answers higher, and so the model learns to agree. Ask "is this a good idea?" and you'll usually be told yes. Push back on a correct answer and it will often fold and apologise rather than hold its ground.
For a founder building alone this is genuinely dangerous. You have no colleague to say "that won't work". If your only reviewer is trained to agree with you, you don't have a reviewer — you have an audience.
The fix is to say so explicitly, and to ask in a way that doesn't smuggle in the answer you want. "Is this a good approach?" invites a yes. "What's wrong with this approach?" presupposes there's something, which is nearly always true. Better still, ask for both sides and make it commit to one.
I want your honest assessment, not encouragement. Ground rules for this answer:
- Do not open by telling me this is a good question or a solid idea.
- If I'm wrong, say I'm wrong, in the first sentence, and say why.
- If you don't know, say you don't know. Don't fill the gap with something plausible.
- If I push back on something you're confident about, hold your position and explain it again. Don't fold just because I disagreed.
- Rate your own confidence, and tell me what would change your mind.
Here's what I'm proposing: [describe the decision, feature or approach]
Tell me: the strongest case against it, what I'm assuming that might not be true, what it would cost me in three months if I'm wrong, and what you'd do instead. Then give me your actual recommendation — commit to one, don't give me a balanced list and leave the decision to me.
Adversarial review — make it attack the work
Adversarial means acting as an opponent. Rather than asking "does this look alright?", you assign the AI the job of breaking what's just been built — ideally in a fresh conversation, so it isn't defending work it can still remember writing.
This is a real practice in software teams, where it's called red-teaming: someone's explicit job is to attack the thing, so the flaws are found by a colleague rather than a customer. You can run it single-handed for the price of a prompt.
Two things make it work. Start a new chat — /clear in Claude Code, a new conversation in Cursor. An AI that just wrote the code is reviewing its own homework, and you'll get a gentler read. And ask for a fixed number of problems: "tell me ten ways this breaks" is much harder to wriggle out of than "any issues?", which can be answered with "looks good!".
The prompt below works on anything — a plan, a spec, a feature, a page of code. There's a code-specific companion to it at the bottom of this page, Get a second opinion on generated code, which also makes the AI explain what the code actually does. Reach for that one when you can't read the code yourself; reach for this one when you can already describe what it's meant to do and you want to know how it breaks.
Fresh eyes. You didn't build this and you owe it nothing. Your job is to find what's wrong with it — not to reassure me.
[Paste the plan, spec, code or feature — or point at the files]
Give me:
1. The ten most likely ways this fails in real use, most likely first. Be specific: what someone does, and what goes wrong.
2. What I've assumed without checking.
3. What breaks at ten times the usage.
4. What looks finished but isn't — anything wired to nothing, any error quietly swallowed, anything that only works for my own account.
5. The one thing here most likely to actually burn me, and why that one.
Rules: don't fix anything yet, don't soften it, and don't pad the list to reach ten — if there are only six real problems, give me six and say so.
Prompting when things go wrong
Bad: "it's broken, fix it". Good, in order:
- What you expected — "clicking Save should close the dialog and show the new item in the list"
- What actually happened — "the dialog stays open and nothing appears; the item does show after I refresh"
- The evidence — the full error from the terminal and the browser console, pasted in raw
- What you already tried — so it doesn't suggest it again
- The instruction — "find the root cause before changing anything. Explain it to me, then fix it."
That last line matters more than it looks. Without it you get a patch on a symptom; with it you're much more likely to get the actual cause. More in When it breaks.
I'm going to give you a prompt I was about to send to an AI coding agent. Don't do the task. Instead, critique the prompt.
Tell me:
1. What's ambiguous — where could you legitimately interpret this more than one way?
2. What context is missing that you'd need to do this well?
3. What constraints am I not stating that I probably assume? (Especially: what shouldn't be changed, what shouldn't be added.)
4. What success criteria am I missing — particularly failure cases and edge cases I haven't thought about?
5. Is this actually several tasks pretended to be one? If so, split it.
Then rewrite it as a prompt you'd be confident executing correctly first time, and explain what you changed and why.
My prompt: [paste yours]
I'm a non-technical founder building [describe your app] with [Next.js / your stack]. I'll be prompting an AI coding agent many times a day for the next few months.
Create me a reusable prompt template with fill-in-the-blanks, plus three worked examples for the kinds of task I'll actually do:
1. Adding a new page or screen
2. Changing existing behaviour without breaking anything else
3. Fixing a bug
For each, show the empty template and a filled-in example. Highlight which parts I must never leave out and why. Keep it short enough that I'll actually use it — if it takes five minutes to fill in, I won't.
Before I build [feature], interview me. Ask one question at a time, and don't move on until I've answered.
Cover:
- What exactly the user is trying to achieve, and what happens immediately before and after
- Every state this feature can be in: empty, loading, error, success, partially complete
- What happens when things go wrong: no network, bad input, double-click, session expired, back button
- Who's allowed to do this, and what happens if someone who isn't tries
- What data this creates or changes, and whether anything else depends on it
- What "done" means, precisely enough that we could both test it
Once you've asked everything, write it up as a short specification with numbered acceptance criteria. Don't write any code.
You didn't write this code — review it as a sceptical senior engineer who's been asked whether it's safe to ship.
Look at [file or recent changes] and tell me:
1. What it actually does, in plain English, step by step.
2. What happens in each failure case: bad input, empty values, network failure, a user who isn't logged in, the same action done twice quickly.
3. Anything that would break with 100 users instead of 1.
4. Anything that looks like it works but doesn't — UI wired to nothing, error handling that swallows problems silently, values that are hardcoded when they shouldn't be.
5. The single highest-priority thing to fix, and why.
Be blunt. I'm non-technical and I need to know what I'm actually holding.
Pick one small feature — "a newsletter signup box". Write four prompts for it: a one-liner; one with context added; one with constraints added; one with all four parts including success criteria. Run each in a fresh session in a throwaway project.
You'll know it worked when you can see the results getting less generic and more correct each time. Keep the four outputs side by side. This single exercise is worth more than reading this entire page.
Take a feature you genuinely want in your product. Write a prompt for it, ending with: "Before writing any code, ask me every question where you'd otherwise have to guess. Ask them one at a time."
You'll know it worked when it asks you at least three questions you hadn't thought about. Write down the questions — they're the gaps in your own thinking about your product, which is useful well beyond this exercise.
Deliberately break something in your practice project, then write a bug report prompt with all five parts above. Compare the response to what you get from just "it's broken".
You'll know it worked when the structured version identifies the actual cause on the first try, and the lazy version starts guessing. Save your structured version as a template.