AI Dev

Lesson 2

Prompting and context

A model is only as good as the context it sees — and the prompt is only the last part of that context

Lesson 1 established the loop. This lesson is about the step you run most often inside it: asking for a change.

Two things determine what you get back, and most people only think about one of them.

The prompt

What you are asking for, why, and how you will know it worked. Roughly a paragraph.

The context

Every file, rule and earlier message the model can see. Often thousands of lines, and the part you control least deliberately.

A model can only be as good as the context it sees. A perfect prompt against the wrong files produces a confident, well-structured, wrong answer.


The structured prompt

Vague requests get plausible-looking guesses. A structured prompt removes the guessing by answering the questions the agent would otherwise answer for itself.

Text
Role:        Senior <stack> engineer working in <this codebase>.
Goal:        <one line: the exact intent>
Business
Context:     <why this matters — the user problem, the rule,
              downstream impact, who's affected>
Technical
Context:     <relevant files @X @Y, current architecture,
              framework/versions, the pattern to follow>
Current:     <what happens today>
Desired:     <target behavior>
Constraints: <what NOT to touch; perf/security limits;
              no new deps; style rules>
Acceptance:  <checkable success conditions / tests that must pass>
Output:      <plan first & wait for approval | diff only |
              list assumptions first | ...>

You will not write all nine fields for a one-line fix. The point is that each one, when omitted, is a decision you have silently handed to the model.

FieldWhat it prevents
RoleGeneric answers pitched at the wrong level of expertise
GoalSolving a nearby problem instead of yours
Business contextTechnically valid changes that break the actual rule
Technical contextInventing a pattern when one already exists in the repo
Current / DesiredAmbiguity about whether this is a bug or a feature
ConstraintsScope creep, new dependencies, refactors you did not ask for
Acceptance“Done” being a matter of opinion
OutputGetting 400 lines of code when you wanted a plan

Business context is the field people skip

Technical context tells the agent how the code works. Business context tells it what would count as a wrong answer. The second is what stops technically clean, functionally wrong changes.

Text
Goal:        Stop sending the welcome email twice.
 
Business
Context:     Users on the legacy plan get migrated by a nightly job that
             re-runs the signup hook. Sending twice looks like a bug to
             the user and trips our spam threshold with the provider.
             Under no circumstances should a genuinely new signup miss
             the email — a duplicate is embarrassing, a miss is a churn
             event.

That last sentence tells the agent which way to fail. Without it, an agent optimising for "no duplicates" may add a guard that also suppresses legitimate first sends — a strictly worse bug, and one your tests probably do not cover.


Precision beats vague

The same request, twice.

Vague

“Add caching to the user service so it’s faster.”

Which calls? Cached where — memory, Redis, HTTP? Invalidated when? What is “faster” enough? The agent will pick, and it will pick something reasonable-looking and probably wrong for you.

Precise

“Cache getUserProfile() in the existing Redis client (@src/cache/redis.ts), keyed by user id, 5-minute TTL. Invalidate on any write in @src/services/user.ts. Do not touch the auth path. Existing tests must pass and add one for invalidation.”

The precise version names the function, the mechanism, the existing module to reuse, the key, the TTL, the invalidation trigger, the boundary, and the acceptance condition. Every one of those was a decision. You made eight of them instead of zero.


Give the agent a way to check its own work

This is the highest-leverage sentence in the lesson: an agent with a test can iterate; an agent without one can only guess and stop.

1

Name the command

“Run npm test -- user.spec.ts until it passes” gives it a loop. “Make sure it works” does not.

2

Write the test first when the behaviour is subtle

A failing test is an unambiguous specification. It is also the one artefact the agent cannot argue with.

3

Say which checks must stay green

Otherwise “make this test pass” is satisfiable by weakening a different one — and agents do exactly that.


Work in small steps, and watch it

Lesson 1 made the case structurally. In practice it means three habits:

  • One coherent change per turn. If your prompt contains the word "and" twice, it is probably two prompts.
  • Read the diff as it lands, not after twelve files have changed. Cursor shows edits as they happen; that is a feature, not noise.
  • Hit Stop early. The moment an agent starts editing a file you did not expect, stop it. The cost of stopping is one message. The cost of not stopping is a revert and a re-read.

Before you prompt: the checklist

Run this in about ten seconds

  • Can I state what “done” looks like in one sentence?
  • Did I pick the right tool — Tab, Inline, Ask, Agent or Plan?
  • Did I give business context (why) and technical context (@files, the pattern to follow)?
  • Did I state constraints and what not to touch?
  • Did I give it a way to verify — tests, build, or a run command?
  • For a big or ambiguous task, did I use Plan mode first?

Six questions. Any "no" is a prediction about how the diff will disappoint you.


A prompt library

Most work falls into a handful of shapes. Keeping a template per shape removes the blank-page problem and stops you forgetting the constraints field at 5pm.

ShapeThe field that matters most
New featureBusiness context — what would count as wrong
Bug fixCurrent vs Desired, plus a reproduction
RefactorConstraints — behaviour must not change
Test generationAcceptance — which edge cases must be covered
Code reviewOutput — findings only, ranked, no rewrites
Security reviewRole and technical context — the threat model
DocumentationRole — who is reading this and what do they know
Understand before changingOutput — explanation only, change nothing

Two worth writing out, because their failure modes are the sharpest.

