Vibe Coding Fundamentals & AI-Driven Development
"Vibe coding" — describing what you want and letting AI write it — went from meme to daily practice fast. This chapter separates the useful core from the hype: what it is, where it genuinely accelerates you, where it quietly creates liabilities, and the disciplines that keep AI-generated code trustworthy.
AI coding assistants (Claude Code, Cursor, Copilot, and others) can write, explain, and refactor code with you. Used well they're a huge accelerator; used blindly they introduce subtle bugs. This section covers the tools and — more importantly — the habits that make AI-assisted coding safe and productive.
The words you'll hear (in plain terms):
| Term | What it actually means |
|---|---|
| AI-assisted development | coding with an AI helper that suggests, writes, or edits code. |
| pair programming | two 'developers' working together — here, you + the AI. |
| autocomplete vs agent | inline suggestions vs. an AI that edits files and runs commands. |
| verification | checking the AI's output (read it, run tests) instead of trusting it. |
| prompt / context | what you tell the assistant + the code it can see, which shape its output. |
What you need before starting:
- Basic programming experience so you can judge the AI's output.
- The Claude Code intro (C3) pairs well with this section.
- The relevant tool installed (or read along to compare them).
New to the topic? Read this box, then take the chapters in order — each section is tagged essential → expert so you always know the depth you're at.
Learning objectives
- Define vibe coding and AI-driven development, and where each fits.
- Explain the spectrum from autocomplete to fully agentic coding.
- Apply the core loop: intent → generate → verify → refine.
- Name the real risks — trust, security, skill atrophy — and how to manage each.
- Write prompts and set up context that make AI coding tools reliable.
What "vibe coding" actually is essential
Picture the two hours you'd normally lose to a boring task. You need a script that reads a folder of CSVs, merges them, and drops the duplicates. You know how — you just don't want to type it. So you describe it in a sentence, an AI writes forty lines, you run it, it works, you move on. That felt like magic, and it's why "vibe coding" spread from a joke to a daily habit almost overnight: for the first time, describing the what often gets you the how for free.
Here's the honest catch, and it's the whole reason this chapter exists. The word "vibe" is doing something sneaky — it quietly implies you don't have to look. And for that throwaway CSV script, fine, you probably don't. But the same one-sentence-and-ship reflex, pointed at the code that charges a customer's card or checks a password, is how teams ship bugs they can't even find later. The danger isn't the AI writing the code — it's you not reading it. Speed is real; the trap is treating all code as equally safe to skip reading.
The common beginner mistake is to think of vibe coding as a single thing you either do or don't do. It isn't. Think of it instead like a dimmer switch, not an on/off toggle: at one end you read and understand every line; at the other you accept the output blind. Every real task lives somewhere on that dial, and the professional move is choosing where deliberately based on what breaks if the code is wrong. A prototype you'll throw away tomorrow can sit near "blind" — the cost of a bug is that you rerun it. A payment path cannot — the cost of a bug is a chargeback and an incident review.
The term (coined in early 2025) describes coding by describing intent in natural language and letting an AI generate, run, and iterate on the code — often without reading every line. At its loosest it means "trust the vibes, ship what works." That's fine for a throwaway prototype and dangerous for production. The useful reframe: vibe coding is a spectrum of how much you delegate, and the skill is choosing the right point on it for the stakes.
The delegation spectrum essential
Think about how much you'd trust a new intern versus a senior engineer. You'd let the intern draft a first pass and then check it closely; you'd let the senior take a whole feature and only skim the result. You already calibrate trust to competence and stakes every day — you just do it with people. AI coding tools ask you to do the same thing, except the "trust" comes in discrete levels, and if you don't notice which level you've picked, you'll apply the wrong amount of checking.
That's what this section is really about: not the tools, but the setting you're operating them at. An autocomplete that finishes your line is a very different act of trust from an agent that edits twelve files and runs your test suite while you get coffee. Same underlying model, wildly different amount of code you didn't personally write. The mistake almost everyone makes is sliding rightward — handing over more — because it feels productive, without sliding their verification rightward to match. That mismatch is the single most common way AI coding goes wrong.
Here's the counter-intuitive rule that trips people up: more delegation does not mean less work — it means the work changes shape. When you type the code, understanding is a free byproduct of writing it. When an agent types it, you have to buy that understanding separately, by reading and testing. So the far-right end of the spectrum doesn't hand you a finished result; it hands you a draft from a fast, confident, occasionally wrong stranger that you're now on the hook to review. The autonomy is real, but so is the review bill that comes with it. Read the diagram below as a dial you set per task — and remember that setting it higher raises, not lowers, the verification you owe.
AI coding tools sit at increasing levels of autonomy. Knowing which level you're operating at tells you how much verification you owe.
First, what is vibe coding? It means describing what you want in plain English and letting an AI write the code — sometimes without reading every line. This picture shows that "letting the AI do it" is not one thing but a sliding scale of how much work you hand over. Read the line from left to right: the further right, the more the AI does on its own.
- The horizontal line is the scale of delegation — how much you trust the AI to do without you. Each coloured dot is one level, labelled above it, with a short hint below.
- Autocomplete (far left, green): the AI finishes the single line or block you're already typing — the smallest step, like a smarter tab-complete.
- Chat / edit (blue): you ask it questions and it edits code on request — the note "you drive" means you're still steering every move.
- Agent (purple): now it changes many files and even runs commands by itself ("multi-file, runs") — you're supervising, not typing.
- Autonomous (far right, amber): you hand it a whole task and it works largely on its own.
- The line of text at the bottom is the whole point: more delegation → more verification you owe. The more the AI wrote, the harder you must check it before trusting it, because you wrote less of it yourself.
In short: There's no "best" spot on this line — pick the level that matches the stakes. A throwaway script can sit far right; code that ships to real users belongs left, where you check more.
The core loop: intent → generate → verify → refine essential
Imagine ordering food in a language you barely speak. You point at the menu (intent), the kitchen cooks (generate), the plate arrives — and here's the moment that matters — you taste it before you pay (verify), and if it's wrong you clarify and reorder (refine). Nobody sane pays for a dish they haven't tasted just because it looked right through the kitchen window. Yet that is exactly what "accept the AI's code without running it" is: paying for the meal off the photo. This section is about the one discipline that turns AI coding from gambling into engineering.
The loop is worth naming step by step because each step has an owner, and getting the owners wrong is where sessions fall apart. Intent is yours — the tool cannot know what "done" means for you, so vague intent ("make it better") yields vague code, every time. Generate is the tool's job, and it's genuinely good at it. Verify is yours again, and it's the one people quietly drop because generation felt like the finish line. Refine is yours too — and the skill is correcting the intent ("it must reject negative amounts") rather than just barking "no, try again," because a sharper intent is what makes the next lap converge instead of wandering.
Why single out verify as load-bearing? Because it's the only step where you catch the failure mode that makes AI code dangerous: code that is confidently, plausibly wrong. A human beginner writes code that looks shaky, so you check it. An AI writes code that looks polished and reads like a senior engineer wrote it — clean names, sensible structure — while silently mishandling the empty list, the leap year, the concurrent write. The polish disarms your suspicion at exactly the moment you should be most suspicious. Verify is how you refuse to be disarmed: you read the diff, you run it, you run tests, and you scale that effort to the stakes. Skip it and you haven't saved time — you've just moved the bug-finding to production, where it's far more expensive. The entire craft of AI-assisted engineering is, at bottom, the refusal to skip the amber box below.
Every productive AI-coding session, at any autonomy level, runs the same loop. The one step people skip — verify — is the one that separates shipping from shipping bugs.
This is the repeating cycle every good AI-coding session follows, no matter which tool you use. Read the four boxes left to right, then notice the curved arrow underneath that loops back to the start — that's why it's called a loop, not a straight line.
- Intent (first box): you say clearly what you want — the goal, any rules, and what "done" looks like. Vague intent gives vague code.
- Generate: the AI writes the code from your intent. This is the step people think is the whole job — it isn't.
- The straight arrows between boxes show the normal forward flow: intent leads to generate, which leads to verify, which leads to refine.
- Verify (shown in amber, the emphasised box): you check the result — read it, run it, run tests. The red caption warns this step is NOT optional: skipping it means you're "gambling," not coding.
- Refine: you sharpen your request based on what you saw, then the curved arrow at the bottom carries you back to Intent to go round again. Each lap gets the code closer to what you actually meant.
In short: The one habit that separates shipping working software from shipping bugs is never skipping Verify. If you remember one thing from this diagram, remember that the loop always passes through the amber box.
| Step | What good looks like |
|---|---|
| Intent | Specific goal + constraints + "done" criteria — the Chapter 2 prompt discipline, applied to code |
| Generate | Let the tool write it; give it the right context (see §Context below) |
| Verify | Read the diff, run it, run tests, check edge cases — proportional to the stakes |
| Refine | Correct the intent (not just "no, try again"); the loop converges when intent is sharp |
Where it accelerates you — and where it doesn't intermediate
Here's the mental model that predicts almost every case: an AI coding tool is a phenomenally well-read pattern-matcher, not a reasoner. It has seen millions of REST endpoints, config files, and test skeletons, so when your task rhymes with something common, it reproduces the pattern faster and more accurately than you could type it. But when your task is genuinely novel — a tricky invariant, a domain nobody blogs about, a bug whose cause lives three files away — there's no pattern to match, and it does what a confident student does on an exam it didn't study for: it produces something fluent and plausible that may be wrong.
So the useful question before you delegate isn't "can the AI do this?" — it usually looks like it can — but "has the world done this a thousand times before?" If yes, lean in hard: scaffolding a Flask route, writing a batch of unit tests, converting JSON to a dataclass, explaining a gnarly regex someone else wrote. These are where you get the 3× speed-up with almost no risk, because they're both common and cheap to verify. If the task is rare, subtle, or high-stakes, the tool becomes a junior assistant you must supervise closely, not an oracle — useful for a first draft, dangerous as a final answer.
The common trap is a seductive inversion: because the AI handles the hard-looking boilerplate so smoothly, people start trusting it with the genuinely hard stuff too — the architecture, the security boundary, the concurrency model — and relegate themselves to tidying the boilerplate. That's exactly backwards. The table below sorts the terrain; read the right column not as "never use AI here" but as "here you're the engineer and it's the intern, so verify like one is watching over your shoulder."
Read it left to right along the single axis: the one question at the top — "has the world done this a thousand times?" — splits every coding task into two zones.
- The green box on the left is the YES zone: common work the model has seen millions of times — boilerplate, tests, config — and which is also cheap to verify. Both things are true at once, which is what makes it safe to hand over.
- The red box on the right is the NO zone: novel logic, architecture, security, or a bug whose cause is out of view. There's no pattern to copy, so fluent output here can be quietly wrong.
- The labels under the axis tell you the move for each end: on the left you lean in and treat the AI as an accelerator; on the right you supervise it like a junior intern and check every line.
- The payoff is the bottom line: it's the same tool both times — what changes is how much trust the task has earned, and you decide that by how cheaply you can check the result.
In short: Don't ask whether the AI can do a task — ask whether the task is common and cheap to verify. That answer tells you which end of the axis you're on.
| AI coding shines at… | AI coding struggles with… |
|---|---|
| Boilerplate, scaffolding, config | Novel algorithms & subtle logic |
| Familiar patterns in popular languages | Obscure libraries / niche domains (little training data) |
| Tests, docs, refactors with clear intent | Deep architecture decisions |
| Explaining unfamiliar code | Debugging where the root cause isn't in view |
| Prototypes & one-off scripts | Security-critical or high-blast-radius code |
The real risks intermediate
Every powerful tool has a way it hurts you, and knowing it is what separates a professional from an enthusiast. A chainsaw cuts wood fast and takes fingers; the pro doesn't fear it, they wear the guard and keep their hands clear. AI coding is the same. The risks below aren't arguments for going back to typing everything by hand — that ship has sailed and the productivity is real. They're the specific hazards you learn to handle on purpose, so the speed is a gain and not a liability you discover in an incident review.
What makes these risks slippery is that none of them announce themselves. Slow code you notice; a crash you notice. But subtly-wrong code passes the demo, hardcoded secrets sit quietly in the repo until someone scrapes them, and skill atrophy feels great right up until the day the tool is down and you can't debug your own service. The failure is deferred and invisible, which is exactly why it needs a deliberate habit rather than good intentions — you cannot rely on the problem to interrupt you.
One risk deserves calling out because it's the least technical and the most corrosive: skill atrophy. If you use the AI to avoid understanding — accepting code you couldn't have written and wouldn't recognize as broken — you slowly hollow out the very judgment that lets you verify its next answer. It's a doom loop: the less you understand, the less you can check, so the more you must blindly trust. The escape is to use the tool as a tutor, not a crutch — read what you accept, ask it why, and make sure every session leaves you a slightly better engineer, not a more dependent one. The table maps each risk to the concrete move that defuses it.
These aren't reasons to avoid AI coding — they're the things a professional manages deliberately.
| Risk | What it looks like | How to manage it |
|---|---|---|
| Trust / correctness | Confident code that's subtly wrong; passes a glance, fails an edge case | Verify proportional to stakes; tests are your safety net |
| Security | Injected vulnerabilities, hardcoded secrets, unsafe defaults | Review security-sensitive code line by line; scan; never trust generated auth/crypto blindly (T1) |
| Skill atrophy | You can generate it but no longer understand it | Read what you accept; understand before you ship; use it to learn, not to avoid learning |
| Maintainability | Fast-generated code you can't debug or extend later | Own the code as if you wrote it — because you're responsible for it |
| Hallucinated APIs | Calls to functions/packages that don't exist | Run it; the compiler/tests catch these fast |
Making AI coding tools reliable intermediate
Imagine handing a task to a brilliant contractor who started this morning, has never seen your codebase, and will forget everything by tomorrow. That's your AI coding tool on every fresh request. It doesn't know you run tests with make test, that you use httpx not requests, or that the legacy/ folder is radioactive and must never be touched. If you don't tell it, it will guess — competently and confidently — and its guesses will be generically reasonable and specifically wrong for your project. Reliability, then, isn't a property of the model; it's a property of how well you brief it.
This is the same lever you already met in prompting, just pointed at code: the output is downstream of the context and the intent, so the highest-leverage thing you can do is stop leaving them to chance. The good news is that most of this briefing is write-once. A project rules file — CLAUDE.md, .cursorrules, whatever your tool reads — captures your conventions, test command, and no-go zones a single time and re-feeds them into every session automatically, so you're not re-explaining your project on every prompt. The rest is per-request discipline: point at the right files, say specifically what "done" means, and on anything large ask for a plan before a diff, because it is far cheaper to redirect a paragraph than to untangle a twelve-file change.
The single mindset shift that makes all of it click: the prompt is still the program. Vague in, vague out was always true of code; it's just now literally true of the English you type. "Make it output JSON" is under-specified in exactly the way a bad ticket is — so it comes back under-specified. "Add a --json flag that prints the result as JSON, and include a test" states a contract and an acceptance check, so the first generation is something you can verify rather than merely read. You're not chatting with the tool; you're writing a spec, and the sharper the spec, the fewer laps around the loop you take. The habits in the table below are just that principle broken into moves you can reuse across every tool in this module.
Tool output quality is mostly a function of the context and intent you give it — the same lesson as prompting (Chapter 2) and Claude Code's CLAUDE.md (C3). These habits transfer across every tool in this module.
| Habit | Why it works |
|---|---|
| Give it your conventions | A project rules file (CLAUDE.md, .cursorrules, etc.) stops it guessing your style, test cmd, and no-go zones |
| Be specific about intent | "Add a --json flag that prints the result as JSON, with a test" beats "make it output JSON" |
| Point at the right files | Relevant context in, relevant code out; irrelevant context dilutes it |
| Ask for a plan first on big changes | Cheaper to redirect a plan than a 12-file diff (C3) |
| Tell it to verify | "Run the tests and fix failures before finishing" turns a generator into a checker |
Common pitfalls advanced
The mistakes below all share one root, and spotting it is worth more than memorizing the list. Every pitfall here is a version of the same slip: trusting output in proportion to how good it looks rather than how well it's been checked. AI-generated code looks good by default — that's what the model is optimized for — so your eyes hand out trust the code hasn't earned. Almost everything that goes wrong in AI-assisted coding is that gap between looks right and is verified right.
Notice that these aren't exotic edge cases; they're the boring, everyday reflexes of a busy engineer under deadline. Skipping the test because the demo worked. Reusing prototype-grade trust on code that's now in front of customers. Accepting a clever block you don't fully follow because it compiles and you're tired. None of them feel reckless in the moment — each feels like reasonable efficiency — which is precisely why they're pitfalls and not just "bad practices." The fix is never "try harder"; it's a concrete habit that fires whether or not you're paying attention: a test that must pass, a rules file that's always loaded, an explain-it-back gate before you merge. Read the table as a set of tripwires to install, not virtues to aspire to.
| Pitfall | Fix |
|---|---|
| Skipping the verify step | Read, run, and test proportional to the stakes — always |
| Prototype-grade trust on production code | Match rigor to blast radius; own every shipped line |
| Trusting generated auth/crypto/security code | Review it line by line; scan; never ship it unread (T1) |
| Vague intent ("make it better") | Specific goal + constraints + done-criteria |
| Accepting code you don't understand | Understand before you ship; you own it either way |
| No project rules file | Give the tool your conventions once, reuse every session |
Exercises advanced
Exercise V1.1 — Locate yourself on the spectrum
Context: Knowing your own delegation habits is the first step to calibrating them. Placing real tasks on the spectrum reveals whether you over- or under-verify for the stakes.
Your task: For three recent coding tasks, place each on the delegation spectrum (autocomplete → autonomous) and say what verification the stakes actually demanded.
Requirements:
- Place each of three real tasks on the autocomplete–autonomous spectrum
- State the verification level the stakes actually demanded for each
- Judge whether you were over- or under-verifying on any of them
- Tie the verdict back to how cheaply each result could be checked
💡 Hint: Compare the autonomy you used against the verification the stakes deserved — the gap is your miscalibration.
Exercise V1.2 — Run the loop deliberately
Context: The fastest way to feel why verify is non-negotiable is to run the same feature twice: once accepting the first output unread, once through the full loop. The difference is usually a hidden edge-case bug.
Your task: Pick a small feature and do two passes — one accepting the AI's first output unread, one running the full intent→generate→verify→refine loop — then compare.
Requirements:
- Run pass 1: accept the first generation without reading it
- Run pass 2: run the full loop with real verification and refinement
- Compare on bugs, edge cases, and how well each matched your actual intent
- Draw the conclusion about why the verify step is non-negotiable for anything real
💡 Hint: The unverified pass usually looks fine and hides one edge-case bug — the loop catches it exactly at the verify step.
Show what to look for
The unverified pass usually looks fine and hides an edge-case bug or a mismatch with what you meant. The verified loop catches it in the "verify" step — which is exactly why that step is non-negotiable for anything real.
Exercise V1.3 — Write a project rules file
Context: A project rules file is the reusable context that makes any AI coding tool behave like it knows your project. Writing one now powers the rest of the V2–V5 exercises.
Your task: For a project you know, write the rules file you'd give any AI coding tool.
Requirements:
- Include the test command and the lint command
- Include at least two project conventions
- Include one ‘never touch this’ rule
- Keep it reusable — this same file will power the V2–V5 exercises
💡 Hint: Encode your project's specifics (commands, conventions, no-go zones), not generic best practices the tool already knows.
🪜 Practice ladder beginner → industry
Six graded exercises, easy to real-world. Try each before opening its solution.
Context: Every AI coding workflow runs the same loop, and each step has one thing the tool cannot supply for you. Naming them is how you spot which step you're skipping when things go wrong.
Your task: State the four steps of the vibe-coding core loop and, for each, name the one thing you must supply that the tool cannot.
Requirements:
- List the loop: intent → generate → verify → refine
- For intent, supply a precise, testable description of ‘done’
- For generate, supply the context (files, constraints, examples) the model needs
- For verify, supply the judgment on whether it meets intent and is correct
- For refine, supply the specific correction, not ‘try again’
- State that the loop fails when you skip verify and let generated code accumulate unchecked
💡 Hint: The tool owns generation; you own intent and verification — map each step to which side supplies it.
Show solution
The loop is intent → generate → verify → refine.
| Step | You must supply |
|---|---|
| Intent | A precise, testable description of what ‘done’ means |
| Generate | The context (files, constraints, examples) the model needs |
| Verify | Judgment: does this actually meet the intent & is it correct? |
| Refine | The specific correction, not ‘try again’ |
The tool generates; you own intent and verification. The loop fails when you skip verify and let generated code accumulate unchecked.
Context: Delegation should scale with how cheaply you can verify the result. Matching the level of autonomy to the task is the difference between acceleration and a mess.
Your task: For autocomplete, chat/scoped-edit, and autonomous-agent modes, give one task each where that level is the right amount of delegation.
Requirements:
- Autocomplete: a task verified instantly as you read it (e.g. familiar boilerplate)
- Chat/scoped edit: a bounded change you can diff and review in one pass (refactor a function, write a test)
- Autonomous agent: a well-specified, low-risk, test-covered multi-file chore with contained blast radius
- State the principle: delegation scales with how cheaply you can verify — high autonomy on unverifiable work is where it goes wrong
💡 Hint: Pick the level by asking how fast you can check the output, not how much you'd like to hand off.
Show solution
| Delegation level | Good-fit task | Why |
|---|---|---|
| Autocomplete (line/block) | Filling boilerplate you already know the shape of | Verification is instant — you read the line as it appears |
| Chat / scoped edit | Refactor a function, write a test, explain code | Bounded change you can diff and review in one pass |
| Autonomous agent | Well-specified, low-risk multi-file chore (rename, migrate a pattern) with tests | Verifiable by the test suite; blast radius contained |
Principle: delegation should scale with how cheaply you can verify the result. High autonomy on unverifiable work is where vibe coding goes wrong.
Context: The most dangerous vibe-coding pitfall is accepting code you don't understand: it compiles, the demo works, it merges — and no one can debug it later. The guard is a concrete habit, not good intentions.
Your task: Pick the most dangerous common pitfall and describe the concrete habit that guards against it.
Requirements:
- Name the pitfall: accepting code you don't understand
- Describe the ‘explain-back’ gate before accepting a non-trivial generation
- The gate requires you to state what it does line-by-line, what inputs break it, and why this approach over the obvious alternative
- If you can't explain it, you don't merge — you ask the tool to explain, add a pinning test, or rewrite it simpler
- Explain that the habit turns generated code into understood code, the only kind you can maintain
💡 Hint: The test is whether you could defend every line in review as if you'd typed it yourself.
Show solution
Most dangerous pitfall: accepting code you don't understand. It compiles, the demo works, so it merges — and no one on the team can debug it later or spot that it silently mishandles an edge case.
Guard: the ‘explain-back’ gate. Before accepting a non-trivial generation, require yourself to state (a) what it does line-by-line, (b) what inputs break it, and (c) why this approach over the obvious alternative. If you can't, you don't merge it — you ask the tool to explain, add a test that pins the behavior, or rewrite it simpler.
The habit turns generated code into understood code, which is the only kind you can maintain.
Context: A weak prompt costs you four rounds of iteration; a reliable prompt front-loads the verification criteria so the first generation is already checkable. Seeing the before/after makes the payoff concrete.
Your task: Rewrite the weak request ‘write a function to parse the file’ into a reliable prompt and explain what each addition buys.
Requirements:
- The rewrite pins the signature and return type (removes shape ambiguity)
- It specifies explicit error behavior for the edge cases you'd otherwise re-prompt for
- It encodes non-functional constraints (e.g. stream, don't load whole file)
- It asks for tests so verify becomes a one-command check, not a read-through
- Explain what each addition buys in fewer iteration rounds
💡 Hint: A reliable prompt states the contract and the acceptance test up front so the first output can be checked, not just read.
Show solution
Before: ‘write a function to parse the file’ — ambiguous format, no contract, no error behavior. You will iterate 4 times.
After:
Write parse_orders(path: str) -> list[Order].
- Input: a CSV with headers id,customer,total_cents (total_cents is an int).
- Return a list of Order dataclasses (define it).
- Skip rows with a missing id; raise ValueError on a non-integer total_cents.
- Do not read the whole file into memory; stream line by line.
- Include a docstring and 3 pytest cases: happy path, missing id, bad total.| Addition | Buys |
|---|---|
| Signature + return type | Removes shape ambiguity |
| Explicit error behavior | Pins the edge cases you'd otherwise re-prompt for |
| ‘stream line by line’ | Encodes the non-functional constraint (memory) |
| ‘3 pytest cases’ | Makes verify a one-command check, not a read-through |
A reliable prompt front-loads the verification criteria so the first generation is checkable.
Context: When a team adopts AI coding tools, the risk isn't slower typing — it's unowned, untested, unreviewable code slipping in under ‘the AI wrote it.’ A short PR standard keeps the velocity without the erosion.
Your task: Write the 5-point PR standard that keeps velocity without eroding code quality as a team adopts AI coding tools.
Requirements:
- Author owns it fully — ‘the AI wrote it’ is not a review defense
- Tests are mandatory for generated logic (generation makes them cheap)
- No unexplained code — if a reviewer asks ‘why?’ and the author can't answer, it doesn't merge
- Diffs must be reviewable — reject giant agent dumps, require scoped commits
- Secrets & licensing check on generated code
- Explain that the standard keeps the gain (fast authoring) while blocking the loss (unowned/untested/unreviewable code)
💡 Hint: Each rule targets one specific way generated code erodes quality — ownership, testing, understanding, reviewability, and provenance.
Show solution
- Author owns it fully. ‘The AI wrote it’ is not a defense in review; the human author is accountable for correctness as if hand-written.
- Tests are mandatory for generated logic. Generation makes writing tests cheap, so there is no excuse — behavior must be pinned.
- No unexplained code. If a reviewer asks ‘why this?’ the author must answer; if they can't, it doesn't merge.
- Diff must be reviewable. Reject 2000-line agent dumps; require scoped, reviewable commits even if generated.
- Secrets & licensing check. Scan generated code for hard-coded secrets and verbatim-copied licensed snippets.
The standard keeps the gain (speed of authoring) while blocking the loss (unowned, untested, unreviewable code).
Context: ‘Works in the demo’ optimizes for the happy path; production is defined by the paths the demo never hit. Rescuing a vibe-coded prototype is an assess-then-harden job, not a blind rewrite.
Your task: Give the assessment plan and hardening checklist for taking a vibe-coded prototype that demos perfectly to production.
Requirements:
- Assess first: map what exists, list actual features, and separate understood parts from ‘magic’ (highest risk)
- Find the missing edges the demo skipped: bad input, empty state, concurrency, network failure, large inputs
- Harden across correctness (tests over real behavior before changing anything), input handling, secrets/config, failure modes, observability, and data safety
- State the lesson: vibe coding defers the verification and edge-case work — budget for it explicitly instead of trusting the demo
💡 Hint: Don't rewrite blindly — pin current behavior with tests first, then harden the paths the demo never exercised.
Show solution
Reality: ‘works in the demo’ optimizes for the happy path; production is defined by the paths the demo never hit. Do not rewrite blindly — assess, then harden.
- Map what exists: read the code, list the actual features, and identify which parts are understood vs magic. Magic parts are highest risk.
- Find the missing edges: what happens on bad input, empty state, concurrent use, network failure, and large inputs? Prototypes almost never handle these.
| Hardening area | Action |
|---|---|
| Correctness | Add a test suite over real behavior before changing anything |
| Input handling | Validate & sanitize all external input; define error responses |
| Secrets/config | Move hard-coded keys to config; rotate anything committed |
| Failure modes | Timeouts, retries, graceful degradation on dependency failure |
| Observability | Logging + metrics so prod failures are diagnosable |
| Data safety | Backups/migrations; no destructive ops without guards |
Lesson: vibe coding is excellent for reaching a working prototype fast; the production cost is the verification and edge-case work it deferred. Budget for that explicitly instead of trusting the demo.
✓ Checkpoint — you can move on when you can…
- Define vibe coding vs AI-assisted engineering and when each fits.
- Place a tool/task on the delegation spectrum and its verification burden.
- Run the intent→generate→verify→refine loop and explain why verify is essential.
- Name the key risks and how you'd manage each.
- Set up context/rules that make an AI coding tool reliable.
Knowledge check check yourself
The lesson frames the intent -> generate -> verify -> refine loop and calls one step the "load-bearing" one. Which step, and why is skipping it described as "gambling, not coding"?
Show answer
According to the delegation spectrum, why does moving further right (toward autonomous agents) increase the verification you owe rather than decrease your workload?