MODULE 07

Vibe Coding & AI-Assisted Development

A practical module on coding with AI. Not "AI will replace programmers" hype, not "AI is useless" defensiveness — what actually works, what doesn't, and how to maintain quality when most of your typing is done by something else.

3 lessons·~120 minutes
LESSON 7.1

What vibe coding is (and isn't)

"Vibe coding" is the practice of writing software where most of the code is produced by an AI assistant, while you act as the architect, reviewer, and director. You describe what you want, the AI writes it, you check the vibe — does it look right? does it run? — and iterate. The term is recent; the practice is everywhere.

Treated carelessly, vibe coding produces brittle, opaque code you don't understand. Treated as a discipline, it can make a competent engineer dramatically more productive, especially on the work that used to be tedious: scaffolding, boilerplate, refactoring, test writing.

The spectrum of AI assistance

It helps to know where on the spectrum you're operating:

  1. Autocomplete — the AI suggests the next few tokens. You stay in control of intent. (Copilot, Cursor tab.)
  2. Inline generation — the AI writes a function or block on request. You review, edit, accept. (Claude in an editor, Cursor inline edit.)
  3. Agentic coding — the AI plans, edits multiple files, runs tools, iterates. You set the goal and check the result. (Claude Code, Cursor agent mode, Cline.)
  4. Full vibe coding — you describe the app or feature; the AI produces it end-to-end. You barely read the code; you check the behavior.

Most working engineers operate across all four daily. The skill is knowing which to reach for and when.

When vibe coding shines

When vibe coding fails

The honest middle ground

The most productive engineers using AI today aren't extremists in either direction. They:

This module teaches you the techniques to do this well — to get the productivity gains without the technical debt.

The vibe-coding trap: the moment you stop being able to explain what the code does, you've crossed from "AI-assisted" to "AI-dependent." That's the line. Stay on the right side of it.
LESSON 7.2

Working with AI pair programmers effectively

The same engineer can get 10× different results from the same AI assistant depending on how they use it. This lesson is about the techniques that actually move the needle: what to ask for, what to provide, and how to course-correct.

The single most underrated technique: give it your code

Most "the AI wrote bad code" complaints come down to one thing: the AI didn't see your codebase. It wrote generic code in a vacuum, and generic code doesn't match your conventions, your types, your error patterns, or your tests.

The fix is mechanical: include enough surrounding code that the AI can match the style. When asking for a new function, paste:

Modern editor integrations (Cursor, Claude Code, etc.) do this automatically by sending nearby code. If you're using a chat interface directly, do it manually.

State the constraint before the request

Generic prompts get generic code. Specific prompts get specific code. The difference is usually constraints stated up front.

Weak:

Write a function to deduplicate this list.

Stronger:

Write a function to deduplicate this list, preserving order of first
occurrence. Type-annotated. No external dependencies. Match the
existing style:

  def chunk_lines(lines: list[str], size: int) -> list[list[str]]:
      ...

Function signature: def dedupe(items: list[str]) -> list[str]:

The second prompt cuts down on revision rounds dramatically. You're shifting work from "review and fix" to "specify up front."

Have it explain before it codes

For non-trivial tasks, ask for an explanation of the approach before the implementation:

I need to batch-process these CSV files and merge them, handling
collisions on the "user_id" column by keeping the most recent row.

Before writing code, briefly explain your approach in 3-5 bullets.
Then I'll review and we can adjust before you write it.

This catches misunderstandings cheaply. A wrong approach explained in a paragraph costs you 30 seconds; a wrong approach implemented in 400 lines costs you much more.

Prefer iterative requests over one shots

If you ask the AI to build a feature end-to-end in one shot, you get something you have to review end-to-end. If you ask for the structure first, then each piece, you get a chance to redirect along the way.

The pattern:

  1. "Give me the function signature and types."
  2. "Now write the happy path."
  3. "Add error handling for these cases: ..."
  4. "Write tests covering: ..."
  5. "Refactor to handle the case where ..."

Each step is short and reviewable. The whole feature emerges incrementally with you in the driver's seat.

Use the AI to think, not just type

Some of the highest-value uses of AI in coding aren't generating code at all:

The "constrain the diff" technique

When asking for an edit, constrain how much code can change. Otherwise the AI will helpfully refactor things you didn't ask about and quietly break unrelated tests.

Modify the get_user function to handle the case where the user is
deleted. Do not change anything outside this function. Do not modify
imports, types, or other functions. Show only the diff.

For agentic coding (Claude Code, Cursor agent), this is even more important — the agent has the keys to your repo. Scope it.

When you get bad output, debug the prompt

If you get code that's wrong, your first instinct shouldn't be to edit the code by hand. It should be to figure out why the AI produced that — and fix the prompt. Why? Because the next similar request will produce the same wrong code. Fixing it once at the source compounds.

Common reasons for bad output:

Exercise: Pick a recent task where you used AI to write code. Look at the final code you committed vs. the AI's first draft. What changed, and what would you have had to add to the prompt to get the right code on the first try?
LESSON 7.3

The discipline of AI-assisted code: review, test, refactor

The productivity gain from AI coding is real. The trap is that without discipline, you ship more code faster — but a higher proportion of it is wrong, fragile, or technical debt you don't yet recognize. This lesson is about the practices that keep quality high.

Read everything before you commit

The single rule that separates productive vibe coders from people who eventually rebuild everything from scratch: read every line of AI-generated code before you commit it.

This is not negotiable. Even if it "looks fine." Even if the tests pass. Even if you're tired. The AI confidently produces code that:

If reading the code is too much work, you didn't decompose the task enough. Make the chunks small enough that reading them is fast.

Test what you don't trust

Every piece of AI-generated code falls into one of two buckets:

Use the AI to write the tests. The AI is shockingly good at thinking of edge cases when asked to — much better than it is at handling them in the implementation. Pair generation with adversarial testing in the same conversation:

1. Write a function that does X.
2. Now list 10 edge cases this function should handle.
3. Now write tests for those edge cases.
4. Now run the tests mentally and tell me which fail.
5. Now fix the function.

This loop alone catches a startling fraction of bugs the AI would otherwise commit.

Refactor more than you'd think

AI-generated code is verbose, defensive, and over-decomposed by default. Three small functions where one would do. try/except blocks that swallow errors. Comments explaining the obvious.

Refactoring AI output is part of the workflow, not a separate step:

The good news: you can ask the AI to refactor its own output once you spot the patterns. "Make this less verbose" and "match the style of the file" go a long way.

Use AI to review AI's code

Counterintuitive but effective: have a second AI (or a fresh conversation with the same one) review the code without context, looking for bugs.

You are reviewing a pull request. The author is junior. Read this
function critically. List concerns in order of severity:
- correctness bugs
- security issues
- performance concerns
- maintainability problems
- style nitpicks (skip if the rest is bad)

If you find no issues, say so explicitly.

<code>
{code_block}
</code>

The reviewing AI doesn't have the context of how the code was written. That's exactly what you want — fresh eyes. A surprisingly large fraction of AI-introduced bugs are caught this way.

Maintain a "voice" for your codebase

Long-term, the biggest risk of vibe coding isn't bugs — it's incoherence. Different files start to feel like they were written by different teams, because they kind of were. The AI doesn't have a memory of your codebase's conventions; you have to be it.

Tactics:

Know when to put the AI down

The skill people don't talk about: knowing when to close the AI chat and write the thing yourself.

Signs it's time to stop vibe coding:

That last signal is real and worth listening to. The AI is confident even when it shouldn't be; your unease is information.

Speed is downstream of quality

Counterintuitive but observed: engineers who maintain high code quality with AI tooling end up faster than those who don't. Why? Because cheap code that doesn't quite work creates compounding debugging cost. The "5× productivity boost" of vibe coding evaporates if you spend the saved time chasing bugs you don't understand.

The discipline is the productivity gain. Don't trade it away.

Exercise: For one week, before committing any AI-generated code, do three things: read every line, ask the AI to list potential bugs, and add at least one test. Track how often the bug-finding step catches something you would've missed.

Module 7 wrap-up

Vibe coding is a real shift in how software gets made. The engineers who do it well aren't doing magic — they're applying the same prompt-engineering discipline from Modules 1–6 to the act of writing code, plus a habit of review and test that the AI's confidence can't replace.