Refactor — the danger is silent behaviour change:

Text
Goal:        Extract the retry logic from @src/api/client.ts into a
             reusable helper.
Constraints: Behaviour must be identical — same backoff, same jitter,
             same error types propagated. No new dependencies. Do not
             change any public signature.
Acceptance:  Every existing test passes untouched. If you need to modify
             a test to make it pass, stop and tell me instead.
Output:      Diff only.

That last acceptance line is the whole trick: it converts "tests pass" from something the agent can satisfy by editing tests into something it can only satisfy by preserving behaviour.

Understand before changing — the danger is it starts editing:

Text
Goal:        Explain how session invalidation works across
             @src/auth/session.ts and @src/middleware/auth.ts.
Output:      Explanation only. Change no files. List anything that
             looks inconsistent, and any assumption you had to make.

Context management

Every message, file and rule the model can see occupies the same finite window. Three things follow.

The window fills up across a conversation

A long chat is not a neutral container. Each turn adds the previous answer, the files it touched, and the tool output it produced.

As a chat growsWhat that does to the answer
Earlier turns stay in the windowSuperseded decisions still influence output
Contradictions accumulate“Use Redis” from turn 2 fights “keep it in memory” from turn 9
Signal is dilutedThe relevant file is one of forty things in view
The window overflowsSomething gets dropped — and you do not choose what

The remedy is a fresh chat, not a longer explanation. When an agent starts contradicting itself or reverting its own earlier work, that is the signal. Start again, state the current goal, and reference only the files that matter now.

At-symbols: scoping context deliberately

ReferencePulls inUse when
@fileOne specific fileYou know exactly what is relevant — the default
@folderA directoryA change spans a small, cohesive module
@CodebaseSemantic search across the indexYou genuinely do not know where the code lives
@DocsIndexed external documentationA library’s current API matters
@rule-nameA specific rule fileYou want a manual rule applied this once (Lesson 3)

Reaching for @Codebase on every prompt is the context-dumping anti-pattern. It is a search tool for when you are lost, not a substitute for knowing your repository.


What Cursor indexes, and how to control it

Cursor builds an embedding index of your repository so it can retrieve semantically relevant code rather than relying on filename matches. That index is what @Codebase searches and what the agent falls back on when you have not scoped the context yourself.

You get two controls, and they are not the same thing.

FileEffectStill usable if referenced?Reach for it when
.cursorignoreFull block — hidden from all AI featuresNoSecrets, credentials, sensitive data
.cursorindexingignoreIndex only — excluded from searchYes, when you explicitly reference itLarge generated or vendored files

Both use .gitignore syntax, and Cursor honours your existing .gitignore automatically.

Bash
# .cursorignore — never visible to any AI feature
.env
.env.*
secrets/
**/*.pem
config/production.yaml
Bash
# .cursorindexingignore — keep the index clean, stay referenceable
dist/
build/
coverage/
**/*.generated.ts
public/vendor/

The distinction matters. Putting a huge generated client in .cursorignore means you cannot @ it when you genuinely need it. Putting your .env in .cursorindexingignore means it is still one explicit reference away from the model.


What to leave out

More context is not better context. Each of these actively degrades the answer, by crowding out signal or by teaching the agent the wrong pattern.

Exclude

  • Generated files and build artefacts
  • node_modules and vendored code
  • Unrelated services
  • Historical experiments
  • Deprecated code paths
  • Secrets and config values

Include

  • The files the change will touch
  • One example of the pattern to follow
  • The interface or contract at the boundary
  • The test that defines success
  • Which files are explicitly off-limits

Deprecated code is the subtle one. An agent shown an old pattern and a new one has no reliable way to tell which you prefer, and will often follow whichever appears more often. Deleting dead code is a context-engineering act as much as a housekeeping one.


Load the context before you ask for changes

A workflow worth adopting deliberately: spend the first turn or two building context, and ask for nothing.

Text
Turn 1 — @src/billing/ @docs/billing-rules.md
         Explain how proration works today. Change nothing.
 
Turn 2 — What are the edge cases around mid-cycle plan downgrades?
         Still change nothing.
 
Turn 3 — Now: <the structured prompt for the actual change>

Why it works

The retrieved context is already in the window. By turn 3 the relevant code is loaded, so the change request is answered against real code rather than a fresh, partial retrieval.

You get to audit its understanding for free. If the turn-1 explanation is wrong, you have found a misunderstanding before it became a diff — the cheapest possible moment.

The edge cases surface before implementation. Turn 2 routinely produces a case you had not considered, which then belongs in your Acceptance field.

How to do it

  1. Reference the files and docs with @, and ask for an explanation only
  2. Ask a follow-up about edge cases or invariants — still read-only
  3. Correct anything it got wrong, explicitly
  4. Only then issue the structured prompt for the change

This costs two cheap turns and routinely saves a revert. It pairs naturally with Plan mode for larger work.


Recap

Lesson 3 removes the repetition: instead of restating your standards in every prompt, encode them once as rules the agent inherits.

Check your understanding

1 / 10
business context versus technical context

What does the Business Context field give the agent that Technical Context does not?

Test your understanding

Prof is ready

Prof will ask you questions about Prompting an AI coding agent and engineering its context — not explain it. You'll be surprised what you don't know until you have to say it.

Finished this lesson?

Read through the lesson first (0/20s).