Prompting

Noob · 9 min read

A prompt is a work order for a very fast, very literal contractor who has never met you, will not ask clarifying questions unless invited to, and will cheerfully build the wrong thing to a high standard. Everything else in this course is a refinement of one skill: saying what you actually want, with the context needed to want it correctly. This lesson is that skill, taught properly — not "tips," but the mechanics of why prompts fail and the small set of moves that fix them.

Why prompts fail: the completion machine

A language model is a completion machine: given everything in front of it, it produces the most plausible continuation. That single fact explains nearly every prompting failure you will ever have:

  • Give it an ambiguous request, and it completes with the most statistically common interpretation — which is the average developer's intent, not yours.
  • Give it a thin request, and it fills the gaps with defaults from its training data: popular frameworks, common conventions, tutorial-grade patterns. Not wrong exactly. Just generic.
  • Give it a conflicting request (your words say one thing, the code you pasted implies another), and it resolves the conflict silently, in whichever direction seemed more probable, without telling you a conflict existed.

The model is never lazy and never obstinate. It is doing exactly one thing — plausible continuation — with exactly what you gave it. So the craft of prompting is really the craft of constructing a context in which the most plausible continuation is the thing you want. Hold that frame and the rest of this lesson is just consequences.

Outcome, not activity

Weak prompts describe an activity. Strong prompts describe an outcome and its acceptance criteria. Watch the difference on a real task:

Weak: Improve the error handling in this service.

Strong: When the payment provider times out, the current code retries forever and the request hangs. Change it so that: (1) retries stop after 3 attempts with exponential backoff starting at 200ms; (2) the final failure surfaces as a 503 with error code PAYMENT_UPSTREAM_TIMEOUT; (3) a test proves a timeout no longer hangs. Use the existing ApiError class. Don't touch the happy path.

Annotate what each clause is doing, because every one has a job:

ClauseJob
"retries forever and the request hangs"anchors the problem, so the model optimizes for the symptom you have, not error handling in general
"3 attempts, 200ms backoff"removes a judgement call the model would otherwise make silently
"503 with error code ..."defines the observable contract — testable, reviewable
"a test proves it"defines done as something checkable
"use the existing ApiError"anchors to your codebase, blocks the invented-abstraction reflex
"don't touch the happy path"fences the blast radius

The strong prompt is four times longer and roughly ten times cheaper, because the weak one costs three rounds of "no, not like that" — and each round drags a failed attempt forward in context, degrading the next one (lesson 2 explains the mechanics of that pollution).

The drill that builds this habit: before sending anything, ask "could a competent stranger, seeing my screen for the first time, do this correctly from these words?" The model is that stranger. Every time. It does not remember yesterday's session, your conventions, or the thing you both agreed on last week — unless something puts it back in front of it (that "something" is lessons 4, 8, and 24).

The four ingredients

Nearly every coding prompt worth sending contains four things. When output disappoints, diagnose by checking which one you skipped:

1. Intent — what should be true when the work is done, as a result, not a procedure. "Users can reset their password by email" beats "add a route and a token table," because stating the result lets the model propose a better procedure than yours if one exists — and it often does.

2. Context — what the model cannot guess: your framework versions, the patterns this codebase already uses, the two files that matter. Agents can search your repo, but pointing beats hoping: "the relevant code is in src/auth/session.ts; follow the pattern in src/auth/reset.ts" turns a 40,000-token exploration into a 2,000-token read (lesson 2 will make you feel this cost).

3. Boundaries — what is out of scope. An uninstructed model refactors opportunistically: renames, reformats, "improves" adjacent code. Each change might even be good; together they bury your actual change in a diff nobody can review. "Do not reformat unrelated code. No new dependencies. Migrations are off-limits" — one line each, and the diff stays reviewable.

4. Definition of done — a test that must pass, a command that must exit zero, an input/output example. If you can't state one, stop: you don't know what you want yet, and no phrasing will extract from the model a decision you haven't made. Half of "bad model output" is unmade decisions, outsourced by accident.

