# Coding Prompts

Course: [Prompt Engineering](https://www.devtutorials.ai/learn/prompt-engineering) · Section: Practical Applications
Source: https://www.devtutorials.ai/learn/prompt-engineering/practical-applications/coding-prompts

> Prompts for writing, explaining, debugging, refactoring and reviewing code: precise tickets, extracting and testing generated code, safe refactors, structured reviews, and keeping secrets out.

Developers use AI models every day. They use them to write functions, explain code they don't know, find bugs, clean up old code, and review pull requests. When you use a model well, it saves hours. When you use it carelessly, it adds code that looks right and seems fine at a quick look, but then breaks in production.

What makes the difference is the prompt, and the checks you do after it. A good coding prompt gives the language, the exact shape of the function, the rules, and some example inputs and outputs. A good habit is to **run** the code against tests before you trust it.

**The main idea:** describe the code like a clear, exact ticket. Ask for the code in a form you can pull out of the reply, and then test it. The only real proof is code that runs and passes its tests.

## A Real-World Example

Think about giving a task to a new developer on your team.

- If you say "Write a date function", you get something back. But you don't know what format it uses or which edge cases it handles.
- If you say "Write `formatDate(date)` in TypeScript. Return `DD MMM YYYY`, like `05 Mar 2026`. Throw an error for invalid dates. Don't use any libraries. Here are three tests it must pass", you get exactly what you need.

Even then, you would still run their code before you merge it. A model is the same. It is a fast developer who needs a clear ticket and a code review.

## What You Will Learn

In this lesson, you will learn:

1. How to write a prompt that asks for code like a clear, exact ticket.
2. How to pull code out of a reply and test it.
3. How to ask for explanations at the right level.
4. How to write debugging prompts that give the model what it needs.
5. How to refactor safely, and check that the code still does the same thing.
6. How to get code reviews in a fixed format.

Bigger models are much better at coding tasks. In the Prompt sandbox, switch to **Qwen2.5 1.5B** before you try the coding cards. Small models are still good practice for finding and testing mistakes.

## Code Generation: Write a Precise Ticket

A good prompt for writing code includes:

| Part | Example |
|---|---|
| Language and version | JavaScript (ES2020), no libraries |
| Exact signature (the function's name, inputs and output) | `function slugify(title)` returns a string |
| Rules | Use lower case, turn spaces into `-`, and remove all other symbols |
| Examples | `"Hello World!"` gives `"hello-world"` |
| Output format | Only the code, in one code block |

```js runnable title="Build a code generation prompt"
function codePrompt(t) {
  return [
    "Write a " + t.language + " function. " + t.limits,
    "Signature: " + t.signature,
    "Rules:",
    ...t.rules.map((r) => "- " + r),
    "Examples:",
    ...t.examples.map(([input, output]) => "- " + JSON.stringify(input) + " returns " + JSON.stringify(output)),
    "Reply with only the code in one code block. No explanation.",
  ].join("\n");
}

console.log(codePrompt({
  language: "JavaScript",
  limits: "No libraries.",
  signature: "function slugify(title)",
  rules: ["Lower case", "Spaces become -", "Remove anything that is not a-z, 0-9 or -", "No - at the start or end"],
  examples: [["Hello World!", "hello-world"], ["  Prompt  Engineering 101 ", "prompt-engineering-101"]],
}));
```

Expected output:

```text
Write a JavaScript function. No libraries.
Signature: function slugify(title)
Rules:
- Lower case
- Spaces become -
- Remove anything that is not a-z, 0-9 or -
- No - at the start or end
Examples:
- "Hello World!" returns "hello-world"
- "  Prompt  Engineering 101 " returns "prompt-engineering-101"
Reply with only the code in one code block. No explanation.
```

Try it with the real model. Compare a vague request with the clear ticket:

## Extract the Code and Test It

The model's reply puts the code inside a code block, between fences. Your code can pull it out and then run it against your test cases. The reply below is a sample written for this example. It has a small bug left in on purpose.

```js runnable title="Extract generated code and run the tests"
const fence = "`".repeat(3);
const reply = "Here is the function:\n" + fence + "js\nfunction slugify(title) {\n  return title.toLowerCase().trim().replace(/\\s+/g, \"-\").replace(/[^a-z0-9-]/g, \"\");\n}\n" + fence;

const code = reply.split(fence)[1].replace(/^js\n/, "");
const slugify = new Function(code + "\nreturn slugify;")();

const tests = [
  ["Hello World!", "hello-world"],
  ["  Prompt  Engineering 101 ", "prompt-engineering-101"],
  ["C# & .NET tips", "c-net-tips"],
];

for (const [input, expected] of tests) {
  const got = slugify(input);
  console.log(got === expected ? "pass" : "FAIL", JSON.stringify(input), "->", JSON.stringify(got));
}
```

Expected output:

```text
pass "Hello World!" -> "hello-world"
pass "  Prompt  Engineering 101 " -> "prompt-engineering-101"
FAIL "C# & .NET tips" -> "c--net-tips"
```

The first two tests are the same as the examples in the prompt, so they pass. The third test is an edge case the model never saw. When the code removes `#`, `&` and `.`, it leaves two dashes side by side. The tests found this problem in a second. Now you can add "Collapse repeated - into one" to the rules, or fix the code yourself.

**Easy Way to Remember:** for code a model wrote, **"it looks right" proves nothing, but "it passes the tests" does.** Always include at least one edge case that the prompt did not show.

Only run code from a model in a safe place: a test, a sandbox, or a setup you can throw away. Never let code from a model touch real data, secrets, or live systems before you have read it. And never paste API keys, passwords, or private customer data into a prompt.

## Explaining Code

When you ask a model to explain code, say **who you are** and **what you want to understand**. If you only say "Explain this", you get a line-by-line tour that you don't need.

```js runnable title="An explanation prompt with a level and a focus"
function explainPrompt(code, level, focus) {
  return [
    "Explain the code below to " + level + ".",
    "Focus on: " + focus + ".",
    "Use at most 5 short bullet points. Do not rewrite the code.",
    "<code>",
    code,
    "</code>",
  ].join("\n");
}

console.log(explainPrompt("const total = items.reduce((sum, i) => sum + i.price * i.qty, 0);", "a junior developer who knows for loops", "what reduce does and why 0 is passed"));
```

Expected output:

```text
Explain the code below to a junior developer who knows for loops.
Focus on: what reduce does and why 0 is passed.
Use at most 5 short bullet points. Do not rewrite the code.
<code>
const total = items.reduce((sum, i) => sum + i.price * i.qty, 0);
</code>
```

When you say what the reader already knows (here, "for loops"), the model can explain the new idea by comparing it to the one they know.

## Debugging Prompts

A model can only debug what it can see. "My code doesn't work" gives it nothing to go on. A good debugging prompt has four parts:

1. **The code**, cut down to the smallest part that still shows the bug.
2. **What you expected** to happen.
3. **What actually happened**, with the exact error message.
4. **What you have already tried.**

```js runnable title="Check a debugging prompt has what the model needs"
function debugReady(p) {
  const missing = [];
  if (!p.code || p.code.split("\n").length > 40) missing.push("code under 40 lines");
  if (!p.expected) missing.push("what you expected");
  if (!p.actual) missing.push("what happened");
  if (p.actual && !/error|exception|undefined|NaN|\d/i.test(p.actual)) missing.push("the exact error or output");
  if (!p.tried) missing.push("what you already tried");
  return missing.length ? "Add: " + missing.join(", ") : "Ready to send";
}

console.log(debugReady({ code: "", expected: "", actual: "it doesn't work", tried: "" }));
console.log(debugReady({
  code: "const avg = sum / items.lenght;",
  expected: "the average price",
  actual: "avg is NaN",
  tried: "logged sum, it is 120",
}));
```

Expected output:

```text
Add: code under 40 lines, what you expected, the exact error or output, what you already tried
Ready to send
```

The second prompt is ready to send, and you can already see the bug in it. `lenght` is a spelling mistake for `length`, so the code divides by `undefined`. Very often, just writing a good debugging prompt is enough to find the bug yourself.

## Big Tasks: Diagnose, Fix, Test, Refactor

For a big coding task, don't ask for everything at once. Split it into small requests, and check each one:

1. **Diagnose:** "Explain the most likely cause of the problem. Don't change any code yet."
2. **Fix:** "Suggest the smallest safe fix."
3. **Test:** "Write a test that fails before the fix and passes after it."
4. **Refactor:** "Now clean up the code without changing what it does."

A strong debugging prompt names the language and framework, the exact error, what should happen and what actually happens. It also asks for the smallest fix and a way to check that the fix works:

<PromptTry tab="chat" system="You are a careful senior JavaScript developer." prompt="I get TypeError: Cannot read properties of undefined (reading 'country') in this Node.js route.\nExpected: return the cart total.\nActual: it crashes when the user has no shipping address.\nCode:\nfunction cartTotal(user, items) {\n  const tax = user.shipping.country === 'UK' ? 0.2 : 0;\n  const sum = items.reduce((s, i) => s + i.price, 0);\n  return sum * (1 + tax);\n}\nExplain the likely root cause, suggest the smallest safe fix, and tell me how to verify it." />

Also, show the model only the code that matters. Tools like GitHub Copilot use the file you have open and the chat history. So keep the chat history on topic, and open the files that matter.

## Refactoring Safely

A refactor changes how code is written, but not what it does. When a model refactors your code, your job is to prove that the code still does the same thing. Run the old version and the new version on the same inputs, and compare the results.

```js runnable title="Check a refactor keeps the same behaviour"
function oldTotal(items) {
  let total = 0;
  for (let i = 0; i < items.length; i++) {
    if (items[i].qty > 0) total = total + items[i].price * items[i].qty;
  }
  return total;
}

// Suggested refactor (sample):
const newTotal = (items) => items.reduce((sum, i) => sum + i.price * i.qty, 0);

const inputs = [
  [{ price: 10, qty: 2 }],
  [{ price: 5, qty: 1 }, { price: 3, qty: 4 }],
  [{ price: 8, qty: -1 }],
  [],
];

inputs.forEach((items, n) => {
  const a = oldTotal(items);
  const b = newTotal(items);
  console.log("Input " + (n + 1) + ":", a === b ? "same (" + a + ")" : "DIFFERENT old=" + a + " new=" + b);
});
```

Expected output:

```text
Input 1: same (20)
Input 2: same (17)
Input 3: DIFFERENT old=0 new=-8
Input 4: same (0)
```

The new code looks cleaner, but it dropped the `qty > 0` check. Now a negative quantity makes the total smaller. Only input 3 shows this problem. This is why every refactoring prompt should say "keep the same behaviour, including edge cases", and why you should test with unusual inputs.

## Code Review Prompts

When you ask for a code review, ask for a fixed format: one problem per line, with how serious it is, the line number, and a short fix. Then your code can sort and count the problems.

```js runnable title="Read a structured code review"
const review = [
  "HIGH | line 12 | SQL built with string concatenation | use a parameterised query",
  "LOW | line 3 | unused import 'fs' | remove it",
  "MEDIUM | line 20 | no check for an empty list | return early when the list is empty",
].join("\n");

const order = { HIGH: 0, MEDIUM: 1, LOW: 2 };
const issues = review.split("\n").map((l) => {
  const [severity, line, problem, fix] = l.split("|").map((s) => s.trim());
  return { severity, line, problem, fix };
});

issues.sort((a, b) => order[a.severity] - order[b.severity]);
issues.forEach((i) => console.log(i.severity.padEnd(6), i.line.padEnd(8), i.problem));
console.log("Blocking:", issues.filter((i) => i.severity === "HIGH").length);
```

Expected output:

```text
HIGH   line 12  SQL built with string concatenation
MEDIUM line 20  no check for an empty list
LOW    line 3   unused import 'fs'
Blocking: 1
```

The review text here is a sample written for this example. A review from a model is a useful extra pair of eyes. It is especially good at common problems like injection attacks, missing checks, and unused code. But it does not replace a review by a person, or your tests.

## Common mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| Vague code request | "Write a date function." | Give the signature, the rules, and examples. | The code has the wrong shape, and the model guesses how it should behave. |
| Trusting code just by reading it | "It looks correct." | Run it against tests that include edge cases. | Hidden bugs reach production. |
| Debugging without details | "My code doesn't work, fix it." | Give the code, what you expected, what happened, and what you tried. | The model guesses at a problem it can't see. |
| Refactor without tests | "It's the same logic, just shorter." | Compare the old and new results on unusual inputs. | The code quietly starts doing something different. |
| Secrets in prompts | "I'll paste the config to get help." | Remove keys and private data first. | Your secrets are no longer under your control. |
| Treating the review as the final word | "The AI review found nothing." | Use it as an extra check, not the only one. | Real problems the model missed get merged. |

## Exercises

In each exercise, the last line should give back a value. Press **Check** to compare your value with the expected answer.

## What to Remember

- Write coding prompts like a **clear, exact ticket**: the language, the signature, the rules, examples, and the output format.
- **Pull the code** out of the reply and **run tests**, including edge cases.
- When you ask for an explanation, say **your level** and **what to focus on**.
- Debugging prompts need **the code, what you expected, what actually happened, and what you tried**.
- Split big tasks into **diagnose, fix, test, refactor**. Ask for the **smallest safe fix** and a way to check it.
- After a refactor, **compare what the old and new code do** on unusual inputs.
- Ask for reviews in a **fixed format**, and use them as an extra check.
- Never put **secrets or private data** in a prompt, and only run code from a model in a **safe place**.

This is the end of Chapter 5. Next: **Advanced AI Workflows**, where you put long text, checks, and tools together, and learn when prompting is not enough.
