AI Dev
Lesson 1The collaboration contract
What you stay responsible for, and the loop that keeps an agent from running away with your codebase
An agent will write more code in ten minutes than you can carefully read in an hour. That single fact drives every practice in this course.
Everything here follows from one rule.
The one rule
Treat AI output as a first draft you guide and verify.
Cursor is a collaborator under your control, not an autopilot.
Tool choice, prompting, context, rules, review — all of it is machinery for making that rule survive contact with a real codebase.
Why the rule exists
The rule is not caution for its own sake. It follows from an asymmetry in the economics of the tool.
Generation
Near-free and near-instant. Cost barely rises with the size of the change. Producing 800 lines is not meaningfully harder for the model than producing 80.
Verification
Paid entirely by you, in attention, and it scales with the size of the change — worse than linearly once a diff spans several files.
Two consequences follow, and they explain most of what looks like fussiness later in this course.
Diff size is the variable you actually control. You cannot make the model more careful by asking. You can make the diff small enough that careless output is obvious. This is why "small steps" appears in nearly every practice here — it is the only lever that reliably moves verification cost.
Nobody is accountable but you. The model has no stake in the outcome, cannot be on call, and will not be in the incident review. Merging a change transfers responsibility for it to you, permanently and completely, regardless of who typed it.
The litmus test
A change is finished when you can do three things without the tool open.
1 · Explain it
Say what the code does, in your own words, to a colleague who has not seen it.
2 · Reason about its edges
Name what breaks it: nulls, empty collections, concurrency, scale, hostile input.
3 · Extend it
Add the next requirement yourself, without going back to the agent.
Fail any one and the change is not done — no matter how green the checks are.
Why green checks are not enough
Consider a discount calculator an agent produced. It compiles, it is typed, and its tests pass:
export function applyDiscount(price: number, percent: number): number {
return Math.round(price * (1 - percent / 100) * 100) / 100;
}test('applies 20% off', () => {
expect(applyDiscount(100, 20)).toBe(80);
});Green. Now run the litmus test.
| Test | Result |
|---|---|
| Explain it | Fine — subtract a percentage, round to two decimals. |
| Reason about its edges | Fails. What does percent = 120 do? It returns a negative price. What about NaN, or a float like 0.1 + 0.2 in money? None of this is handled or rejected. |
| Extend it | Fails. Add stacked discounts or currency and you discover the function has no opinion about rounding order — a real source of money bugs. |
The test suite proved the code does something. It did not prove the code is right, and it certainly did not prove you could maintain it at 2am when a customer is charged a negative amount.
The loop
Agents drift when handed a large task and left alone. The fix is structural: work in small steps and put a verification gate after each one.
Two properties matter more than the exact boxes:
- Every path back leads to a smaller step, not a bigger prompt. When a diff goes wrong, revert and narrow the scope. Writing a longer instruction and retrying is the reflex to resist.
- The gates are machine-checkable. "Tests / build / lint pass" is something the agent can run and loop on unaided. "Looks fine" gives it nothing to iterate against.
Stage by stage
Understand the task
Before any prompt: can you state what “done” looks like in one sentence? If not, you are not ready to delegate — you are ready to investigate. Use Ask mode, which changes nothing on disk.
Plan the approach
For anything big or ambiguous, get a plan before code exists. A wrong plan costs one message to fix; a wrong implementation costs a revert and a re-read. This is the cheapest correction point in the whole loop.
Small step, scoped context
One coherent change. Not “build the feature” but “add the validation schema and wire it into this one route”. Scoped context means the specific files it needs, not the whole repository — the subject of Lesson 2.
The three gates
Diff on track is your judgement — is it solving the problem you posed, in the shape you expected? Tests / build / lint is the machine’s. More to do asks whether this was one step of several. Only the middle gate can run without you.
Commit — small and reviewed
A commit is a claim that you understand and stand behind the change. Small atomic commits with meaningful messages are what make an AI-heavy branch reviewable at all.
Right tool for the job
Cursor is not one feature. Reaching for Agent every time is the most common way to spend tokens and attention on work that Tab would have finished in a keystroke.
| Mode | Reach for it when | Writes to disk? | You stay in control by |
|---|---|---|---|
| Tab / autocomplete | The next few lines follow obviously from context | Only what you accept | Reading before you accept |
| Ask | You want to understand code, not change it | No | Nothing can go wrong — it is read-only |
| Plan | The task is big, ambiguous, or crosses modules | No | Approving the plan before any code exists |
| Agent | The change is scoped and you can state “done” | Yes | Small steps, checkpoints, Stop early |
| Background agents | Work is parallelisable and you are busy elsewhere | Yes, on its own branch | Reviewing the branch like any external PR |
| Bugbot / AI review | A diff is ready and you want a second pass | No — it comments | Verifying its findings; it hallucinates too |
The rule of thumb: the more autonomy you hand over, the more specific the definition of “done” has to be before you start. Tab needs none. An agent turned loose on “improve the auth module” needs one badly, and will invent its own if you do not supply it.
Picking a mode, worked
| The task in front of you | Mode | Why |
|---|---|---|
| Finishing a switch statement you are mid-way through | Tab | Intent is already on screen |
| “Why does this cache invalidate on write?” | Ask | You want an answer, not an edit |
| Migrating auth from sessions to JWTs | Plan first | Crosses modules; the design decisions are yours |
| Adding one validated field to an existing endpoint | Agent | Scoped, and “done” is one sentence |
| Bumping a dependency across 40 call sites | Background agent | Mechanical, verifiable, and slow to do by hand |
| A colleague’s PR that touches payments | Bugbot and a human | Second pass is useful; it does not replace review |
What you never delegate
Some decisions do not become cheaper when an agent is available, because their cost was never in the typing.
Architecture
High-level and component-level design. The agent can draft inside your boundaries; it should not draw them.
Product trade-offs
What to build, what to cut, what to defer. The model has no access to your users or your roadmap.
Security decisions
Threat model, authz boundaries, what counts as sensitive. Lesson 4 covers why agents relax these by default.
Correctness boundaries
What “right” means for this system, and which invariants must never break.
Engineering standards
The conventions the codebase holds to. You set them once — Lesson 3 shows how to make the agent inherit them.
Everything else
Is fair game to delegate — under the loop above, with a diff you read.
A useful split when starting a system: draw the high-level and component diagrams yourself, plan with the agent, break the plan into sub-problems, then let the agent execute sub-problems one at a time. The plan lives in a file the agent can read; your own view of status and priorities can live wherever suits you.
Where AI assistance degrades
Model quality is not uniform across tasks. Three areas stay stubbornly hard, and knowing them tells you when to stop delegating and start thinking.
Complex runtime-state debugging
The bug lives in state the model cannot see — a race, a stale cache, a connection pool under load. It will confidently propose fixes for the code in front of it, which is not where the problem is. Reproduce and instrument first; bring the agent the evidence, not the mystery.
Architecture and system design
These are trade-off decisions against constraints the model does not hold: your team’s size, your latency budget, what you are willing to operate at 3am. It will produce something plausible and generic, which is the worst kind of architecture.
Legacy brownfield code
Undocumented behaviour that some caller depends on looks exactly like a bug worth cleaning up. Agents optimise for clean code and will happily “fix” a load-bearing quirk. Here, small steps and real tests are not optional.
Anti-patterns
Each of these is a real habit with a real replacement.
| Anti-pattern | Do instead | Why it is tempting |
|---|---|---|
| “Vibe coding” — accepting output you cannot explain | Guide and verify; own the merge | It works, and reading is slower than accepting |
| Blindly accepting a large diff | Read it; keep changes small; split the PR | The diff already exists; splitting feels like waste |
| Letting the agent run away on a huge task | Plan first, small steps, hit Stop early | Watching it work feels like progress |
| Prompt-and-pray on a vague request | Structured prompt with acceptance criteria | Sometimes it guesses right, which reinforces it |
| Context dumping — everything, every chat | Specific @files; fresh chats; let it search | More context feels safer than less |
| No verification path | Tests, build or lint the agent can loop on | Writing the test feels like extra work |
| One giant rules file | Small, scoped .cursor/rules/*.mdc | One file is easier to write than five |
| Auto-installing AI-suggested packages | Verify every dependency against the real registry | The import looks plausible and the name sounds real |
| Skipping human review on security code | Never skip it for auth, payments or data | The automated checks were green |
| Trusting self-reported speedups | Measure throughput, stability and rework | It genuinely feels faster, and often is, in part |
Measuring whether it is actually working
The last anti-pattern deserves its own section, because it is the one that misleads whole teams rather than individuals.
AI tooling reliably increases the number of pull requests. That is a real effect and it is easy to measure, which is exactly why it gets reported on its own. Throughput in isolation cannot distinguish between these two situations:
A real speedup
PRs up. Change-failure rate flat. Rework flat. Time-to-restore flat. You are shipping more of the same quality.
A loan
PRs up. Change-failure rate up. Reverts and follow-up fixes up. You are shipping work that will come back, on someone else’s calendar.
Track at least one stability signal and one rework signal alongside volume. Throughput plus stability plus rework is the smallest honest set.
Recap
Lesson 2 takes the step you will run most often — the prompt — and shows how to structure it and what context to feed it.
Check your understanding
1 / 10The litmus test says a change is done only when you can do three things without the tool. Which is NOT one of them?