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.

Level 150 minTrack 3 · Prompt engineering

By the end of this page you can

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.

Analogy: briefing a brilliant new colleague Imagine a very smart contractor who joined today. They know every language and framework, but nothing about your project, team, constraints or taste. What would you tell them before handing over a task? That is your prompt.

The anatomy of a great prompt

1. Rolewho should it act as?"senior Scala reviewer" 2. Taskwhat exactly to do"refactor to use Either" 3. Contextbackground, code, errors"Scala 3.5, Cats Effect" 4. Constraintsrules and boundaries"no new dependencies" 5. Output formatshape of the answer"diff + 3-bullet summary" 6. Examplesshow, don't just tell"like this existing test…"
Not every prompt needs all six. For anything non-trivial, task + context + format are the minimum.

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

  1. Be specific about the goal. Say what "done" looks like.
  2. Give the context the model lacks. Versions, frameworks, error messages, relevant code, business rules.
  3. Say what to do, not only what to avoid. "Use immutable case classes" works better than "don't use vars".
  4. Specify the output format. Code only? Diff? Table? JSON? Bullets? Length?
  5. Show examples. One good example of the style you want is worth a paragraph of description.
  6. Separate instructions from data. Use delimiters such as ```, <code>...</code> or headings, so pasted text is not mistaken for instructions.
  7. Break big tasks into steps. Plan first, then implement piece by piece.
  8. Let it ask questions. "Before you start, ask me up to 3 clarifying questions."
  9. Ask it to check its work. "Review your answer for bugs and edge cases, then fix them."
  10. 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

Prompt Answer Evaluate Accept refine: add context, constraints, an example, or split the task run it, test it, read it
Experts iterate quickly with specific feedback ("the second test is wrong because…"), not "try again".
SymptomLikely causeFix
Generic, shallow answerToo little contextAdd the code, versions, constraints and goal.
Uses wrong library/versionModel assumed defaultsState versions explicitly, and paste docs if the version is new.
Answer ignores part of the requestToo many asks at onceSplit into steps, or number the requirements.
Too verboseNo format given"Code only", "max 5 bullets", "no explanations".
Keeps repeating the same mistakePolluted conversationStart a fresh chat with a better prompt and the lesson learned.
Invents APIsKnowledge gapGive the real API signature or docs, or let an agent compile and check.
Start fresh more often than you think Long conversations collect wrong turns, and the model keeps "seeing" its old mistakes. When a thread goes off track, open a new chat and write one clean prompt that includes what you learned.

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:

More in Agents 101.

Anti-patterns

Try it (15 min)
  1. Ask an assistant: "Write a function to validate email addresses." Note what it assumes.
  2. Now rewrite the prompt using all six building blocks (pick Scala or Python, define what is valid, error type, tests, format).
  3. Compare the two answers. Then ask: "What questions should I have asked myself before writing that prompt?"

Key takeaways