03 · Prompting Fundamentals
A prompt is a specification. The clearer the spec, the better the result. Here you learn the anatomy of a strong prompt, the core techniques, and a library of developer prompts you can reuse every day.
By the end of this page you can
- Write prompts with the six building blocks: role, task, context, constraints, format, examples.
- Use zero-shot, few-shot, step-by-step, self-critique and "ask me questions" techniques.
- Turn a vague request into a precise one, and iterate when the answer is wrong.
- Reuse ready-made prompts for explaining, debugging, testing, refactoring and reviewing code.
Why prompting matters
From page 02 you know the model predicts the most plausible continuation of everything in its context. A vague prompt has many plausible continuations, and the model picks a generic one. A precise prompt narrows the possibilities down to what you actually want.
The anatomy of a great prompt
Vague vs precise
Vague
Write a function to parse dates.
Which language? Which formats? What happens on failure? Time zones? The model will guess.
Precise
Role: You are a senior Scala 3 developer. Task: Write a function parseDate(s: String): Either[String, LocalDate]. Context: Inputs come from CSV files; formats are "2024-03-15", "15/03/2024" and "Mar 15, 2024". Constraints: java.time only, no exceptions escape, no external libraries. Format: the code, then 4 MUnit tests, then 2 lines on edge cases you handled.
Ten principles that always apply
- Be specific about the goal. Say what "done" looks like.
- Give the context the model lacks. Versions, frameworks, error messages, relevant code, business rules.
- Say what to do, not only what to avoid. "Use immutable case classes" works better than "don't use vars".
- Specify the output format. Code only? Diff? Table? JSON? Bullets? Length?
- Show examples. One good example of the style you want is worth a paragraph of description.
- Separate instructions from data. Use delimiters such as
```,<code>...</code>or headings, so pasted text is not mistaken for instructions. - Break big tasks into steps. Plan first, then implement piece by piece.
- Let it ask questions. "Before you start, ask me up to 3 clarifying questions."
- Ask it to check its work. "Review your answer for bugs and edge cases, then fix them."
- Iterate. The first answer is a draft. Refine with targeted feedback.
Core techniques
Zero-shot
Just ask, with no examples. Works well for common tasks.
Convert this Python list comprehension to idiomatic Scala 3: [x * 2 for x in nums if x % 2 == 0]
Few-shot
Give 2–5 examples of input → output so the model copies the pattern. This is excellent for consistent formats, naming conventions and classification.
Convert log lines to JSON.
Input: 2026-01-03 12:00:01 ERROR PaymentService timeout after 30s
Output: {"ts":"2026-01-03T12:00:01","level":"ERROR","svc":"PaymentService","msg":"timeout after 30s"}
Input: 2026-01-03 12:00:05 INFO UserService login ok id=42
Output: {"ts":"2026-01-03T12:00:05","level":"INFO","svc":"UserService","msg":"login ok id=42"}
Input: 2026-01-03 12:01:17 WARN CacheLayer eviction rate high
Output:
Step-by-step (chain of thought)
For reasoning tasks, ask the model to work through the problem before giving the answer. Reasoning models do this internally, but asking for an explicit plan still helps you check the logic.
This Spark job produces duplicate rows after the join. First list the possible causes, ranked by likelihood given the code below. Then explain how to confirm each one. Only then propose a fix. ```scala ...code... ```
Role / persona
A role sets vocabulary, depth and priorities: "Act as a security reviewer", "Explain like a patient teacher to a beginner", "Respond as a strict code reviewer who blocks merges for missing tests".
Ask me questions first
I want to design a rate limiter for our Scala HTTP service. Before proposing anything, ask me the 5 most important questions you need answered.
This one technique prevents a large share of bad answers, because the model stops guessing at your requirements.
Self-critique and refine
Now act as a strict reviewer of the code you just wrote. List concrete problems (bugs, edge cases, performance, readability), then output a corrected version.
Generate options, then choose
Give me 3 different approaches to caching these results (in-memory, Redis, HTTP caching). Compare them in a table: complexity, consistency, ops cost, failure modes. Recommend one for a single-instance service with 200 req/s.
Structured output
Analyse the stack trace and return ONLY JSON matching:
{"root_cause": string, "file": string, "line": number, "confidence": "low"|"medium"|"high", "fix": string}
Level 2 covers enforced structured output with JSON Schema through APIs.
The iteration loop
| Symptom | Likely cause | Fix |
|---|---|---|
| Generic, shallow answer | Too little context | Add the code, versions, constraints and goal. |
| Uses wrong library/version | Model assumed defaults | State versions explicitly, and paste docs if the version is new. |
| Answer ignores part of the request | Too many asks at once | Split into steps, or number the requirements. |
| Too verbose | No format given | "Code only", "max 5 bullets", "no explanations". |
| Keeps repeating the same mistake | Polluted conversation | Start a fresh chat with a better prompt and the lesson learned. |
| Invents APIs | Knowledge gap | Give the real API signature or docs, or let an agent compile and check. |
Developer prompt library
Copy, adapt and save these. Page 07 shows how to turn your favourites into reusable prompt files / skills.
Explain unfamiliar code
Explain this code to me. I know Python well but I'm new to Scala 3. 1) One-paragraph summary of what it does. 2) Walk through it block by block. 3) Point out any Scala features I may not know (givens, extension methods, for-comprehensions, etc.) and compare each to the closest Python equivalent. ```scala <paste code> ```
Debug an error
I get this error when running `sbt test`: ``` <paste full error and stack trace> ``` Relevant code: ```scala <paste code> ``` Environment: Scala 3.x, sbt 1.x, JDK 21. What I expected: ... What happened: ... What I already tried: ... Give the most likely root cause first, how to confirm it, then the fix.
Write tests
Write MUnit tests for the function below. Cover: happy path, empty input, boundary values, invalid input, and one property-style test. Follow the style of this existing test: ```scala <paste an existing test> ``` Code under test: ```scala <paste code> ```
Refactor safely
Refactor this to be more idiomatic and readable WITHOUT changing behaviour. Constraints: keep the public API identical; no new dependencies; prefer immutability. Output: the refactored code, then a bullet list of each change and why.
Code review
Review this diff as a strict senior engineer. Categorise findings as [BLOCKER] / [SHOULD] / [NIT]. Check: correctness, edge cases, error handling, concurrency, security, performance, naming, tests. Be specific with line references. Don't praise. ```diff <paste diff> ```
Design discussion
I'm designing <feature>. Constraints: <...>. Propose 2-3 designs, compare them in a table (complexity, scalability, failure modes, testability), recommend one, and list the open questions I must answer before implementing.
Learn a concept
Teach me <concept> step by step. Start with an intuitive explanation and analogy, then a minimal Scala example, then a Python example, then common mistakes. After that, quiz me with 3 questions, one at a time, and wait for my answer.
Commit message / PR description
Write a commit message for this diff. Format: imperative subject under 60 chars, blank line, body explaining WHY (not what), bullet list of notable changes. ```diff <paste diff> ```
Prompting agents vs chatting
When you prompt an agent (which can read files and run commands), the same principles apply, with additions:
- State the goal and the definition of done: "All tests in
core/pass andsbt scalafmtCheckis clean." - Point to where to look: files, modules, similar existing code to copy.
- State boundaries: "Don't modify the public API. Don't touch the
legacy/folder. Ask before adding dependencies." - Ask for a plan first on big tasks, review it, then let it execute.
More in Agents 101.
Anti-patterns
- "Make it better." Better how? Faster, shorter, safer, more readable?
- Mega-prompts with 20 requirements and no structure. Number them, or split the task.
- Pasting secrets, customer data or proprietary code into tools you are not allowed to use. See page 14.
- Accepting code you don't understand. Ask it to explain, or don't merge it.
- Threats, bribes or ALL CAPS. Clear and specific beats emotional. Modern models respond best to calm, precise instructions.
- Ask an assistant: "Write a function to validate email addresses." Note what it assumes.
- Now rewrite the prompt using all six building blocks (pick Scala or Python, define what is valid, error type, tests, format).
- Compare the two answers. Then ask: "What questions should I have asked myself before writing that prompt?"
Key takeaways
- A prompt is a spec: role, task, context, constraints, format, examples.
- Few-shot examples, step-by-step, ask-questions-first and self-critique are your core tools.
- Iterate with specific feedback, and restart fresh when a thread gets polluted.
- Build a personal prompt library. You will turn it into skills later.