In W3.1.1 you saw what Copilot sends to and from your machine and where the limits are. This lesson is about the start of the path: the prompt and the context it brings along. You steer both, and both decide whether the answer is usable or only sounds plausible. At the end you have given the same task with three prompts and compared the results, and carried out a restructuring of the Kladde in small steps.
What holds for the whole course still holds: What Copilot answers is not predictable. So the course shows prompts, never "the" answer, and writes "a possible answer" when it describes an answer; it then says what to check in it. Commands and surfaces that were only read against the documentation carry the note "not run against Copilot".
The basis of this lesson is the page "Prompt engineering for GitHub Copilot" in GitHub's documentation and the learning paths on Microsoft Learn (links below). Where your editor differs from the documentation, the editor applies; your editor's documentation gives the exact spellings.
The structure of a prompt
A prompt is a work instruction to someone who writes good code but does not know the Kladde and does not ask questions. What you do not write, they guess. Five building blocks help to forget nothing. Not every prompt needs all of them:
| Building block | The question behind it | Example from the Kladde |
|---|---|---|
| Goal | What should exist at the end? | "Tests for TimeRange.Overlaps" |
| Context | What is the starting point, what exists already? | "TimeRange is a record with Start and End; tests are in Kladde.Core.Tests" |
| Constraints | Which rules and limits apply? | "xUnit, no mock, names in the style ARangeThatTouchesAnotherDoesNotOverlap" |
| Output format | What should the result look like? | "Only the test methods, no explanatory text" |
| Example | What does a good piece of it look like? | an existing test as a template |
GitHub's documentation gives four pieces of advice that fit this table:
- First general, then specific. Describe the goal first, then the requirements, the most important first. The page says that Copilot gives instructions at the beginning and at the end of a prompt more weight.
- Give examples. Example inputs, example outputs or an example implementation; tests can serve as an example too.
- Avoid ambiguity. Name the function or file instead of "this one". State rules directly, not as an instruction about instructions.
- Break complex tasks into simpler ones and have them done step by step.
A bad and a better prompt for the same task:
Write tests for the appointment check.
Goal: xUnit tests for TimeRange.Overlaps (file TimeRange.cs, in the context).
Rule: Two time ranges overlap if each begins before the other
ends. If the end and the start touch, it is not an overlap.
Constraints: xUnit and Assert, no mock, fixed points in time, method names in
PascalCase without underscores, one assertion per test.
Cases: equal, fully contained, partially overlapping (both directions),
touching (both directions), separate.
Output: only the test methods, no explanatory text.
The second prompt is longer, but not rambling: It states the rule, because the rule fixes the edge that a model otherwise guesses, and it names the cases that you want checked. The more of your knowledge about the Kladde is in a request, the less Copilot has to guess.
How the context comes about
Copilot does not know your repository as a whole. For every request the editor assembles a context (W3.1.1): open files, a selection, the name of the repository, the chat history and an instruction file, if there is one. You influence it in four places.
Open files. The documentation advises opening relevant files and closing irrelevant ones. This is the simplest measure and
often the most effective: For "tests for TimeRange" you open TimeRange.cs and an existing test file, not twelve other files.
Selection. Selected code is a clear hint of what it is about. For "explain" and "restructure" you select the method, not the file.
References with @. According to the documentation, in the chat in the editor and on github.com you can attach files, issues, pull requests or
repositories with @; some editors also know slash commands such as /fix and /tests. In the Copilot CLI, @path names a
file of the repository and #number an issue or pull request. Which characters and commands your editor knows is in its
documentation (not run against Copilot).
The chat history. Conversational forms use the history so far as context. That is an advantage (you can refine: "and now also for appointments without participants") and a trap: A history with false starts, mistakes and contradictory constraints is bad context. The documentation says that irrelevant contributions can be removed or rolled back where that is supported (the CLI has commands for it; the cloud agent does not allow it).
Two conclusions:
- Check what is in the context before you judge the answer. Did Copilot see the file you refer to? If your editor shows which files were included, look there (the display differs by editor; not run against Copilot). If something is missing, the answer is not "bad", it was written without knowledge.
- What is in the context leaves the machine (W3.1.1). Close files with keys or real personal data before you ask. The rules for this and the exclusions follow in W3.2.3.
Zero-shot and few-shot
A zero-shot prompt describes the task without giving an example. A few-shot prompt contains one or more examples of what the result should look like. Both have their place:
- Zero-shot is enough if the task is clear and common ("explain this method").
- Few-shot pays off if form, naming or style matter and are hard to describe. An example often says more than a paragraph of constraints.
A few-shot prompt for test data (the format is an example, not the Kladde's data model). The examples show the form; you still have to check the generated data:
Generate five more test contacts in the same format as the two examples.
Only invented names, addresses with the domain example.test, no real people.
{ "name": "Mara Sample", "email": "mara@example.test", "company": "Sample Inc." }
{ "name": "Jonas Sample", "email": "jonas@example.test", "company": null }
Here too: An example steers strongly. If the example is faulty or too specific, Copilot takes over the mistake. Show only examples that you have checked yourself, and choose them so that they show the range (here: one contact with a company, one without). Examples with real personal data or credentials are taboo.
Small steps, one session per task
The documentation advises breaking complex tasks into simpler ones and starting new tasks in a new conversation. Both have a practical reason:
- Small steps can be checked. An answer that contains two methods and a test you check in one go. One that changes ten files you no longer check, you skim it. How big a step is, is determined by your ability to check, not by the ability of the model.
- One session per task keeps the context clean. If you put test data first, then a restructuring, then a bug hunt into the same conversation, you give Copilot contradictory constraints. Begin the next task with a new session and take along only what belongs to it (the constraints in the prompt, the relevant file).
A decomposition pattern for a restructuring that has proven itself:
- Understand: have Copilot explain what the code does; your check is whether the explanation is right.
- Secure: have tests written for today's behavior and see them green.
- Change: the smallest restructuring, one concern per step.
- Check: tests, read the diff, commit.
- Repeat.
The path stays the same whether you work alone or with Copilot: tests first, one concern per step, the diff is read.
Five kinds of tasks
Generating
New code from a description. The risk is greatest because there is nothing for Copilot to hold on to: Invented methods and wrong assumptions show up here most often. Give goal, constraints and example, and check every assumption that is in the answer against the real code.
An example from the Kladde: The response of the appointment API, AppointmentResponse, has no Id: It contains Title, Start, End and
ContactIds (K2, lesson 2.4.3). If you ask Copilot without this information to write "a test for the appointment endpoint", a possible
answer is a test that reads response.Id. The member does not exist; the compiler reports it, and you know that the context was missing.
Then write the shape of the response into the prompt or open the file AppointmentResponse.cs.
Explaining
Copilot explains selected code. This is the lowest-risk task because you accept nothing, and the most instructive because you learn how
Copilot reads your code. Two rules: Check the explanation against the code (Copilot sometimes describes what the code
should do), and ask about edge cases, not only the normal case. For Overlaps, for example: "What happens if one
time range begins exactly where the other ends?" You can measure the answer against the rule in K1.
Restructuring
Changing existing code without changing its behavior. Here small steps and tests matter most. State exactly what should change ("extract method", "rename") and what must stay the same ("public signature and behavior"). A test before the restructuring (step 2 above) is your proof that the behavior stayed the same. Read the diff before you accept; a restructuring that "on the side" changes a condition is the most dangerous form of error.
Documenting
Comments, XML documentation, README sections. Copilot phrases well, but it describes what is in the code, not why it
is that way. You supply the why. Check that the documentation describes the actual behavior and contains no assurances that the
code does not keep. An example: The line Start < other.End && other.Start < End deserves a comment on the edge ("touching is not
overlapping"); a comment "checks whether two time ranges intersect" says nothing that the name does not say.
Test data
Invented data for tests and examples. Copilot is useful and fast here, but check three things: The data is invented
(no real people, no real addresses: example.test or example.com), it keeps the Kladde's rules (an appointment with
an end before its start is not a valid TimeRange), and it covers edge cases instead of only the normal case. Examples with credentials are
placeholders like <secret>, never a real value.
Exercise 1
Three prompts for the same task. Compare an imprecise prompt, a precise one and one with an example.
- Create a branch
feature/copilot-promptsand openTimeRange.csand an existing test file of the Kladde. - In a new chat session each, give three prompts for the same task "Tests for
TimeRange.Overlaps":- A, imprecise: "Write tests for the appointment check."
- B, precise: as in the second prompt above (goal, rule, constraints, cases, output format).
- C, with example: like B, plus an existing test as a template with the note to take over style and naming.
- Do not copy any answer into the Kladde before you have judged it. Create a table (
docs/copilot-prompt-comparison.md) with one column per prompt and these rows: compiles, uses only existing members, checks the edge (touching), number of cases, naming style, contains invented or superfluous things. - Run the tests of the best answer (
dotnet test, read the exit code) and note whether they all pass, and what you had to change.
How you check: Your table has three filled columns, and you can say in one sentence which building block of the prompt made the biggest difference. An answer that covers all cases without your changes is a lucky hit, not proof: Repeat prompt B in a new session and compare.
Solution 1
There is no expected result; what Copilot writes differs from run to run. Check these points on every answer:
- Edge: Is there a test in which end and start coincide, in both directions, with
Assert.False? If it is missing, the test that catches the most common mistake is missing (W3.1.1). - Invented things: Does a test access members that
TimeRangedoes not have? The compiler shows it. - The same twice: Do three tests check the same thing? More tests are not more checking.
- Indifferent tests: A test that passes with every implementation (say because it checks
Assert.True(true)or only the construction) checks nothing. Delete it. W3.2.2 covers such tests in detail. - Style: Are the names in PascalCase and without underscores, as in the tests from K2?
What you will typically see (as an expectation, not as a measurement): The imprecise prompt A makes Copilot guess the file and the style; it depends most on the context. With B and C you gain control over cases and style. Whether C is better than B depends on your example. That is the point of the exercise: You see which building block works, and where an example says more than words.
Exercise 2
A restructuring in steps. Restructure a small place in the Kladde and record every step.
Task: In IsTaken (K2, lesson 2.5.10) the Kladde checks two lists with the same expression: Appointments.Any(a => a.Time.Overlaps(time))
and Bookings.Any(b => b.Time.Overlaps(time)). Extract the common check into a small helper method without changing the
behavior. If you do not have the code in this form, take a similar place with a duplicated expression.
- New session. Open only the file with
IsTaken. Ask Copilot to explain what the method does. Check the explanation against the code. - Secure. Are there tests for
IsTaken(throughBookAsync)? Are they green? If not, ask Copilot to write tests for the current behavior, and check them as in Exercise 1. Commit them:test(core): secure the behavior of IsTaken. - Change. New session, prompt with goal, constraint "public signature and behavior stay the same, only this method, change nothing else". Read the diff line by line.
- Check. All tests, read the exit code. Commit:
refactor(core): shared overlap check in IsTaken. - Note two sentences in
docs/copilot-prompt-comparison.md: Where did Copilot change more than you asked for? What did you take back?
How you check: There are two commits (tests, restructuring), the tests were green before and after the restructuring, and the diff of the restructuring contains only the
intended change. git diff --stat shows one file, not five.
Solution 2
A restructuring of this size is an example of the five steps. What you typically have to check:
- Step 1: An explanation that says "
IsTakenchecks whether the time is free" is too coarse: It is the wrong way round. The method answers whether the time is taken (truemeans taken). Check that the explanation knows the direction. - Step 2: A test that checks only the success case is no safeguard. You need one case each with an appointment, a booking and touching (end equals start, not taken).
- Step 3: Watch the diff for three forms of error: a flipped condition (
&&instead of||), a changed order (short-circuit evaluation changes only in order, not in result, but check it), and changes outside the method (variables renamed "on the side", changed types). - Step 4: If a test is red after the restructuring, that is the proof that the safeguard was worth something. Take the restructuring
back (
git restore) and start again with a narrower prompt, instead of adjusting the test.
That Copilot changes more than you asked for is the most common observation. The countermeasure is already in the prompt ("only this method") and in the diff that you read. If both are not enough, the step is too big.
What you can do now
- You structure a prompt into goal, context, constraints, output format and example, and put in the rule that fixes the edge.
- You steer the context: open files, selection, references with
@, clean chat histories, and you know that the context can leave the machine. - You tell zero-shot and few-shot apart and choose examples that you have checked yourself.
- You break tasks into small, checkable steps and keep a separate session for each task.
- You use Copilot for generating, explaining, restructuring, documenting and for test data, and check every answer against the Kladde.
In the next lesson Copilot leaves the editor suggestion: Agent mode changes files and runs commands, the command line brings Copilot into the terminal, and MCP connects tools. There the question "What do you approve?" becomes more important than the one about the prompt.
Further reading
- Prompt engineering for GitHub Copilot: structure of prompts, examples, context and chat history.
- Introduction to prompt engineering with GitHub Copilot: module on Microsoft Learn.
- GitHub Copilot Fundamentals, Part 1: learning path on Microsoft Learn, contains the module on prompts.
- Developer use cases for AI with GitHub Copilot: use cases and limits.
- Application card: GitHub Copilot Chat: limits and responsible use of the chat.