a wisha work order"make it better"the completion machinefills every gap with the averagesomething plausible,for a codebase that is not yoursgoalthe outcome, not the activityconstraintswhat must not changepointersthe files and docs to readdonethe check you will actually runa diff you can verify
The same model, two inputs. A wish makes the model guess; every unstated choice is filled with the statistical average of everyone else's codebase. A work order closes the gaps the machine would otherwise fill, and it ends in a check you can run rather than an impression you can form.

Ambiguity: make asking legal

By default, a model treats your prompt as complete and fills every gap itself — confident nonsense in, confident nonsense out. One line changes the contract: "If anything is ambiguous or underspecified, ask before writing code." Models honor this well, but only when told; asking first isn't the plausible continuation of a work order unless you make it one. For big tasks, go further: "Propose a plan first — files you'll touch, approach, risks — and wait for my approval before editing." The plan costs one round trip and catches misunderstandings when they're words instead of diffs. (Claude Code has a plan mode for exactly this rhythm; use it for anything that touches more than a couple of files.)

Steer early, don't correct late

There are two ways to spend words: constraints before the work, or critiques after it. Constraints are five to ten times cheaper. "Use the existing error class" costs a sentence; noticing the invented error class, explaining the problem, and regenerating costs a full cycle — and leaves the wrong attempt in context, quietly influencing what comes next.

Which leads to the counterintuitive move that separates experienced operators: when output is wrong, don't pile on corrections — edit the original prompt and start clean. A conversation containing one wrong attempt plus three course-corrections is a worse foundation than a corrected prompt with a fresh start. You're not scolding an employee who must learn; you're revising a work order for a contractor with no memory. Revise the order. (In practice: two corrections is the threshold. If the third correction is forming, rewrite the prompt instead — you now know exactly what it was missing.)

Show, don't describe

Models pattern-match harder than they rule-follow. One real example of the output you want outperforms paragraphs of adjectives:

Write the endpoint following this house style — here's our GET /api/orders/:id for reference: [paste 30 lines]. Same error shape, same validation approach, same test structure.

Thirty pasted lines just taught error shape, naming, validation style, and test conventions simultaneously — things you'd struggle to specify in prose and the model would struggle to follow as rules. When the task has edge cases, show one: "note how this handler returns 404 rather than throwing when the id doesn't parse — do the same" teaches the boundary better than any description of the boundary. This is also why lesson 4 tells you to keep one canonical example of your recurring artifacts where every session can see it.

The failure catalog

Learn these by name; you'll meet them weekly.

  • The eager guess. Ambiguity resolved silently in favor of the common case. Symptom: output that's reasonable for someone else's project. Fix: the ambiguity line above, plus more context.
  • The scope creep. Ten improvements you didn't ask for wrapped around the one you did. Symptom: unreviewable diffs. Fix: explicit boundaries; ask for "the minimal diff that achieves X."
  • The confident wrong constant. Flag names, API signatures, version numbers — a model's memory of these is lossy compression of the internet at training time. Symptom: code that's 98% right and fails on a parameter name. Fix: supply the specifics, or instruct it to check the docs/source rather than recall (agents can read node_modules and man pages — tell them to).
  • The buried instruction. In long prompts, middle-of-prompt instructions get the least attention; beginnings and ends dominate. Fix: non-negotiables first, and restate the single most important one at the end.
  • The over-specified prompt. Yes, it exists: dictate every implementation detail and you've reduced the model to a typist, inheriting your design bugs verbatim. Specify outcomes and constraints tightly, procedures loosely — leave room for it to know things you don't.

Where this goes

Single-shot prompting hits a ceiling fast, and the rest of the course is what lies past it: instructions that persist (lessons 4, 8), prompts packaged as reusable commands (lesson 9), prompts triggered by situation rather than memory (lesson 14), and finally prompts you never type because pipelines send them (lesson 18 onward). Every one of those is this lesson wearing armor — a persistent instruction is a prompt; a skill is a prompt with a trigger; an agent fleet is prompts with a supervisor. Precision here compounds through everything downstream. Vagueness here is what the fanciest orchestration cannot fix.

A personal teaching project, in development. The registry it builds is a teaching artifact and is not an assurance opinion about any issuer. Not affiliated with or endorsed by any employer, or by any vendor whose tools it describes. Where a unit depends on a specific flag or path, it names the version it was verified against. All units.