[
  {
    "id": "vibe-coding",
    "title": "Vibe Coding\nwith Confidence",
    "subtitle": "The Vibecoder's Handbook, from idea to production",
    "tagline": "Build Apps That Work Beyond the Demo.",
    "description": "Plan it. Build it. Debug it. Ship it. Scale it. With AI as your engineer.",
    "author": "Mahmoud Zalt",
    "authorRole": "Principal AI Architect",
    "status": "available",
    "url": "https://zalt.me/guides/vibe-coding",
    "coverImage": "/images-optimized/blog/auto/vibe-coding-art-medium.webp",
    "edition": "2026 Edition",
    "version": "v0.1",
    "parts": [
      {
        "id": "intro",
        "title": "Introduction",
        "chapters": [
          {
            "slug": "start-here",
            "title": "Start Here",
            "label": null,
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/intro/start-here",
            "content": "---\ntakeaway: Idea to lasting software\nshare: \"Anyone can get AI to build an app in a weekend; almost nobody keeps it alive a month later. This handbook hands you the system that closes that gap: idea to live software, the AI building and you in control.\"\n---\n{/* KEEP: hook = anyone can build an app with AI, almost nobody keeps it alive; the difference is a system, not talent or code. */}\n\nAnyone can get AI to build an app in a weekend. Almost nobody can keep that app alive a month later. The difference is not talent, and it is not code. It is a system, and this book hands it to you: idea to live software, AI doing the building, you in control.\n\n{/* KEEP: pain line (breaks in ways you can't explain, not sure where anything lives) sets up the mind-map motif. Mind map = the engineer's mental picture: what pieces exist, where each fits, how they talk. AI owns the internals; reader zooms in only on sensitive areas (payments, user data, security). */}\n\n## You only need a mind map\n\nIf you have built with AI before, you know the wall: it breaks in ways you cannot explain, and you are not sure where anything even lives. Here is what nobody tells you: you do not need every line in your head, no engineer has that. You need the picture an engineer carries: what pieces your app is made of, where each one lives, and how they talk to each other.\n\nThe insides of each piece are AI's job. Yours is knowing each piece works, and zooming in only where it is sensitive: payments, user data, security.\n\n{/* KEEP: this is the book's PROMISE/solution (not a consequence of the mind map). What you walk away with: clarity of a modular layout -> confidence to add or change a feature without breaking the rest (no bug-whack-a-mole loop) -> and you stay fast because AI does the QA honestly, not rubber-stamping. This is the core payoff; frame it forward-looking. */}\n\n## Move fast, break nothing\n\nHere is what the book gives you: the clarity to lay your app out as modular pieces, so you can add or change one feature confident the rest still stands. No more the loop where every fix breaks two things you thought were done.\n\nAnd you stay fast, because AI does the checking. We show you how to put it on QA duty, testing its own work honestly instead of rubber-stamping it, so speed never costs you trust.\n\n{/* KEEP: the full lifecycle path in walk order; NOT a tutorial / prompt collection / coding course; the book covers what AI cannot decide (what to build, in what order, how to keep it alive). */}\n\n## What this is\n\nThe full path from idea to live software, in the order you actually walk it: plan, set up, automate, architect, build, debug, test, harden, secure, protect, ship, operate, scale. At the end you have real software you can maintain and grow, plus a repeatable way to build the next one.\n\nIt is not a tutorial, not a prompt collection, and not a coding course. AI writes the code. This book covers what AI cannot decide for you: what to build, in what order, and how to keep it from falling apart.\n\n{/* KEEP: two readers (idea + no coding background, daily AI builder tired of fragile output); explicitly NOT for people already running production systems. */}\n\n## Who it's for, and who it's not\n\n- **You have an idea and no coding background.** You can ask AI for software, but nobody taught you how software should be built. This closes that gap.\n- **You already build with AI daily.** Speed is not your problem anymore, fragile and unmaintainable output is. This gives you a workflow designed for AI, not old habits with AI bolted on.\n\nIf you already run production systems for a living, you are not the audience. Hand this to the person who keeps asking you how.\n\n{/* KEEP: thesis = old best practices assumed humans were the bottleneck; the book's question: if you started today knowing AI exists, how would you build? Knowing which principles survive is the advantage. */}\n\n## Start from scratch\n\nMost \"best practices\" exist because humans were the bottleneck: a person typing, reviewing, and remembering every line. That world is gone, yet nearly everyone still forces AI into workflows built for it.\n\nThis book asks the only question that matters now: if you started today, knowing AI exists, how would you build software? Some old principles survive that question. Many do not. Knowing which is which is the whole advantage.\n\n{/* KEEP: app = output, the system that produces it = the real product; that system is the part nobody open-sources; the book hands it over. */}\n\n## You're building the factory, not the car\n\nThe app is the output. The real product is the system that produces it: the workflows, rules, and checks that make AI ship production-ready work every time. The car is the easy part; the factory is where the leverage lives.\n\nThat factory is the part nobody open-sources, because it is a real edge. This book hands it to you, written down, in the order you can easily follow."
          },
          {
            "slug": "how-to-read",
            "title": "How to Read it",
            "label": null,
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/intro/how-to-read",
            "content": "---\ntakeaway: Read in build order\nshare: \"How to read this handbook: it runs in the exact order you build real software, each stage producing the one thing the next stage needs, so you are never stuck starting a step without what it takes to finish it.\"\n---\n{/* KEEP: this page = the manual (job 6). Concrete reader-situation lead-in (you want the lay of the land before starting), NOT the old \"most technical books are reference shelves\" meta opener. Core point: the book runs in build order, each stage feeds the next, so you're never stuck. */}\n\nBefore you build anything, you want the lay of the land: where to start, what you need, and what order to move in. This book runs in the exact sequence you build real software, each stage producing the one thing the next stage needs. Read it in order and you are never stuck starting a stage without what it takes to finish it.\n\n{/* KEEP: the one-screen map of all 13 stages, each with its concrete output in hand; output of one stage = input of the next, which is why the order matters. */}\n\n## Thirteen stages, one path\n\nEvery part of this book is one stage of a real software project, and every stage ends with something concrete in your hands:\n\n1. **Plan:** a spec your AI can build from.\n2. **Set Up:** a machine and an AI agent ready to produce code.\n3. **Automate:** the operating system you and your agents run everything from.\n4. **Architect:** a codebase layout your AI can extend without rewrites.\n5. **Build:** a real, running app.\n6. **Debug:** the skill to unstick your agent instead of abandoning the build.\n7. **Test:** a safety net that catches breaks before your users do.\n8. **Harden:** the rough edges rounded off into a real product.\n9. **Secure:** an app that defends its data, accounts, and surfaces.\n10. **Protect:** user data handled responsibly and within the law.\n11. **Ship:** your software live on real infrastructure.\n12. **Operate:** a running system you can see into and keep healthy.\n13. **Scale:** growth without breaking, and knowing your limits.\n\nEach stage's output is the next stage's input. That is the whole reason the order matters.\n\n{/* KEEP: fresh-machine assumption (nothing pre-installed, set up when needed); the only two prerequisites = a computer + an AI coding agent; the agent is your teacher, the book is your judgment. */}\n\n## All you need is a computer and an agent\n\nThe book assumes a fresh machine, maybe a laptop you unboxed today. Everything else, the editor, the languages, the accounts, the hosting, gets set up exactly when you need it.\n\nTwo things before chapter one:\n\n- A computer running Windows, macOS, or Linux.\n- An AI coding agent. We help you pick one in Set Up; until then, just read.\n\nYour agent is your teacher, this book is your judgment. It tells you what to ask for, in what order, and how to know the answer is good.\n\n{/* KEEP: this section = ORDER/sequence (not reading mode, that's the next section). Go through the stages in order on the first pass because each stage depends on the prior stage's output; afterward use it as a reference, jumping to the stage where you're stuck. Title must NOT clash with \"Build as you read\" (don't both say \"read\"). */}\n\n## Follow the stages in order\n\nGo through the book in order on your first pass. Because each stage needs what the last one produced, jumping ahead means starting a stage empty-handed.\n\nFrom the second visit on, use it like a manual: a deploy fails or a database buckles, go straight to that stage and skim.\n\n{/* KEEP: this is a build-along book, NOT read-only. Every chapter ends in one concrete action + copy-paste prompts (senior judgment). WHY the web/ebook version beats print: you read on the same screen as your agent, copy each prompt straight across, and build as you go; a paper copy leaves you only reading, the one thing this book is not for. Keep the agent open beside the book so the app and your understanding move together. */}\n\n## Build as you read\n\nThis is not a read-only book. Every chapter ends with one concrete thing to do, and most hand you a ready-made prompt to paste straight to your agent, loaded with the judgment a senior engineer would bring but you could not write yourself yet.\n\nThat is why the web and ebook versions beat print here. You read on the same screen as your agent, copy each prompt straight across, and build as you go. A paper copy would leave you only reading, the one thing this book is not for.\n\nSo keep your agent open beside the book. Finish a chapter and your app has moved forward, not just your understanding.\n\n{/* KEEP: the learning-in-the-AI-era philosophy + why the book is shaped this way. AI changed how you learn, so the book is deliberately SLIM: only the judgment and order that matter, no filler, covering everything a 1000-page book would; your AI supplies depth ON DEMAND (hand it a chapter, ask your own follow-ups). That's why there is NO printed/PDF version, learning is now interactive and collaborative with your AI. Plus site-only features print can't give: progress tracking, notes where they matter, one-click copy of any prompt or the whole chapter to feed your agent, discussions, and contributing edits/suggestions to the content itself. Do not cut the \"no PDF / slim on purpose / collaborative\" trio. */}\n\n## Short by design, deep on demand\n\nThis book is deliberately short. AI changed how you learn: you no longer need a thousand pages spelling out every detail, because your AI can explain any part, in your own words, the moment you ask. So we kept only the signal, the judgment and the order that actually matter, and let your AI supply the depth on demand.\n\nThat is why there is no thick PDF to print. Learning is interactive now, so the book is built to be read beside your agent: hand it a whole chapter to go deeper, then ask your own follow-up questions until it clicks for you.\n\nAnd because it lives on the page, it does what paper cannot: it tracks your progress, lets you leave notes where they matter, and copies any prompt or a full chapter in one click to feed your agent. You can also join the discussion on any chapter, and suggest edits to the content itself."
          },
          {
            "slug": "the-author",
            "title": "About the Author",
            "label": null,
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/intro/the-author",
            "content": "---\ntakeaway: Every move has a reason\nshare: \"Every vibecoding guide has the same gap: one narrow slice, vague, or long on what and silent on why. Here is the track record behind a handbook where every move carries its reason.\"\n---\n{/* KEEP: this page = trust (job 4) + payoff (job 5). Lead-in = vibecoding handbooks are flooding out, author actually surveyed them, they share one gap (too narrow, or vague, or they say what to do but never WHY). This book's edge = the reason behind every move, which the reader has already felt reading this far. Then pivot to the track record. Do NOT regress to narrating the page's three sections. */}\n\nVibecoding handbooks are everywhere now, and more land every week. I read through what is out there, and each one has the same gap: zoomed onto a single slice, or vague, or long on what to do and silent on why. You have read this far, so you can already feel the difference here, every move carries its reason. Here is the track record behind that voice.\n\n{/* KEEP: concrete credentials: 16 years; ~100 projects built from scratch (the phrase itself links to /projects, like the OSS links below); ~another 100 managed, architected, or contributed to; 3 continents; OSS (Laradock, Apiato, Porto); Sista AI = solo-built, guides startups through AI transformation; Sistava = solo-built platform for running an entire business on AI agents. Mention for CREDIBILITY only, keep it factual and understated, not a promo/CTA (user is explicit about this). */}\n\n## I have shipped over a hundred projects\n\nMahmoud Zalt, sixteen years shipping software. Around [100 projects](/projects) built from scratch, and roughly another 100 managed, architected, or contributed to, with top teams across three continents. More on [LinkedIn](https://www.linkedin.com/in/mahmoudzalt).\n\nMore than ten open-source projects, including [Laradock](https://laradock.io), [Apiato](https://apiato.io), and [Porto](https://porto.zalt.me), tools other engineers build their own systems on. Full list on [GitHub](https://github.com/Mahmoudz).\n\nTwo companies built solo: [Sista AI](https://sista.ai), guiding startups through their AI transformation, and [Sistava.com](https://sistava.com), a platform for running an entire business on AI agents.\n\n{/* KEEP: humble + professional, do NOT criticize other teachers. Track record = 16 years building through every shift in the field; early AI adopter who went through every phase as the tools improved; with AI has shipped many products of his own and helped many others do the same (NO numbers, no \"proudest of\" style). This continuity is the credibility. */}\n\n## I adopted AI early, and kept shipping\n\nFor sixteen years I have built software, adapting through every shift the field went through. When AI coding arrived I was among the first to adopt it, and I have stayed with it through every phase, rebuilding how I work as the tools grew more capable.\n\nWith AI I have shipped many products of my own, and helped many others do the same.\n\n{/* KEEP: openly written with AI (best model available), section by section with human ordering and judgment; the onboarding-a-junior frame (if you joined my team, this is what I'd tell you). */}\n\n## AI drafted it, I ordered it\n\nThis book was written with AI, and I will say so plainly: the most capable model available at the time did the drafting. But nothing here was generated in one pass. Each section took hours of back-and-forth, pulling what I know into words and into an order a beginner can actually follow.\n\nThat ordering is the real work, and the part AI still cannot do alone. It is what I would do sitting beside a new junior on their first day: not everything at once, but the right thing next, until they can build on their own.\n\n{/* KEEP: payoff = you become a professional vibe coder (idea to live, maintained software, AI as builder); then it is practice, build real projects with AI guiding you (the thing the author never had), until you can do what a junior engineering role asks for. NO vanity counts (\"three systems\"). */}\n\n## What you walk away with\n\nYou will not write code by hand, and you will not need to. Work through this book and you become a professional vibe coder: someone who takes an idea all the way to live, maintained software, with AI doing the building.\n\nFrom there it is practice. Build real projects this way, with something I never had sixteen years ago, an AI guiding you the whole way, and before long you can do what a junior engineering role actually asks for."
          },
          {
            "slug": "free-vs-paid",
            "title": "What's Free, What's Paid",
            "label": null,
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/intro/free-vs-paid",
            "content": "---\ntakeaway: Free ships it, paid sustains it\nshare: \"Most of this book is free and the rest is paid, and the split is not random: free gets you to a real, working app; paid protects it and keeps it standing once real users show up. Here is exactly where that line sits and why.\"\n---\n{/* KEEP: lead-in grounds in the reader's actual doubt (is this a bait and switch) rather than opening with free/paid housekeeping directly (banned in content-architecture.md). Answer: the split follows one rule, tied to what the app needs at each stage. Ends on what this page hands over: that rule. */}\n\nSomewhere ahead in this book you will hit a chapter that costs money, and it is fair to wonder if that is a bait and switch. It is not. The split follows one rule: what your app needs to exist versus what it needs to survive real people using it. This page hands you that rule, so you know exactly what you are paying for before you ever reach for a card.\n\n{/* KEEP: free = Plan through Harden (Plan, Set Up, Automate, Architect, Build, Debug, Test, Harden) -> a real, working, tested, polished app. Name the promise concretely, not vaguely \"a lot of content\". */}\n\n## Free takes you to a real, working app\n\nEverything from Plan through Harden costs nothing: planning, setup, building, debugging, testing, and hardening. Work through them and you have a real app running on a layout your AI can extend, tested so it stays working, and polished into a product instead of a rough draft.\n\nThat is not a trimmed demo. It is the actual first version of your software, and for a side project or a prototype, it may be all you ever need.\n\n{/* KEEP: paid = Secure, Protect, Ship, Operate, Scale, the parts that matter once real people use the app: security, their data and the law, real infrastructure, staying up, growth. Honest reason for the price, not \"more content for more money\". */}\n\n## Paid is what real users demand\n\nSecure, Protect, Ship, Operate, and Scale cost money. They cover what only matters once real people, not just you, are using the app: closing security holes before someone finds them, protecting user data and staying on the right side of the law, real infrastructure instead of your laptop, staying up while you sleep, and growing without falling over or blowing the budget.\n\nSkipping these parts is exactly how a working prototype turns into a breach or an outage. The price buys the parts a beginner does not know to worry about yet.\n\n{/* KEEP: what one payment covers, in plain terms, no dollar figure (lives in the UI, not prose, so this page never goes stale). One payment, once, every future paid chapter as it ships, no subscription. */}\n\n## One payment, not a subscription\n\nOne payment unlocks every paid chapter, including the ones added after you buy. No recurring charge, no seat, no login wall on the free half. Pay once and it is yours for good.\n\n{/* KEEP: contrast vs a generic course, a rigid template, or hiring a developer. This book: your actual project, your actual stack, one price, and you keep the judgment afterward. Do not repeat \"who it's for\" from Start Here, this is about the money, not the audience. */}\n\n## You keep the project and the skill\n\nA course teaches concepts in general. A template hands you one opinionated stack you still have to bend to fit. Hiring a developer costs far more, on an ongoing basis, and you learn none of it yourself.\n\nThis book works your actual project, in your actual stack, for one price, and you walk away able to do it again without paying anyone.\n\n{/* KEEP: honest exit, no manufactured guarantee/refund claim that doesn't exist. If the free half doesn't already prove the book's fit, don't buy the paid half; contact stays the same channel used elsewhere in the intro, don't invent a new promise. */}\n\n## If the free half is not doing it for you\n\nJudge the fit on the free parts, not a promise. If Plan through Harden has not already gotten your app running, the paid parts will not fix that, the problem is earlier. Stuck or unsure either way, reach out directly and ask before you pay."
          }
        ]
      },
      {
        "id": "plan",
        "title": "Structure your idea into a clear specification",
        "chapters": [
          {
            "slug": "set-up-your-workspace",
            "title": "Set Up Your Workspace",
            "label": "Workspace",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/plan/set-up-your-workspace",
            "content": "---\ntakeaway: Ready your machine first\nshare: Set up a clean coding workspace and your AI agent on the computer you already own, before you write a single line of code.\n---\n{/* KEEP: this is the book's real first working chapter (Plan ch1). Before any step makes sense you need two things: a place to write and an AI agent (you won't type code by hand). Sets both up on the computer you already own, and leaves a specs/ folder ready for the spec written next. Absorbs the old dev-environment chapter + the old \"set up your workspace first\" section. */}\n\nBefore any step in this book makes sense, you need two things: a place to write, and something to build with. You will not type code by hand, so that something is an AI agent. This chapter sets both up on the computer you already own, and leaves you a folder ready for the spec you write next.\n\n{/* KEEP: your existing OS (Windows/macOS/Linux) is the ground floor and enough; any recent laptop works (8GB ok, 16 comfortable); buy nothing, switch nothing. Bold-first: operating system. */}\n\n## Your own computer is enough\n\nYour computer already runs Windows, macOS, or Linux. That is its **operating system**, the ground floor everything else installs onto, and any of the three builds everything in this book.\n\nAny laptop from the last few years is enough: 8 GB of memory works, 16 GB is comfortable. Buy nothing and switch nothing, you start from what you have.\n\n{/* KEEP: the one thing you cannot skip = an AI coding agent (writes and changes your code); it's the engine of the book since you don't hand-write code. Two things decide quality: the agent itself AND the model powering it, watch both. Bold-first: AI coding agent, model. */}\n\n## The agent matters more than the editor\n\nThe one thing you cannot skip is an **AI coding agent**: the program that actually writes and changes your code. Since you never type code by hand, the agent is the engine this whole book runs on.\n\nTwo things decide how good it is: the agent itself, and the **model** powering it, the AI brain it thinks with. Keep an eye on both. A sharp agent on a weak model, or the reverse, will let you down.\n\n{/* KEEP: where you work is your choice and barely matters if the agent is good. VS Code if more technical / want control; agent-first tools (Claude Code, Codex, Cursor, OpenCode) if you want the simplest path. New ones appear constantly, ask or search for the current best, pick what you're comfortable in. What matters = agent + model, not the interface. Do NOT prescribe only VS Code. */}\n\n## Pick where you work\n\nWhere you and the agent work together is your choice, and it matters far less than the agent itself.\n\n- **More technical, or want full control?** Install [VS Code](https://code.visualstudio.com), a free editor from Microsoft, and run your agent inside it.\n- **Want the simplest path?** Use an agent-first tool like [Claude Code](https://claude.com/claude-code), [Codex](https://developers.openai.com/codex), [Cursor](https://cursor.com), or [OpenCode](https://opencode.ai), where the agent is the whole interface.\n\nNew options appear all the time. Ask your agent or search for the current best, then pick the one you feel comfortable in. What matters is the agent and its model, not the shell around them.\n\n{/* KEEP: the model is the brain the agent runs on, and most agents let you SWAP it. Frontier models (closed, big labs: Claude/GPT/Gemini/Grok) = most capable for hard reasoning/code; open-source models (Llama/DeepSeek/Qwen/GLM) = cheaper, self-hostable, catching up fast. Rankings change constantly, no permanent winner, check a live leaderboard (LMArena, Artificial Analysis), never trust last year's. The move: pick an agent that lets you switch models, re-check every few months, never lock into one vendor. Ends with a Hint callout: OpenRouter = one account/bill/interface to switch models; open-source can also run locally but needs serious hardware. Links: vendors + leaderboards + OpenRouter. Do NOT re-bold \"model\" (bolded earlier); bold-first: frontier, open-source. */}\n\n## Never lock into one model\n\nYour agent runs on a model, and most agents let you swap that model out. Two camps are worth knowing:\n\n- **Frontier** models, like [Claude](https://claude.com), [GPT](https://openai.com), [Gemini](https://gemini.google.com), and [Grok](https://grok.com), are the most capable. They are closed, run by the big labs, and best for hard reasoning and tricky code.\n- **Open-source** models, like [Llama](https://llama.com), [DeepSeek](https://deepseek.com), [Qwen](https://qwen.ai), and [GLM](https://z.ai), you can run cheaply or host yourself, and they are closing the gap fast.\n\nWhich model leads changes every few weeks, so no ranking you read today stays true for long. Do not trust last year's winner: check a live leaderboard like [LMArena](https://lmarena.ai) or [Artificial Analysis](https://artificialanalysis.ai) when the choice matters.\n\nThe move is to stay loose. Pick an agent that lets you switch models, and re-check every few months. That way you are never stuck with one vendor, and you always ride the current best.\n\n> **Hint:** [OpenRouter](https://openrouter.ai) gives you most models through one account, one bill, and one interface, so you switch between them without signing up with each vendor. Open-source models you can also run on your own machine, but that takes serious hardware to run a capable one well.\n\n{/* KEEP: create one project folder + a specs/ folder inside so the NEXT chapters have somewhere to write the spec. Full project layout waits until you know the app's pieces; a specs folder is all you need to start. */}\n\n## Make a folder, and a home for your spec\n\nCreate one new folder on your computer for this project; it will hold everything you build. Inside it, add a folder named `specs` for your planning notes. That is your whole workspace today.\n\nThe full project layout waits until you know your app's pieces. A `specs` folder is all you need to start writing, which is the very next thing you do.\n\n{/* KEEP: one setup prompt (senior voice, fixed top + one append slot) that recommends an agent + interface for THIS reader and walks the install + folder creation. Ends with Do this now. */}\n\n## Set it all up in one pass\n\nThis prompt has your agent recommend the right tools for you and walk you through the whole setup:\n\n```\nAct as a patient senior engineer setting someone up to\nbuild software with AI for the first time. Based on my\ncomputer and how technical I am, recommend an AI coding\nagent and where to run it: a plain editor like VS Code, or\nan agent-first tool. Then walk me, step by step, through\ninstalling it, creating a project folder with a folder\nnamed specs inside, and checking it all works.\n\nMy computer and experience:\n```\n\n**Do this now:** set up your agent and editor, create your project folder with a `specs` folder inside, and open it once so you have seen your workspace."
          },
          {
            "slug": "gathering-requirements",
            "title": "Turning an Idea Into a List",
            "label": "Requirements",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/plan/gathering-requirements",
            "content": "---\ntakeaway: Testable must-have list\nshare: Your AI agent cannot build a feeling, and you cannot check whether a feeling is finished. Turn the idea in your head into a clear, testable list of what the software must do, before any code.\n---\nYou have the whole idea in your head. Your agent cannot build a feeling, and you cannot check whether a feeling is finished. Before any code, you turn that idea into a clear, specific list of what the software must do. This chapter gets you that list.\n\n## A requirement is testable, not a wish\n\nA **requirement** is one thing your software must do, written plainly enough that anyone can tell whether it works yet.\n\n| Wish | Requirement |\n|---|---|\n| Users can log in | A person signs in with an email and password, and resets a forgotten one through an email link |\n\nThe difference is that you can test the requirement and point at a clear yes or no. Write every requirement so it passes that test.\n\n## Write each one as a user story\n\nProfessional teams capture requirements as **user stories**, one line each: `As a [type of user], I want to [do something], so that [reason].` It forces you to name who the feature is for and why it exists, which quietly kills the features nobody actually needs.\n\n> **Example:** As a shopper, I want to save items to a cart, so that I can pay for them all at once.\n\n## Split must-have from later\n\nList every story you can think of, then mark each one **must-have** or **later**. Be harsh: a must-have is something the product is useless without, not something that would be nice to have. The must-haves are all you build first; everything else waits its turn.\n\n## Get them from real users, not your head\n\nRequirements come from the people who will actually use the thing, not your imagination. Talk to three to five real potential users and write down what they ask for in their own words.\n\n> **Watch out:** if you cannot find one person who wants this, that is the cheapest moment you will ever get to learn it, long before you have built anything. Check before you build; if you build anyway, at least know you skipped it.\n\nThis prompt turns the idea in your head into that list:\n\n```\nAct as a senior product engineer. Interview me about the\napp I want, then turn my idea into user stories, one line\neach: \"As a [user], I want [action], so that [reason].\"\nMark each must-have or later, and keep every one testable,\nsomething we can point at and call done. Save the list in\nmy specs folder.\n\nMy idea:\n```\n\n**Do this now:** paste the prompt, describe your idea, and let your agent draft the user-story list into your `specs` folder. That list is the raw material for every chapter that follows."
          },
          {
            "slug": "scoping-your-mvp",
            "title": "Cutting Scope to the Core",
            "label": "MVP",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/plan/scoping-your-mvp",
            "content": "---\ntakeaway: Cut to the core\nshare: \"Every item on your must-have list feels essential, which is exactly why it will sink your first version. Cut it down to an MVP: the smallest thing you can ship that actually helps one real person.\"\n---\nYour must-have list is still too big. Every item on it feels essential, which is exactly why the list will sink your first version if you try to build all of it. This chapter cuts that list down to an MVP: the smallest thing you can ship that actually helps one real person.\n\n## An MVP is the smallest version worth shipping\n\n**MVP** stands for **minimum viable product**. It is the leanest version of your idea that still delivers real value to one kind of user and can go live.\n\nTwo words carry the weight. \"Minimum\" means you cut ruthlessly. \"Viable\" means what is left still works for someone, end to end, on its own.\n\n## Apply the one core job test\n\nEvery product exists to do one core job. For everything on your list, ask a single question: does this feature directly serve that one job, or does it just decorate it?\n\n| Product | The one core job |\n|---|---|\n| A note app | Write a note and find it again |\n| A store | Buy one item and pay |\n| A booking tool | Reserve one slot at one time |\n\nIf a feature is not required for that core job to work once, it is not in the MVP. It can be the best idea you have and still wait.\n\n## Expect to cut some must-haves too\n\nHere is the uncomfortable part: some features you marked must-have in the previous chapter still get pushed to a later version. \"Must-have eventually\" and \"must-have to ship the first slice\" are different bars.\n\n| In the MVP | Pushed to later |\n|---|---|\n| Sign in with email | Sign in with Google, Apple |\n| Post one item for sale | Bulk upload, drafts, scheduling |\n| Pay with one card | Saved cards, refunds, coupons |\n| One language | Translations |\n\n## Build a walking skeleton, not half an app\n\nA **walking skeleton** is one thin path through your whole app that actually works: a user arrives, does the core job once, and gets a result. It is skinny, but every bone connects.\n\nThis beats building many features to fifty percent. Ten half-finished features ship nothing a person can use. One complete path, however plain, is a product you can put in front of someone tomorrow.\n\n## Guard the line against scope creep\n\n**Scope creep** is the slow drift of \"while we are at it, let us also...\" that turns a two-week build into a six-month one. Every added feature feels small in the moment and enormous in total.\n\nWrite your MVP list down and treat new ideas as a separate \"later\" pile, not an edit to the plan. The plan is closed; the pile stays open.\n\n> **Example:** An MVP for a food-delivery app: one restaurant, a fixed menu, one delivery address, pay with one card. No search, no ratings, no live tracking. A hungry person can still order dinner, so it is viable.\n\nThis prompt cuts your list to the core for you:\n\n```\nAct as a senior engineer scoping my MVP. From my user\nstories, find the single core job the app exists to do,\nthen keep only the stories needed to do that job once, end\nto end. Challenge every must-have: if the product still\nworks without it, move it to later. List what ships now and\nwhat waits.\n\nMy user stories:\n```\n\n**Do this now:** paste the prompt with your stories, and let your agent cut them to the smallest version that does the one core job, moving the rest to a \"later\" pile."
          },
          {
            "slug": "the-pieces-of-an-app",
            "title": "The Pieces of an App",
            "label": "Components",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/plan/the-pieces-of-an-app",
            "content": "---\ntakeaway: Name the five pieces\nshare: Every web app is a few standard parts, each with its own job. Learn to name the five pieces and follow a single click through them, so you can tell your agent what to change and reason about why things break.\n---\nYour idea feels like one thing, but every web app is made of a few standard parts, each with a separate job. If you cannot name those parts, you cannot tell your agent which one to change, and you cannot reason about why something broke. This chapter gives you that map: the five pieces and how a single click travels through them.\n\n## The five pieces\n\nEvery web app is built from the same handful of parts. Learn these names once and the rest of the handbook stops sounding foreign.\n\n| Piece | What it does |\n|---|---|\n| Frontend | What the user sees and clicks in the browser: the pages, buttons, forms |\n| Backend | The server that runs your logic: it decides what happens when a request arrives |\n| Database | Where data is stored so it survives after the browser closes |\n| API | The agreed set of messages the frontend and backend use to talk to each other |\n| Hosting | The computers where all of the above actually run, reachable over the internet |\n\n## Frontend and backend are two programs\n\nThe **frontend** runs on the user's device, inside their browser. The **backend** runs somewhere else, on a server you control, out of the user's reach.\n\nThis split matters because anything secret (passwords, private data, business rules) lives in the backend, never the frontend. The frontend is public by nature: anyone can open it and look.\n\n## The API is a contract, not a place\n\nThe frontend cannot reach into the database directly. It sends a request to the backend through the **API**, a fixed list of allowed messages like \"give me this user's orders\" or \"save this comment.\"\n\nThink of the API as a menu. The frontend can only order dishes on the menu, and the backend decides how each one is cooked.\n\n## The database remembers, hosting runs it\n\nThe **database** is the app's memory. Close the browser, restart the server, and whatever was saved there is still waiting.\n\n**Hosting** is simply the rented computers where the frontend, backend, and database live so the public can reach them. Which specific tools fill each slot is a later decision, covered when you choose your stack.\n\n## Trace one click through all five\n\nFollow one action, a user saving a comment, and watch it cross every piece in order:\n\n1. **Frontend:** packages the typed comment and sends it off.\n2. **API:** carries that request to the backend as an agreed message.\n3. **Backend:** checks the user is allowed, then writes the comment down.\n4. **Database:** stores it and confirms it is saved.\n5. **Frontend again:** shows the saved comment on screen.\n\nAll of it runs on **hosting**, the rented computers underneath.\n\nEvery feature you build is some version of this loop. Once you can see the loop, a bug stops being \"the app is broken\" and becomes \"which piece dropped the message.\"\n\nThis prompt maps your app onto the five pieces:\n\n```\nAct as a senior engineer. Map my app onto the five pieces\nof a web app: frontend, backend, API, database, and\nhosting. For each, say in one line what it does for my app\nspecifically. Then trace one core feature as a round trip\nthrough all five.\n\nMy app:\n```\n\n**Do this now:** paste the prompt and let your agent map your five pieces, then read the round trip so you can picture where each feature lives."
          },
          {
            "slug": "modeling-your-data",
            "title": "Mapping What You Store",
            "label": "Data Model",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/plan/modeling-your-data",
            "content": "---\ntakeaway: Model data first\nshare: \"Code changes cheaply; the shape of your stored data does not, once real users have filled it. Sketch a plain data model first: the things you store, what each holds, and how they connect.\"\n---\nYour app stores things: people, their stuff, the records of what they did. Code changes cheaply, but the shape of that stored data does not, because once real users have created it, you cannot rename or reorganize it without touching everything they made. So you sketch that shape first, on paper, before a line of code. This chapter gets you a plain data model: the things you store, what each one holds, and how they connect.\n\n## The shape of your data is called a schema\n\nA **schema** is the plan for what your app stores, separate from the place it gets stored (an earlier chapter covered what a database is). You are drawing the plan now, not choosing the storage yet.\n\nThe plan has two parts: the things you keep, and the links between them. Get both right on paper and the actual build is mostly transcription.\n\n## List your entities and their key fields\n\nAn **entity** is one kind of thing you store, like a `User` or an `Order`. A **field** is one piece of information an entity holds, like a user's email or an order's total.\n\nName each entity as a singular noun, then list only its key fields: the ones the app genuinely needs, not every detail you can imagine. Write it as a plain table.\n\n| Entity | Key fields |\n|---|---|\n| `User` | `name`, `email`, `password` |\n| `Order` | `total`, `status`, `createdAt` |\n\nIf you cannot name an entity as a single noun, it is probably two entities hiding in one.\n\n## Draw the relationships between them\n\nA **relationship** is how two entities connect. Most connections are **one-to-many**: one entity owns many of another, and each of those belongs to exactly one owner.\n\nA `User` has many `Orders`, and each `Order` belongs to one `User`. That is one-to-many. When both sides have many (a `Post` has many `Tags` and a `Tag` covers many `Posts`), that is **many-to-many**, and you note it as such.\n\nState every relationship in that plain \"has many\" and \"belongs to\" form. Sketched out, a shop's model nests like this:\n\n```\nUser\n  has many: Order\n    has many: LineItem\n      references: Product\n```\n\nEach indent is a \"belongs to\": a `LineItem` sits under one `Order`, which sits under one `User`. It reads unambiguously to both you and your agent, no diagram tool needed.\n\n## Keep it a sketch, not a database\n\nResist writing SQL, picking a product, or adding fields for features you have not scoped. The model is a thinking tool, and every field you add now is one more thing to keep true later.\n\nA later chapter turns this sketch into real tables. Right now you only need it clear enough that someone else could read it and know what your app remembers.\n\nThis prompt drafts the sketch from your app:\n\n```\nAct as a senior engineer sketching my data model. From my\napp, list the entities (the things it stores), each with\nits key fields, and state every relationship as \"has many\"\nor \"belongs to.\" Keep it a plain sketch, not SQL and not a\nreal database yet.\n\nMy app, and what it needs to remember:\n```\n\n**Do this now:** paste the prompt and let your agent draft the entity sketch, then check the relationships match how you picture your app."
          },
          {
            "slug": "non-functional-requirements",
            "title": "The Hidden Requirements",
            "label": "NFRs",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/plan/non-functional-requirements",
            "content": "---\ntakeaway: Quality targets as numbers\nshare: Your features say what the software does, not how fast, reliable, or safe it must be. Pin down these quality targets early, while changing them is still cheap, because they quietly decide your architecture and stack.\n---\n{/* KEEP: lead-in = user stories say WHAT it does, not HOW WELL (fast/reliable/for how many). Those quality bars = non-functional requirements: the part most tutorials and books SKIP, and the part serious engineers pin down EARLY, because they decide architecture and stack before any feature exists. This chapter = a short list of measurable targets set now, while changing them is still cheap. */}\n\nYour user stories say what the software does. They say nothing about how well it has to do it: how fast, how reliably, how safely, for how many people. Those quality bars are non-functional requirements, the part most tutorials skip and serious engineers pin down early, because they quietly decide your architecture and stack long before you write a single feature. This chapter gets you a short list of measurable targets, set now, while changing them is still cheap.\n\n{/* KEEP: functional requirement = a thing the software does; non-functional requirement = a standard it must meet while doing it. Table contrasts the two sides. Bold-first: functional requirement, non-functional requirement. */}\n\n## What it does vs. how well it does it\n\nA **functional requirement** is a thing the software does. A **non-functional requirement** is a standard it must meet while doing it.\n\n| Functional | Non-functional |\n|---|---|\n| A shopper can pay for a cart | Checkout completes in under 3 seconds |\n| A user uploads a photo | The site handles 500 people uploading at once |\n| An admin views a report | The report is available 99.9% of the time |\n\nThe functional side is the feature. The non-functional side is the quality bar that feature is held to.\n\n{/* KEEP: targets must be NUMBERS, not adjectives (\"fast\"/\"secure\" can't be tested). Walk five categories (performance, availability, security, scale, accessibility) and turn each vague wish into a measurable target. Three-column table: category | vague | measurable. */}\n\n## Set a number, never an adjective\n\n\"Fast\" and \"secure\" cannot be tested. A number can. Every target you write should be something you or your agent can measure and get a clear yes or no on.\n\nWalk these five categories. For each, turn the vague wish into a number a test can check:\n\n| Category | Vague (won't do) | Measurable target |\n|---|---|---|\n| Performance | \"fast\" | A page loads in under 2 seconds |\n| Availability | \"reliable\" | Up 99.9% of the time |\n| Security | \"secure\" | Passwords encrypted, login enforced on private data |\n| Scale | \"handles growth\" | 10,000 users and 1 GB of data in year one |\n| Accessibility | \"usable by all\" | Works by keyboard and screen reader, meets [WCAG](https://www.w3.org/WAI/standards-guidelines/wcag/) AA |\n\n{/* KEEP: THE most important idea of the chapter, hammer it. Your OBJECTIVE (raw speed vs tight accuracy vs a target user count) decides how the whole app is architected. Document these targets and hand them to the AI BEFORE it designs anything, so it builds the structure to hit them; leave them out and it guesses wrong and you pay to redo the foundation. Not built here, a later part covers hitting the bars. */}\n\n## Write them down for your AI before it builds\n\nThese are not paperwork, they are the most important thing you decide in this part. Your objective, whether you need raw speed, tight accuracy, or a set number of users, decides how the whole app is put together.\n\nA \"handle 500 uploads at once\" target changes your database and hosting from day one. Bolt scale or accessibility on after launch and you are usually rewriting the foundation.\n\nHand these targets to your AI before it designs anything, and it builds the structure to hit them. You are not implementing any of it yet, a later part covers how. Here you just set the bars, so every choice downstream has something to aim at.\n\n> **Example:** 95% of pages load in under 2 seconds on a mobile connection, and the app is available 99.9% of the time.\n\nThis prompt proposes your targets, tuned to your goal:\n\n```\nAct as a senior engineer setting my non-functional targets.\nFor my app, propose one measurable target in each of:\nperformance, availability, security, scale, accessibility.\nEvery target must be a number a test could check, and must\nfit my real objective, not generic best practice. Save them\nin my specs folder.\n\nMy app, and my main objective (speed, accuracy, scale...):\n```\n\n**Do this now:** paste the prompt with your objective, let your agent propose the five measurable targets into your `specs` folder, and adjust any number that does not match your goal."
          },
          {
            "slug": "writing-the-spec",
            "title": "Writing It All Down",
            "label": "Spec",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/plan/writing-the-spec",
            "content": "---\ntakeaway: One short spec\nshare: Your requirements, scope, parts, data, and quality targets are useless scattered across notes. Assemble them into one short spec, the single document your AI agent actually builds from.\n---\nThe earlier chapters each left you with a piece: user stories, a scope line, the app's parts, its data, its non-functional targets. Scattered across notes and your head, those pieces are useless to an agent that reads before it builds. This chapter assembles them into one short document, the spec, that your agent works from.\n\n## A spec is a document your agent builds from\n\nA **spec** is a single short document describing what the software must be and do. If you have heard the term **PRD**, short for product requirements document, that is the same idea under a heavier name.\n\nYour agent needs it as its single source of truth: the one thing it reads before building, so it does not guess and invent details you never wanted. Without it, every ambiguity becomes a coin flip you did not get to call.\n\n## Short and living, not forty pages\n\nA spec is not an exhaustive contract you write once and freeze. It is a working document you keep tight and update as you learn.\n\n| Forty-page document | Living spec |\n|---|---|\n| Written once, out of date by week two | Edited whenever scope or data changes |\n| Covers every edge case up front | Covers the shape; details emerge in the build |\n| Nobody rereads it | Short enough to reread before each task |\n\nAim for something you and the agent can both hold in your head. A page or two beats a chapter nobody keeps current.\n\n## Assemble the sections you already have\n\nEach section maps to a Plan chapter you already worked through. You are not writing new material here, you are collecting it.\n\n- **Problem and audience:** the problem and who it is for.\n- **Scope:** the MVP as must-have user stories.\n- **Main pieces:** the app's components and its modular shape.\n- **Data model:** the entities and how they relate.\n- **Non-functional targets:** speed, security, and scale you committed to.\n\n> **Template:**\n> ```\n> # [Product name]\n> ## Problem & audience\n> ## Scope (MVP user stories)\n> ## Main pieces & structure\n> ## Data model\n> ## Non-functional targets\n> ```\n\n## Keep it in the repo, next to the code\n\nPut the spec in the project itself, its **repo** (the folder that holds all your code), as a plain [markdown](https://commonmark.org) file the agent can open every time. A spec living in a chat window or a separate doc is one your agent cannot reliably read.\n\nKeeping it beside the code means every edit to scope or data lands in the same place the build happens. The spec and the software drift apart the moment they live in different homes.\n\nThis prompt assembles the spec from what you already wrote:\n\n```\nAct as a senior engineer assembling my spec. Pull the\npieces I already have (user stories, MVP scope, the five\ncomponents, the data model, the non-functional targets)\ninto one short spec.md in my specs folder. Keep it tight\nand skimmable, a living document, not forty pages.\n\nMy pieces (paste them, or point to the files in specs):\n```\n\n**Do this now:** paste the prompt, point your agent at your `specs` folder, and let it assemble `spec.md`. A later chapter turns this spec into instructions your agent builds from."
          }
        ]
      },
      {
        "id": "setup",
        "title": "Prepare your environment and tools",
        "chapters": [
          {
            "slug": "choosing-your-stack",
            "title": "Picking Your Tech Stack",
            "label": "Stack",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/setup/choosing-your-stack",
            "content": "---\ntakeaway: Pick boring, popular stack\nshare: Ignore the hundreds of options and the fights about them. Get a solid tech stack to start on today, plus a prompt that picks the right one for your case with a senior engineer judgment.\n---\nYou have a workspace and a spec. Now you choose what to build the app out of: the language, the framework, the database, the place it runs. Ignore the hundreds of options and the fights about them. This chapter hands you a stack to start on today, and a prompt that picks the right one for your case with a senior engineer's judgment.\n\n## A stack is layers, bottom to top\n\nA **stack** is just the layers your app is built from, each sitting on the one below:\n\n- **Platform:** where it runs, web, iPhone, or desktop. This choice decides everything above it.\n- **Language:** the code it is written in, like [TypeScript](https://www.typescriptlang.org) or [Python](https://www.python.org).\n- **Framework:** a proven structure on top of the language, so you do not start from a blank page.\n- **Boilerplate:** a ready-made starter project, so you begin from a working app instead of an empty folder.\n- **Libraries:** open-source pieces you drop in for one specific job instead of building it yourself.\n\nYou pick from the bottom up, and at each layer the popular choice is usually the right one.\n\n## Default to the popular choice\n\nPick the most common option unless a hard requirement forces otherwise. Your agent has built it countless times and makes fewer mistakes with it, so popularity is really how much help you get.\n\n## A stack for almost any app\n\nFor a typical web or consumer app, take this as-is:\n\n| Piece | Use | Why |\n|---|---|---|\n| Frontend | [React](https://react.dev), via [Next.js](https://nextjs.org) | The default way to build a web interface |\n| Backend | [Node](https://nodejs.org) with TypeScript | Same language as the frontend, one stack to run |\n| Database | [PostgreSQL](https://www.postgresql.org) | Proven, free, handles almost anything |\n| Hosting | A plain rented server | Cheapest and simplest; a plain server suits your agent better than a fancy dashboard |\n\nOne language front to back. Next.js can serve both sides, so it is often the whole stack on its own.\n\n## Switch when the job demands it\n\nThe default holds until your app has a real requirement. Then a senior engineer moves deliberately, one line each:\n\n- **AI or heavy data:** a Python backend, its ecosystem is years ahead.\n- **Very low latency or high throughput:** [Go](https://go.dev) or [Rust](https://www.rust-lang.org) on the hot path.\n- **A static or brochure site:** no backend or database at all.\n\n## Let your agent choose for your case\n\nThis prompt already carries the engineering judgment. Paste it in and describe your app in the one slot at the bottom, nothing in the middle to hunt down and replace:\n\n```\nAct as a senior engineer choosing my stack. Recommend a\nfrontend, backend, database, and hosting for the app below.\nDefault to the simplest, most popular, agent-friendly option,\nand override only where my case demands, with real\nengineering judgment:\n\n- Typical web or consumer app: TypeScript everywhere.\n- AI or heavy-data app: Python backend for the ecosystem.\n- Low-latency or high-throughput core: Go or Rust there.\n- Weigh ecosystem maturity, hosting cost, and how easily\n  an agent can maintain it.\n\nFor each choice: one line of reasoning, plus one alternative\nand its tradeoff.\n\nMy app:\n```\n\n**Do this now:** take the default stack, or paste the prompt and describe your app at the bottom, then lock one tool into each piece before the next chapter."
          },
          {
            "slug": "meet-your-ai-agent",
            "title": "Meeting Your AI",
            "label": "Agent",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/setup/meet-your-ai-agent",
            "content": "---\ntakeaway: Agent, not chatbot\nshare: Your AI agent is not a chatbot that answers questions, it reads your files, runs commands, and changes your code. Knowing what it can and cannot do, and where its limits are, is what separates steering it from hoping.\n---\n{/* KEEP: lead-in = you picked an agent in Workspace; now understand what it actually IS so you can steer it instead of hope. Not tool-picking (that's Plan ch1), this is the mental model of working with an agent. */}\n\nYou already picked an agent back when you set up your workspace. Now you need to understand what it actually is, because the difference between steering it and just hoping comes down to knowing how it works. This chapter gives you that mental model.\n\n{/* KEEP: an agent is NOT a chatbot that just answers; it reads your files, runs commands, writes and changes code, and checks its own results, in a loop, inside your project. It acts, it doesn't just talk. Bold-first: agent. */}\n\n## What an agent actually is\n\nAn **agent** is not a chatbot that answers questions. It reads the files in your project, runs commands, writes and changes code, sees what happened, and goes again, in a loop, until the task is done.\n\nThat is the whole shift: a chatbot talks, an agent acts. It works inside your actual project, on your actual files, which is why everything in this book is about directing that action well.\n\n{/* KEEP: what it CAN do (write/refactor/wire features/fix errors/explain code fast) vs what it CAN'T (know your intent, decide what's worth building, judge good-enough, own the consequences). It is astonishing at the how, blind to the whether/why. That gap is the reader's job. */}\n\n## What it can and can't do\n\n| It's brilliant at | It cannot |\n|---|---|\n| writing and refactoring code | know what you actually want |\n| wiring up a feature | decide what's worth building |\n| reading an error and fixing it | judge when it's good enough to ship |\n| explaining a strange file in seconds | own the consequences |\n\nGive it a clear task and it outruns any human at the typing. But it is brilliant at the *how* and blind to the *whether* and the *why*, and that gap is exactly your job.\n\n{/* KEEP: agents work from CONTEXT (what's in front of them right now: open files, your message, what it just read), NOT long-term memory; it forgets between sessions. So you must feed it the right context each time; the rules file and a clear spec are how you make the important context always present (forward-points to the Rules chapter without naming mechanics). Bold-first: context. */}\n\n## It works from context, not memory\n\nAn agent works from **context**: what is in front of it right now, your message, the files it has open, what it just read. It does not carry a memory of your project between sessions the way a teammate would. Close it, reopen it, and it starts fresh.\n\nSo getting good work out of it is mostly getting the right things in front of it at the right moment. Much of this book, the spec you wrote, the rules you will set next, exists to keep the context that matters always in view.\n\n{/* KEEP: trust it to TYPE, not to DECIDE. Let it produce code fast, but you own the decisions (what to build, whether the result is right, what ships). Read what it does, don't rubber-stamp. This sets up code review later without naming it. */}\n\n## Trust it to type, not to decide\n\nLet the agent do the typing, all of it, at full speed. That is what it is for, and second-guessing every line wastes the whole advantage.\n\nBut keep the decisions yours: what to build, whether the result is actually right, what is allowed to ship. Read what it produces instead of waving it through. The agent is the fastest pair of hands you will ever have, and you are still the one steering.\n\n**Do this now:** open your agent and ask it to explain one file in your project out loud, so you see how it reads context and works, before you start directing it for real."
          },
          {
            "slug": "agent-rules",
            "title": "Setting the Ground Rules",
            "label": "Rules",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/setup/agent-rules",
            "content": "---\ntakeaway: Standing rules file\nshare: An agent forgets everything between sessions, so it repeats the same mistakes. A rules file is your standing instructions it reads every time, the difference between correcting it forever and telling it once.\n---\n{/* KEEP: lead-in = an agent starts fresh every session (context, not memory, from the last chapter), so it repeats the same mistakes. A rules file = standing instructions read automatically every time. Chapter = write it once instead of correcting forever. */}\n\nYour agent starts every session fresh, so it repeats the same mistakes: wrong folder, wrong style, a library you already rejected. Correcting it each time is exhausting and it never sticks. This chapter gives you the fix, a rules file the agent reads automatically, every single time.\n\n{/* KEEP [pro]: a rules file = a plain text file of standing instructions the agent reads at the start of every session, on its own. It is how you turn \"context, not memory\" (last chapter) into consistency: the important context is always present without you re-typing it. Bold-first: rules file. */}\n\n## A rules file is your standing instructions\n\nA **rules file** is a plain text file of standing instructions your agent reads at the start of every session, without being asked. It is how you beat the memory problem from the last chapter: the things that matter are in front of the agent every time, so you never re-type them.\n\nThink of it as onboarding a new hire who arrives with amnesia each morning. The rules file is the one-page brief that makes them productive again in seconds.\n\n{/* KEEP [pro]: put the NON-NEGOTIABLES in writing = the decisions you never want re-litigated: the stack, where code goes, the naming style, libraries to prefer or avoid, commit habits, \"always run the app before saying done\". Concrete, testable instructions, not vague vibes. */}\n\n## Put the non-negotiables in writing\n\nFill it with the decisions you never want to explain twice: your stack, where new code goes, the naming style, which libraries to prefer or avoid, how to commit, \"always run the app before telling me it works.\"\n\nWrite them concrete, the way you would tell a person, not as vague vibes.\n\n| Vague | Concrete rule |\n|---|---|\n| \"write clean code\" | \"keep files under 300 lines, one feature per folder\" |\n| \"be careful with secrets\" | \"never hardcode keys, keep them out of the code\" |\n| \"test your work\" | \"run the app and confirm it works before saying done\" |\n\n{/* KEEP: keep it SHORT and LIVING = a bloated rules file gets ignored (by you and the agent); keep it tight. It is living: every time you correct the agent on something it should have known, add that as a rule so you never correct it again. */}\n\n## Keep it short and living\n\nA giant rules file gets skimmed and ignored, by the agent and by you. Keep it tight, only the rules that actually matter.\n\nAnd keep it living. Every time you catch yourself correcting the agent on something it should have known, add that as one line. Over a few weeks the file quietly becomes the exact brief your project needs.\n\n{/* KEEP: where it lives = in the project itself (in the repo, alongside the code), so it travels with the code and every agent on the project reads the same rules. Each tool has its own filename/location, so let the agent set it up for your tool. Ends with Do this now = have the agent create the rules file for your tool + seed it. */}\n\n## Where the file lives\n\nThe rules file lives inside your project, committed alongside the code, so it travels with the repo and every agent that opens the project reads the same rules. Each tool looks for it under its own name and place, so the simplest move is to let your agent create the right one for the tool you chose.\n\nThis prompt has your agent create its own rules file:\n\n```\nAct as a senior engineer configuring my agent. Create the\nrules file for the tool I use, in the right filename and\nplace, and seed it with: my stack, where new code goes, my\nnaming style, and a few non-negotiables (for example: run\nthe app and confirm it works before telling me it's done).\nKeep it short.\n\nMy tool and stack:\n```\n\n**Do this now:** paste the prompt so your agent creates the rules file, then read it and add one rule you already know you care about. Add to it every time you correct the same thing twice."
          },
          {
            "slug": "version-control",
            "title": "Never Losing Work",
            "label": "Version Control",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/setup/version-control",
            "content": "---\ntakeaway: Never lose working code\nshare: You are about to let AI change your code all day. Version control is the safety net so one bad change never buries the version that worked, plus the words to make your AI use it right.\n---\n{/* KEEP: lead-in = you're about to let AI change code all day; without a safety net one bad change buries the working version. Version control is the net; this chapter gives the net + the vocabulary to make sure AI uses it right. */}\n\nYou are about to let AI change your code all day long. Without a safety net, one bad change can bury the version that worked, with no way back. Version control is that net, and this chapter hands it to you along with the words to make sure your AI uses it right.\n\n{/* KEEP: define git plainly = saves a snapshot (commit) of the whole project every time something works; you can return to any of them; the repo is the folder git watches. Unlike editor undo, git never forgets and never runs out. Bold-first: git, commit, repo. */}\n\n## Git is your undo button\n\n[**Git**](https://git-scm.com) is a tool that saves a snapshot of your whole project every time something works. Each snapshot is a **commit**, and you can jump back to any of them, so a version that ran is never more than one step away. The folder git watches is your **repo**, short for repository.\n\nUnlike the undo in a text editor, git never forgets and never runs out. Months of commits stay there, each one a safe place to return to.\n\n{/* KEEP: reassurance = you do NOT memorize or type git; the agent runs every command. Your job is judgment, knowing what should happen so you can tell it's being done right. That is the whole point of the chapter. */}\n\n## Your AI runs git, not you\n\nHere is the relief: you will not memorize a single git command. Your agent runs every one of them for you.\n\nWhat you need is not the typing, it is the judgment: knowing what should be happening, so you can tell when it is being done right. That is all this chapter is for.\n\n{/* KEEP: branch = a private copy split from the main line where AI tries a feature; merge if it works, throw it away if it breaks and main is untouched. Worktrees = one-line mention (several branches at once), AI handles them, reader doesn't. Bold-first: branch, main, worktrees. */}\n\n## Every experiment gets its own branch\n\nWhen AI tries a new feature, it should do it on a **branch**: a private copy of the project split off from the main line. If the feature works, the branch is merged back in. If it breaks, you throw the branch away and the working version, the **main** branch, never felt it.\n\nYou may also hear about [**worktrees**](https://git-scm.com/docs/git-worktree), a way to keep several branches open at once. Same idea, and your agent handles it. You do not need to.\n\n{/* KEEP: the one habit = commit after each small working change, not one giant commit at the end. Small commits = precise undo (undo the one bad step, not an afternoon). Instruction to give the agent: commit often with a short message. */}\n\n## Commit after every working step\n\nOne habit matters more than the rest: have your agent commit after each small change that works, never one giant commit at the end.\n\nSmall commits are precise. When something breaks, you undo the one step that caused it instead of losing an afternoon of work.\n\n> **Rule of thumb:** if it runs and does one new thing, commit it.\n\n{/* KEEP: semantic versioning = three numbers major.minor.patch (e.g. 2.4.1); major = breaking, minor = new feature, patch = fix. WHY START EARLY (the point of this section): versioning from day one gives you a clear marker for big changes/refactors, lets you control deployments and know exactly which version is live, and makes future debugging easier (you can pinpoint which version introduced a bug). Start now at ~0.1.0. Reader doesn't calculate it, tells AI to follow it. Bold-first: semantic versioning. Table for the three parts. */}\n\n## Version numbers that tell the truth\n\nWhen you share software, its version number tells people what changed. The standard is [**semantic versioning**](https://semver.org): three numbers like 2.4.1, each with a job.\n\n| Number | Goes up when | Example |\n| --- | --- | --- |\n| Major | a change breaks how it worked before | 1.9 to 2.0.0 |\n| Minor | you add a feature, nothing breaks | 2.3 to 2.4.0 |\n| Patch | you fix a bug | 2.4.0 to 2.4.1 |\n\nStart numbering from your very first working version, not once things get serious. With versions in place you always know where you stand: which one is live, what changed since, and, when a bug appears, which version introduced it. That one early habit makes big refactors, deployments, and debugging far easier later.\n\nYou do not work the numbers out yourself. Tell your agent to follow semantic versioning from the first release, starting around 0.1.0, and your history stays honest about what changed.\n\n{/* KEEP: the setup prompt (senior-engineer voice, fixed content + one append slot). Sets the agent's git ground rules once: commit per working step with clear messages, branch per feature, semantic versioning. Ends with Do this now = paste it. */}\n\n## Set the git rules once\n\nGive your agent these rules at the start, and it handles version control for the whole project:\n\n```\nAct as a senior engineer managing version control for\nmy project. From now on, without me asking each time:\n- Commit after every change that works, with a short,\n  clear message saying what changed.\n- Put each new feature on its own branch, and merge it\n  back only once it works.\n- Follow semantic versioning for releases: major for a\n  breaking change, minor for a feature, patch for a fix.\nExplain what you are doing the first few times so I can\nfollow along.\n\nMy project:\n```\n\n**Do this now:** paste the prompt above to your agent, so version control is running before you write another line."
          },
          {
            "slug": "dependencies",
            "title": "Living on Other People's Code",
            "label": "Dependencies",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/setup/dependencies",
            "content": "---\ntakeaway: Reuse, but vet\nshare: Almost everything your app needs, login, payments, dates, file uploads, is already built and battle-tested by millions. Learn to lean on these dependencies without inheriting their problems.\n---\n{/* KEEP: lead-in = don't build from scratch; almost everything (login, payments, dates, uploads) already exists as dependencies, tested by millions. Chapter = how to lean on them without inheriting their problems. */}\n\nYou could tell your AI to write everything from scratch. You should not. Almost everything your app needs, login, payments, date handling, file uploads, someone has already built and millions have tested. These ready-made pieces are dependencies, and this chapter shows you how to lean on them without inheriting their problems.\n\n{/* KEEP: dependency = code someone else wrote that your app installs and uses (a library / package); the agent installs them and lists them in one manifest file so any machine rebuilds the exact set; reader never installs by hand, their job is deciding which to trust since each is code they can't fully see. Bold-first: dependency, library, package, manifest. */}\n\n## What a dependency actually is\n\nA **dependency** is code someone else wrote that your app installs and uses, usually called a **library** or a **package**. Rather than build a payment flow or a calendar yourself, you pull in one that already works, hardened by everyone who uses it.\n\nYour agent installs them and lists them in one file, the **manifest** (like `package.json`), so any machine can rebuild the exact same set. You never install them by hand. Your job is deciding which are worth trusting, because each one is code you cannot fully see.\n\n{/* KEEP: choosing = prefer widely-used, recently-updated, still-maintained libraries; popularity means bugs surface fast, recent activity means security holes get patched. And add one only when it saves real work; for a few lines let the agent just write them (fewer deps = smaller surface to trust). Rule-of-thumb callout. */}\n\n## Pick popular, maintained, and alive\n\nNot every library deserves your trust. Before your agent adds one, it should prefer the option that is widely used, recently updated, and still maintained. Popularity means bugs surface fast; recent activity means security holes get patched.\n\nAdd one only when it saves real work, too. For a handful of lines, have your agent just write them, every dependency you skip is one less thing that can break on you.\n\n> **Rule of thumb:** a library with thousands of users and a commit this month beats a slicker one nobody has touched in two years.\n\n{/* KEEP: updating = old deps are where security problems pile up, so keep them current; but an update can change behavior, so update in small batches and check the app still works after each, never update everything at once right before shipping. Do NOT use \"branch\"/\"tests\" here (forward refs to version-control and Harden). */}\n\n## Update often, but never blindly\n\nOld dependencies are where security problems quietly pile up, so keep them current. An update can also change how a library behaves, though, so it pays to update a few at a time and check the app still works after each batch.\n\nWhat you never do is update everything at once the day before you ship. Small, checked steps let you catch the one update that broke something while it is still easy to find.\n\n{/* KEEP: version conflicts = two libs can want different versions of a third, or an update silently pulls in a newer piece that breaks yours; the lockfile (agent creates it automatically) records the exact version of everything so the app builds identically everywhere and nothing shifts unseen. Agent resolves conflicts; reader's job = notice when something that worked stops working. Bold-first: lockfile. */}\n\n## Lock versions so builds stay identical\n\nLibraries depend on other libraries, so two of them can want different versions of a third, or a fresh install can pull in a newer piece that breaks yours. Those version numbers follow a shared convention, [semantic versioning](https://semver.org), so a jump in the first number warns you a change may break things. A **lockfile**, which your agent creates automatically, records the exact version of everything, so the app builds the same on every machine and nothing shifts under you unseen.\n\nWhen versions do conflict, your agent sorts it out. Your job is only to notice when something that worked yesterday suddenly does not, and point the agent at it.\n\n{/* KEEP: license = every library ships with a license (legal terms for using it); most popular ones (MIT, Apache) allow almost anything, a few (some GPL variants) put conditions on how you distribute your own code. Rarely bites a small project, but before building a business on a library have the agent confirm its license is one you can live with. Link: choosealicense.com. Bold-first: license. */}\n\n## Check the license before you build on it\n\nEvery library comes with a **license**, the legal terms for using it. Most popular ones, like MIT and Apache, let you do almost anything. A few, like some GPL variants, put conditions on how you share your own code.\n\nIt rarely bites a small project, but before you build a business on a library, have your agent confirm its license is one you can live with. [choosealicense.com](https://choosealicense.com) explains the common ones in plain language.\n\n**Do this now:** ask your agent to list every dependency your project already pulls in, with each one's popularity, last update, and license, so you know exactly what you are standing on."
          },
          {
            "slug": "secrets-and-config",
            "title": "Handling Keys and Config",
            "label": "Secrets",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/setup/secrets-and-config",
            "content": "---\ntakeaway: Keys out of code\nshare: Put one password or API key in your code, and the day you share that code you hand strangers the keys. Keep every secret out of your codebase from the start with a git-ignored .env file.\n---\n{/* KEEP: lead-in = your app needs passwords/keys to reach its database, payments, AI services; put even one in the code and sharing the code hands strangers the keys. Chapter = keep every secret out of the codebase from day one. */}\n\nYour app needs passwords and keys to reach its database, its payment provider, its AI services. Put even one of those in your code, and the day you share that code, push it, or paste it to your agent, you hand strangers the keys. This chapter keeps every secret out of your codebase from the start.\n\n{/* KEEP: secret = any value that lets someone act as you/your app (db password, API key, payment token, signing key); if leaking it costs money or access, it's a secret. Everything else (titles, feature flags) = plain config, safe to share. Bold-first: secret, config. */}\n\n## What counts as a secret\n\nA **secret** is any value that lets someone act as you or your app: a database password, an API key, a payment token, an auth signing key. The test is simple: if leaking it costs you money or access, it is a secret.\n\nEverything else is just **config**, plain settings like a page title or a feature switch. Config is safe to share; secrets never are. This chapter is about the secrets.\n\n{/* KEEP [pro]: the rule pros never break = a secret is NEVER written in the code itself, because code gets shared, pushed, copied, pasted to the agent, and a hardcoded key travels everywhere the code goes. Secrets live OUTSIDE the code, in environment variables the app reads by name. Bold-first: environment variable. */}\n\n## Keys never touch the code\n\nThe rule a professional never breaks: a secret is never written in the code itself. Code gets shared, pushed to your repo, copied between machines, pasted to your agent, and a key sitting inside it travels to every one of those places.\n\nInstead, secrets live outside the code, in **environment variables**: values the app reads from its surroundings at startup, by name. The code says \"give me the database password,\" never the password itself.\n\n{/* KEEP: the .env file = standard home for secrets, KEY=value per line, read at startup; NEVER committed, add it to .gitignore. Commit a .env.example with the same keys but blank/fake values so others know which keys exist without seeing them. version-control already taught (this chapter comes after), so commit/repo/.gitignore are fine here. Bold-first: .env, .gitignore, .env.example. */}\n\n## One .env file, never committed\n\nThe standard home for those values is a file named **.env** in your project, one `KEY=value` per line, that your code reads when it starts. This file never leaves your machine: you add it to your **.gitignore** so version control skips it and it is never committed.\n\nSo anyone else, including your future self, knows which keys exist without seeing their values, you commit a **.env.example** beside it: the same key names with blank or fake values. The real secrets stay local, the list of what is needed is shared.\n\n{/* KEEP: if a secret ever leaks (screenshot, commit, pasted log), treat it as burned and ROTATE it: generate a new key at the provider, replace the old one, which instantly makes the leaked one useless. Do it immediately. Watch-out: a secret pushed to a repo stays in git history even after you delete it, rotating is the only real fix. Bold-first: rotate. */}\n\n## Rotate anything that leaks\n\nIf a secret ever slips out, in a screenshot, a commit, a pasted log, treat it as burned. **Rotate** it: generate a fresh key at the provider and replace the old one, which instantly makes the leaked copy useless. Do it the moment you notice, not later.\n\n> **Watch out:** a secret pushed to your repo does not disappear when you delete the line, it stays in the history. Rotating the key is the only real fix.\n\n{/* KEEP: prompt (senior voice) to wire up secret handling for the reader's stack: create .env, add to .gitignore, create .env.example, move existing keys out of code into .env and read from there. Ends with Do this now. */}\n\n## Wire it up for your stack\n\nThis prompt has your agent set secret handling up correctly for whatever stack you chose:\n\n```\nAct as a senior engineer setting up secret handling for\nmy project. Create a .env file for my stack and add it to\n.gitignore so it is never committed. Create a .env.example\nwith the same keys but empty values. Move any keys already\nsitting in my code into .env and read them from there.\nTell me what each key is for.\n\nMy stack:\n```\n\n**Do this now:** move every key out of your code into a git-ignored `.env`, and commit a `.env.example` beside it so the list of needed keys is shared but the values are not."
          }
        ]
      },
      {
        "id": "ai-os",
        "title": "Setup your AI agents operating system",
        "chapters": [
          {
            "slug": "os-first",
            "title": "System Comes Before the Code",
            "label": "OS-First",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/ai-os/os-first",
            "content": "---\ntakeaway: System before code\nshare: The instinct after setup is to start building the app. The people who win with AI build the system that runs everything first, then the app is just one thing that system produces.\n---\n{/* KEEP: lead-in = after setup the instinct is to start the app; instead build the SYSTEM that runs everything first (the factory metaphor from Start Here, now taught not just framed). This part builds that system. */}\n\nYou are set up, and the instinct is to start building the app right now. Hold on. The people who get the most out of AI build the system that runs everything first, and then the app is just one of the things that system produces. This chapter is why, and the rest of this part builds it with you.\n\n{/* KEEP: concept = the factory-before-car idea from the intro, now concrete. The system is a set of folders and files your agents work inside: plans, decisions, progress, and the rules everything runs by. It is the factory; the app is one car it makes. Ends with a one-line forward point to unified-system (where the factory lives); do NOT re-teach \"one tree\" here. */}\n\n## The factory before the car\n\nIn the introduction you saw the idea: the app is the car, and the system that produces it is the factory. This part is where you actually build that factory.\n\nThe factory is not abstract. It is a set of plain folders and files on your computer that hold your plans, your decisions, your progress, and the rules your agents work by. The app is just one of the things it produces. Where that factory lives, one tree that holds everything, is the next chapter.\n\n{/* KEEP: build the system BEFORE the app. Jumping straight to app code = building the car with no factory: every feature a one-off, nothing compounds. Build the system first and every later task drops into a place that already exists, with rules and context already set. Plus the practiced-sequence line: add machinery only when a real task hurts without it, the system earns each part. */}\n\n## Build the system before the app\n\nMost people open the editor and start generating app code on day one. That is building the car with no factory: every feature is a one-off, and nothing you do makes the next thing easier.\n\nBuild the system first and it flips. Every task after it, a feature, a fix, a marketing push, drops into a place that already exists, with the rules and the context already set.\n\nThis is a practiced sequence, not a slogan. The people who run real operations this way add each piece of machinery only when a task hurts without it, so the system earns every part it carries and never hauls dead weight.\n\n{/* KEEP: the system is a one-time setup that pays forever. Once it exists the agents have a home: they know where things go, the current priorities, and how you want work done. You stop re-explaining and start commanding. Rest of the part builds it piece by piece. Do this now = create the one folder that will be your operating base, separate from any single app's code. */}\n\n## Build it once, it keeps producing\n\nThe system is set up once and pays off forever. When it exists, your agents have a home: they know where things go, what matters right now, and how you want the work done. You stop re-explaining yourself and start giving commands.\n\nThe proof is never glamorous. In one long-running setup, a single plain file describing how the deploy really works ended a mistake the agent had repeated in every session; that one page paid for the whole system inside a week.\n\nThe rest of this part builds it one piece at a time: one unified tree, folders that act as departments, a control center you steer from, agents configured to work your way.\n\n**Do this now:** create one new folder that will be your operating base, kept separate from any single app's code. It stays empty for now; the next chapters fill it in."
          },
          {
            "slug": "unified-system",
            "title": "One System for Everything",
            "label": "Unification",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/ai-os/unified-system",
            "content": "---\ntakeaway: Everything in one tree\nshare: Your plans are in one app, your tasks in another, your notes in a third, and your agent can see none of them. Put the whole operation in one tree of plain files the agent can actually read.\n---\n{/* KEEP: lead-in = your operation is scattered across apps (Notion/Trello/head) and the agent sees none of it. This chapter puts everything in one tree of plain files the agent can read and write. */}\n\nYour plans live in one app, your tasks in another, your notes in a third, and your agent can open none of them. This chapter puts your whole operation into one tree of plain files on your machine, so nothing that runs your work is hidden from the thing doing it.\n\n{/* KEEP: one tree, not ten apps. Everything (plans, tasks, notes, decisions, the app) lives in one folder tree; NOT Notion for docs, Trello for tasks, chat for decisions. Reason: the agent can open a folder, it cannot open your Notion or your memory. Show a small folder-tree sketch (device variety, os-first was all prose). */}\n\n## One tree, not ten apps\n\nEverything about your operation lives in one folder tree on your machine: your plans, your tasks, your notes, your decisions, and the app itself. Not a docs app, a task app, and a chat thread each holding a piece.\n\nThe reason is blunt: your agent can open a folder. It cannot open your Notion, your task app, or your memory of what you decided last Tuesday.\n\n```\noperating-base/\n  plans/\n    roadmap.md\n  tasks/\n    todo.md\n  decisions.md\n  app/            (your actual application code)\n```\n\nThat is day one. A production tree grows branches you will build through this part: reports the agents write, skills they load, a log of everything that happened.\n\n{/* KEEP: everything is a plain file (usually markdown = plain text with light formatting), not a proprietary format locked in an app. A plan, a task list, a decision = a plain text file the agent reads and writes natively, and that you can still open in ten years. Gloss markdown (new term). Bold-first: markdown. */}\n\n## Everything is a plain file\n\nEvery piece is a plain text file, usually [**markdown**](https://commonmark.org), which is just plain text with light formatting. Not a document locked inside some app's format.\n\nA plan is a text file. A task list is a text file. A decision is one line in a text file. That is exactly what your agent reads and writes on its own, and what you can still open in ten years when today's apps are gone.\n\n> **Hint:** some people formalize this into a shared convention so every agent reads a knowledge base the same way, like the [Open Knowledge Format](https://okf.md) (markdown files plus a few frontmatter rules). You do not need a spec to start, but it is a useful reference once your tree grows.\n\n{/* KEEP: one source of truth. Because it's one tree, each thing lives in exactly ONE place; the current priorities are one file, not remembered three ways. Change it there and every agent reading it sees the truth. No syncing, no \"which version is right\". */}\n\n## One source of truth\n\nBecause it is all one tree, each thing lives in exactly one place. Your current priorities are one file, not three half-remembered versions in your head and two apps.\n\nWhen something changes, you change it there. Every agent that reads it then sees the same truth, with no syncing and no \"which copy is right.\"\n\n{/* KEEP: why one beats many. Ten apps = ten silos the agent can't cross + a picture only you can assemble in your head. One tree = the agent sees the whole thing, moves work between parts, nothing hidden. Also it's YOURS: plain files on your disk, portable, backed up with your code, not locked in someone's cloud. */}\n\n## Why one system beats many\n\nTen apps mean ten silos your agent cannot cross, and a full picture only you can hold, in your head. One tree means the agent sees everything, moves work between the parts, and misses nothing.\n\nIt is also yours. Plain files on your own disk are portable, backed up alongside your code, and never locked inside someone else's cloud.\n\n**Do this now:** pick one thing you currently track in another app, your task list or your notes, and move it into a plain text file inside your operating base. That is the first file of your system."
          },
          {
            "slug": "departments-and-folders",
            "title": "Folders as an Org Chart",
            "label": "Departments",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/ai-os/departments-and-folders",
            "content": "---\ntakeaway: Folders as departments\nshare: Every operation is already an org chart, you just have to make the folders match it. Draw the company as directories and anyone, human or agent, understands it in ten seconds.\n---\n{/* KEEP: lead-in = your work is many kinds of thing scattered with no place an agent knows to look. This chapter turns the operation into folders, an org chart on disk you and your agents read the same way. */}\n\nYour operation is a dozen different kinds of work, scattered across apps and your head, with no place an agent knows to look. This chapter turns it into folders: an org chart on disk that you and your agents read the exact same way.\n\n{/* KEEP: draw the company as directories before any automation (marketing, sales, money, customers, ops); the whiteboard tree = the on-disk tree, so the structure of the work and the structure of the files are the same. Anyone who opens it understands the operation in ten seconds. Show a folder-tree sketch. */}\n\n## Folders are your org chart\n\nBefore you write a line of automation, draw your operation as directories. Marketing, sales, money, customers, ops: each box on the whiteboard becomes a folder in your operating base.\n\n```\noperating-base/\n  marketing/\n  sales/\n  customers/\n  money/\n  ops/\n  app/          (the product itself)\n```\n\nNow the structure of the work and the structure of the files are the same thing, and anyone who opens the tree, you or an agent, understands the operation in ten seconds.\n\n{/* KEEP: organize by FUNCTION, not by tool or file type. No \"spreadsheets\"/\"docs\" folders; one sales/ folder holds everything sales whatever the format. High cohesion inside, clean seams between: an agent working sales opens one folder and finds its whole world. */}\n\n## One folder per function\n\nOrganize by function, never by tool or file type. You do not want a spreadsheets folder and a docs folder; you want a `sales/` folder that holds everything sales, whatever the format.\n\nHigh cohesion inside each folder, clean seams between them. When an agent works on sales, it opens one folder and finds the whole world it needs, and nothing leaks across the walls.\n\n{/* KEEP: each folder gets ONE control file, a status file (call it _status.md), holding three things: the department's rules, its current state, and a running log. It's the surface an agent reads to work and writes to record what it did. You never hunt for a department's truth. Show a minimal _status.md skeleton. Bold-first: status file. */}\n\n## Each department runs from one status file\n\nEvery folder gets a single control file, a **status file**, call it `_status.md`. It holds three things: the rules for that department, its current state, and a running log.\n\n```\n# Sales - status\n\n## Rules\n- Never promise a date we have not agreed internally.\n\n## Current state\n- 3 live deals, 1 in contract.\n\n## Log\n- 2026-07-08: sent the Acme proposal.\n```\n\nIt is the one surface an agent reads to do the work and writes to record what it did. The rule is standing: any agent that works in a department appends a dated line to its Log before it finishes, so you never hunt for the truth of a department. Later, your briefing command reads these files to build the one-screen picture, which is why keeping them honest pays off.\n\n{/* KEEP: start with the departments you HAVE, not the dream company. Three folders is fine. Add a department the day a real function appears, not before. The org chart grows as the operation grows, one folder at a time, never carrying rooms nobody lives in. Do this now = create one folder per function you run today + an empty _status.md in each. */}\n\n## Start with the departments you have\n\nDo not model the company you dream of. Model the one that exists today. Three folders is a fine start.\n\nYou add a department the day a real function appears, not before. The org chart grows the way the operation grows, one folder at a time, and it never carries rooms nobody lives in.\n\nThis prompt has your agent build the tree for you:\n\n```\nAct as a senior engineer setting up my operating system.\nIn my operating base, create one folder per function I run,\nand put an empty _status.md in each, with three headings:\nRules, Current state, Log. Do not invent functions I did\nnot name.\n\nThe functions I run today:\n```\n\n**Do this now:** paste the prompt above and list the functions you actually run, so your agent builds the department tree for you."
          },
          {
            "slug": "control-center",
            "title": "Your Command Hub",
            "label": "Control Center",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/ai-os/control-center",
            "content": "---\ntakeaway: One steering cockpit\nshare: \"Ten departments running at once pull you in ten directions. Build one room you steer from: a cockpit above every folder where you see the whole operation on one screen and decide what matters.\"\n---\n{/* KEEP: lead-in = many departments running at once pull you ten directions. This chapter builds the one room you steer from: a cockpit above every folder where you see the whole operation on one screen and decide what matters. */}\n\nTen departments running at once will pull you in ten directions. This chapter builds the one room you steer from: a cockpit that sits above every folder, where you see the whole operation on one screen and decide what matters this week.\n\n{/* KEEP: create ONE top-level cockpit folder above every department; it does no work, it points at the work. From here you see everything and decide. Every other folder is an employee, this one is your desk. Show the cockpit tree. Bold-first: cockpit. */}\n\n## One hub steers it all\n\nCreate a single top-level folder, the **cockpit**, that sits above every department. Give it an obvious name so it sorts to the top:\n\n```\noperating-base/\n  _control/\n    planner.md      (what we're doing right now)\n    decisions.md    (every call, with the reason)\n    todos/          (the priority list)\n  marketing/\n  sales/\n  ...\n```\n\nIt does no work. It points at the work. Every other folder is an employee; this one is your desk, and it is where you stand to run everything.\n\n{/* KEEP: the heart of the hub = one planning file answering \"what are we doing right now\" (focus, roadmap, the few numbers that matter, money). Agents read it to prioritize, you read it to remember what you decided. When everything feels urgent, this file makes one thing win. Shown as a real artifact (planner.md sketch). todos/ glossed = the committed work the plan produces. */}\n\n## Set the current focus and priorities\n\nThe heart of the cockpit is one planning file that answers a single question: what are we actually doing right now.\n\n```\n# Planner\n\n## Focus (this week)\n- Get the first 10 paying users.\n\n## Roadmap\n- Now: onboarding emails. Next: pricing page test.\n\n## Numbers\n- 41 signups, 6 paying, $174 MRR.\n```\n\nAgents read it to know what to prioritize when they have a choice. You read it to remember what you decided. When everything feels urgent, this one file is what makes a single thing win. The `todos/` folder next to it holds the committed work the plan produces, each task one small file.\n\n{/* KEEP: decision log = every meaningful call gets ONE line, newest first, with the reason (why a feature is on, why this price, why that channel died). Stops you AND the agents relitigating the same call monthly and silently reversing past choices. The log is the operation's memory of its own judgment. Show decisions.md lines. Bold-first: decision log. */}\n\n## Keep a decision log\n\nEvery meaningful call gets one line in a **decision log**, newest first, with the reason attached:\n\n```\n# Decisions (newest first)\n\n- 2026-07-08: Price at $29/mo, not $19. Room to discount later.\n- 2026-07-06: Dropped the LinkedIn channel. No signups in 6 weeks.\n```\n\nThis is not bureaucracy. It is what stops you and your agents from relitigating the same decision every month, or quietly reversing a choice you made for a reason you have since forgotten.\n\n{/* KEEP: the point of the hub = a single entry point, one command that pulls the state of every area into one briefing and shows what needs you. You don't visit ten folders, you run one command, read one screen, act. Forward-points softly to Reporting (commands built there); gloss, don't teach mechanics. Do this now = create the cockpit with a planner file + decisions log, write today's focus + one recent decision. */}\n\n## Drive it with one command\n\nThe whole point of the cockpit is that it can be read in one move. It is laid out so a single command can pull the state of every area into one briefing, instead of you opening ten folders by hand.\n\nIn a running system that briefing has a fixed shape: the numbers that moved, what landed since you last looked, and the decisions waiting on you. You build that command later, when you set up reporting. For now, know that the cockpit is the thing it reads: one screen in, one decision out.\n\nThis prompt stands up the whole cockpit for you:\n\n```\nAct as a senior engineer setting up my operating system.\nAt the top of my operating base, create a _control folder\nwith: planner.md (my focus, roadmap, key numbers, money),\ndecisions.md (a dated log, newest first, each line with its\nreason), and a todos/ folder. Seed planner.md from what I\ntell you below.\n\nMy focus and top priorities right now:\n```\n\n**Do this now:** paste the prompt and describe your current focus, so your agent builds the cockpit and seeds it."
          },
          {
            "slug": "configuring-agents",
            "title": "Standing Up Your Agents",
            "label": "Configuration",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/ai-os/configuring-agents",
            "content": "---\ntakeaway: Roles are files\nshare: An agent with no configuration is a stranger who shows up every morning having forgotten the job. Configuration is a file that hands it the job description before it starts.\n---\n{/* KEEP: lead-in = an unconfigured agent is a stranger who forgot the job daily. Configuration = a file that hands it the job description before it starts. You set your project rules earlier; here you define a specific agent's role. CODE-FORWARD chapter. */}\n\nAn agent with no configuration is a stranger who shows up every morning having forgotten the job. Configuration is the file that hands it the job description before it starts. You already wrote your project's rules; this chapter defines a specific agent with a role of its own.\n\n{/* KEEP: a role is a FILE, not a paragraph you paste. Show the real shape: frontmatter (name, description, tools allowed) + standing instructions. A coworker defined in version control, not a mood set each session. Bold-first: role. */}\n\n## Give each agent a role\n\nA **role** is a small file, not a paragraph you retype into chat each time. In Claude Code it lives in `.claude/agents/`, and every serious tool has the same idea: a [YAML](https://yaml.org) frontmatter block plus instructions.\n\n```\n---\nname: reviewer\ndescription: Reviews code after it's written. Use before any commit.\ntools: Read, Grep, Glob\n---\nYou review for correctness, security, and style. You do not\nwrite features and you do not refactor. Report issues by\nseverity, then stop. A hardcoded secret is a blocking issue.\n```\n\nThat is a coworker defined in a file your project keeps, not a mood you set and lose each session.\n\n{/* KEEP: a role carries two attachments, kept as SEPARATE composable files: skills = procedures loaded only when the task needs them; rules = hard lines, always on. Show the folder layout. Don't pour everything into one giant prompt. Bold-first: skill, rule. */}\n\n## Skills and rules per agent\n\nA role carries two kinds of attachment, and you keep them as separate files so they compose. A **skill** is a procedure loaded only when the task needs it. A **rule** is a hard line, always on.\n\n```\n.claude/\n  agents/reviewer.md         (the role)\n  skills/security-review.md  (loaded when reviewing auth code)\n  rules/no-secrets.md        (always on, every turn)\n```\n\nThat layout is Claude Code's; every serious tool has an equivalent folder. You never pour all of it into one giant prompt. You attach the right skill to the right role and let the rest stay out of the way until it is needed.\n\n{/* KEEP: unconfigured agents DRIFT (same request, different shape Monday vs Friday); a rule file pins the shape. Show a real rules/output.md. The quality bar lives in a file, not in your memory of how you asked. */}\n\n## Configuration keeps output consistent\n\nUnconfigured agents drift: the same request gives a different shape of answer on Monday and Friday. A rule file pins the shape:\n\n```\n# rules/output.md\n- Answer in bullets, never a wall of text.\n- Back every claim about the system with a command you ran.\n- No \"should be\" or \"probably\". Check, then state.\n```\n\nNow the quality bar lives in a file, not in your memory of how you happened to ask last time.\n\n{/* KEEP: configuration is an ASSET, not a per-task chore. The role defined today serves every task that agent runs; the rule written once fires on every future turn. An hour standing an agent up right pays back on every job after. Config is committed alongside the code it governs. Do this now = create one role file for a job you repeat (a reviewer). */}\n\n## Write it once, reuse everywhere\n\nConfiguration is an asset, not a chore. The role you define today serves every task that agent ever runs, and the rule you write once fires on every future turn.\n\nAn hour spent standing an agent up correctly pays back on every job it does after. You are not configuring a task, you are configuring an employee, and the file is committed alongside the code it governs.\n\nThis prompt has your agent write its own role file, in your tool's format:\n\n```\nAct as a senior engineer configuring my agents. Create a\nrole file for a reviewer agent, in the format my tool uses:\nfrontmatter with name, description, and the minimum tools it\nneeds, then standing instructions to review for correctness,\nsecurity, and style and then stop, treating a hardcoded\nsecret as a blocking issue. Keep it short.\n\nMy tool and project:\n```\n\n**Do this now:** paste the prompt so your agent writes the reviewer role file, then read it and tighten one line yourself."
          },
          {
            "slug": "agent-memory",
            "title": "What the Agent Remembers",
            "label": "Memory",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/ai-os/agent-memory",
            "content": "---\ntakeaway: Always-read memory file\nshare: \"Agents are brilliant and amnesiac, they forget your whole world between sessions. A memory file fixes the amnesia: one file the agent reads at the start of every session so you never re-explain.\"\n---\n{/* KEEP: lead-in = agents forget everything between sessions; a memory file is one file the agent auto-reads at session start so it walks in already knowing your world. You met the idea as a rules file (Part 2); this is the real per-tool file + the discipline that keeps it useful. CODE-FORWARD. */}\n\nAgents are brilliant and amnesiac. Close one and reopen it, and your project, your rules, your whole world are gone. A memory file fixes that: one file the agent reads automatically at the start of every session, so it walks in already knowing the room instead of asking you again.\n\n{/* KEEP: the concept, tightened (you met it as the rules file in Part 2). The memory file holds what is ALWAYS true: who you are, how you work, the project, what must never happen. Show a real CLAUDE.md-style example. It's the constitution, not the encyclopedia. */}\n\n## The file your agent reads first\n\nYou met this idea as your rules file. The **memory file** is that file, made real for each tool: it holds what is always true, who you are, how you work, what must never happen, in a few lines the agent reads before it does anything.\n\n```\n# Project: my-app\n\n- Stack: Next.js + Postgres. One language, TypeScript.\n- New code goes under src/, one folder per feature.\n- Never commit secrets. Read them from .env.\n- Run the app and confirm it works before saying done.\n```\n\nKeep it the constitution, not the encyclopedia: the standing law, not every detail.\n\n{/* KEEP: every serious tool has ONE always-loaded file; name the real ones (swappable). Table: tool -> file. The lesson: find yours and set it up first, not as an afterthought. Real tools named + linked. */}\n\n## Find your tool's file\n\nEvery serious agent tool auto-reads one file. Same idea, different name:\n\n| Tool | File it reads |\n| --- | --- |\n| [Claude Code](https://claude.com/claude-code) | `CLAUDE.md` at the repo root |\n| [Codex](https://developers.openai.com/codex) | `AGENTS.md` at the repo root |\n| [Cursor](https://cursor.com) and most editors | a rules file or `.cursor/rules/` folder |\n\nFind yours and treat it as the first thing you set up, not an afterthought. Any equivalent works; the file changes, the idea does not.\n\nThere is now a shared convention for this, [AGENTS.md](https://agents.md), a single file over 30 tools read the same way. If your tool supports it, one file covers most of them at once.\n\n{/* KEEP: plug-in pattern = for an agent with NO native memory file, keep one canonical file yourself and inject it at session start (by hand or a tiny wrapper). Mechanism varies, principle holds: the standing truth reaches the agent before it acts. */}\n\n## No built-in file? Inject it yourself\n\nIf your agent has no native memory file, you build the behavior. Keep one canonical file, `context.md`, and paste it (or have a tiny wrapper prepend it) at the start of each session.\n\nThe mechanism does not matter. The principle does: the standing truth of your project reaches the agent before it does anything else.\n\n{/* KEEP: keep it short = every line is re-sent every turn, so bloat costs on every request forever. Cap it, push topical detail into skills loaded on demand, fix a line the day a fact changes. The MEMORY.md index: one line per durable fact, detail in linked files. */}\n\n## Keep it short, or it rots\n\nEvery line of a memory file is re-sent on every turn, so bloat is a tax you pay forever, on requests that never even touch the topic. Keep it brutally short: only what must be true always. Push topical detail into skills the agent loads on demand.\n\nThis is measured, not a hunch. Studies across thousands of projects put the useful ceiling around 150 lines; past that, the file stops helping and quietly raises cost by roughly a fifth. If yours is longer, it is doing too much.\n\nFor facts that pile up, keep an index instead of a wall:\n\n```\n# MEMORY.md\n- Auth: we use email + magic link. Details in auth/notes.md.\n- Billing: Stripe, test mode until launch. See money/stripe.md.\n```\n\nOne line per durable fact, the detail in a linked file. Fix any line the day the fact changes; a short current file beats a long stale one every time.\n\n{/* KEEP: how memory gets WRITTEN, not just read. Don't hand-maintain it: tell the agent to save the fact when you correct it, so the file grows itself. But two disciplines or it rots: one fact per file/line, and a recalled memory that names a file or flag is a hint to VERIFY against live reality, never a fact to act on (old memory = old documentation). */}\n\n## Let the agent write it, but verify what it recalls\n\nYou do not maintain this by hand. When you correct the agent, tell it to save the correction, and the file grows itself: session 1 you fix a mistake, session 40 it never repeats because the note loads every time.\n\nTwo disciplines keep it from rotting. One fact per line, deduped. And a recalled line that names a file or a setting is a hint to check, never a fact to trust: a note written months ago can point at something since renamed. Treat old memory like old documentation, verify against reality before acting on it.\n\n{/* KEEP: prompt = have the agent create the right memory file for the reader's tool, seeded from their project, kept short. Ends with Do this now. */}\n\n## Set up your memory file\n\nThis prompt has your agent create the right file for your tool:\n\n```\nAct as a senior engineer setting up my agent's memory.\nFind the memory file my tool auto-reads (CLAUDE.md,\nAGENTS.md, or a rules file), create it at the right place,\nand seed it with my stack, where code goes, and my\nnon-negotiables. Keep it short, only what must be true\nevery session. Put topical detail in separate files.\n\nMy tool and project:\n```\n\n**Do this now:** paste the prompt so your agent writes its own memory file, then read it and cut any line that is not true on every single session."
          },
          {
            "slug": "agent-tools",
            "title": "Connecting Your Agent to the World",
            "label": "Tools",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/ai-os/agent-tools",
            "content": "---\ntakeaway: Check, don't guess\nshare: \"A model with no tools can only think and talk. Tools are how it reaches out and touches the real world: your database, your browser, your repo, your accounts, so it checks instead of guessing.\"\n---\n{/* KEEP: lead-in = a model with no tools can only think and talk; tools let it touch the real world (database, browser, repo, accounts) so it checks instead of guessing. CODE-FORWARD. */}\n\nA model with no tools can only think and talk. Tools are how it reaches out and touches the real world: your database, your browser, your repository, your accounts. With them, the agent stops guessing and starts checking.\n\n{/* KEEP: MCP (Model Context Protocol) = the standard plug for connecting an agent to an external system; register a server in config, the agent gains real actions. Show the MCP JSON config. Real tools named + swappable. Bold-first: MCP. Link: modelcontextprotocol.io. */}\n\n## What MCP is\n\n**MCP** (the Model Context Protocol) is the standard plug for connecting an agent to an outside system. You register a server in a config file and the agent gains a set of real actions, one adapter shape for every integration instead of hand-wiring each:\n\n```\n{\n  \"mcpServers\": {\n    \"postgres\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@modelcontextprotocol/server-postgres\",\n               \"postgresql://localhost/app\"]\n    }\n  }\n}\n```\n\nClaude Code reads this from an `.mcp.json` file; most tools read something like it. The servers are swappable; the plug is the same. See [modelcontextprotocol.io](https://modelcontextprotocol.io).\n\n{/* KEEP: with connections wired the agent stops guessing and starts checking: queries the live DB instead of assuming a number, drives a real browser to test a page, opens PRs on GitHub, calls APIs you pay for. Show a real query. \"the agent just looked.\" */}\n\n## Databases, browsers, GitHub, APIs\n\nOnce it is wired, \"the agent cannot know that\" becomes \"the agent just looked.\" Instead of assuming a number, it runs the query:\n\n```\nSELECT count(*) FROM users\nWHERE created_at > now() - interval '7 days';\n```\n\nInstead of describing a bug it drives a real browser and reads the console. Instead of narrating a change it opens the pull request, the change packaged up for review. Each connection turns a guess into a fact.\n\n{/* KEEP: custom tools = when no server exists, write one: a small named function with a TYPED input so the model calls it correctly every time. Show a @tool example. You give it a few exact levers, not everything. */}\n\n## Custom tools\n\nWhen no ready-made server exists, you write one, in whatever language your tool's SDK uses. A custom tool is a small named function with a typed input, so the model calls it the same way every time:\n\n```python\n@tool\ndef send_alert(message: str, level: Literal[\"info\", \"critical\"]) -> str:\n    \"\"\"Notify the operator. Use 'critical' only for outages.\"\"\"\n    post_to_channel(message, level)\n    return \"sent\"\n```\n\nYou are not teaching the agent everything. You are handing it a few exact levers that do the specific things your system needs.\n\n{/* KEEP: when a tool beats a prompt = if you explain the same procedure in prose more than twice, make it a tool. Prompts = judgment; tools = exact, repeatable actions. Move the mechanical into typed tools, leave the prompt for thinking. */}\n\n## When a tool beats a prompt\n\nThe rule of thumb: if you explain the same procedure in prose more than twice, it wants to be a tool. Prompts are for judgment; tools are for actions that must be exact and repeatable.\n\n\"Carefully format the currency like this, round like that\" is a tool waiting to be written. Move the mechanical into tools, and leave the prompt for thinking.\n\n{/* KEEP: prompt = have the agent wire up one MCP server for the reader's stack (or write one custom tool) so it can check reality. Ends with Do this now. */}\n\n## Wire up your first tool\n\nThis prompt connects your agent to something real:\n\n```\nAct as a senior engineer connecting my agent to my stack.\nPick the one connection that would help most right now (my\ndatabase, my repo, or my browser), set up its MCP server in\nmy tool's config, and show me one real check it can now run\ninstead of guessing.\n\nMy stack and what I want it to see:\n```\n\n**Do this now:** paste the prompt and wire up one connection, then ask your agent a question it can only answer by actually looking."
          },
          {
            "slug": "multiple-agents",
            "title": "When One Agent Isn't Enough",
            "label": "Multi-Agent",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/ai-os/multiple-agents",
            "content": "---\ntakeaway: More agents, each smaller\nshare: \"One agent doing everything is a solo founder doing every job: it works until it doesn't. The fix is not a bigger agent, it is more agents, each doing less, handing work to the next.\"\n---\n{/* KEEP: lead-in = one agent doing everything is a solo founder doing every job; works until it doesn't. The fix isn't a bigger agent, it's more agents each doing less. CODE-FORWARD. */}\n\nOne agent doing everything is a solo founder doing every job. It holds up until it does not. The fix is not a bigger agent, it is more agents, each doing less, each handing clean work to the next.\n\n{/* KEEP: signs one agent isn't enough = loses the thread on long tasks; plans+builds+reviews in one breath so the review is soft (same mind grading its own work); context fills; quality sags on anything with several moving parts. */}\n\n## Signs one agent isn't enough\n\nYou feel it before you can name it. The agent starts strong and loses the thread on a long task. It plans, builds, and reviews in one breath, and the review is soft because the same mind that wrote the code is grading it.\n\nIts context fills up, and quality sags on anything with more than a few moving parts. Those are the signals that the work has outgrown a single seat.\n\n{/* KEEP: the first and most powerful split is by PHASE: one plans, one builds, one reviews with fresh eyes. The reviewer catches what the builder can't see precisely because it didn't write it. Separation of roles = separation of blind spots. */}\n\n## Planner, builder, reviewer\n\nThe first and strongest split is by phase. One agent plans the work, another builds it, a third reviews it with fresh eyes.\n\nThis mirrors how real teams work, and for the same reason: the reviewer catches what the builder cannot see, precisely because it did not write the thing. Separation of roles is separation of blind spots.\n\n{/* KEEP: beyond phases, spin up specialists (security reviewer, test writer, researcher), each with its own skills + narrow standard of \"good.\" A specialist beats a generalist on its home turf because its whole config is bent toward one job. Each is a role file (ref configuring-agents). */}\n\n## Specialized agents\n\nBeyond the phases, you spin up specialists: a security reviewer, a test writer, a researcher. Each is a role file of its own, with its own skills and its own narrow standard of good.\n\nA specialist beats a generalist on its home turf, because its whole configuration is bent toward one kind of excellence. You assemble a team, not a hero.\n\n{/* KEEP: the team is only as good as its handoffs; one agent's output must become the next's input cleanly. File-based handoff: agent A writes a file with frontmatter (source, verdict); agent B reads it and stamps it (planned_in) so it isn't re-read. The FILE is the contract. Show the frontmatter. */}\n\n## Handoffs between them\n\nA team is only as good as its handoffs. One agent's output has to become the next one's input with nothing lost in the gap. The cleanest handoff is a file: agent A writes it, agent B reads it.\n\n```\n---\nsource: error-report\nverdict: needs-fix\nhandled_by: null\n---\nCheckout throws on empty cart. Repro + fix below.\n```\n\nThe builder writes that, the reviewer reads it and stamps `handled_by` so it is never picked up twice. The file is the contract between agents, so no context lives only in one agent's head.\n\nSomeone has to run the team, and it is simpler than it sounds: one lead agent spawns the others with a clear brief each, waits, and reads back their summaries to decide what happens next. The children do not talk to each other. Reads fan out freely; writes to the same file stay with one agent, so two never edit it at once and clobber each other.\n\n{/* KEEP: prompt = split the reader's one agent into a builder + a separate reviewer with a clean file handoff. Ends with Do this now. */}\n\n## Split your first team\n\nThis prompt turns your one agent into a small team:\n\n```\nAct as a senior engineer setting up a two-agent workflow.\nGive me a builder role and a separate reviewer role in my\ntool's format, and a simple file-based handoff: the builder\nwrites what it changed to a file, the reviewer reads that\nfile and reports issues before I commit. Keep both short.\n\nMy tool and project:\n```\n\n**Do this now:** paste the prompt, set up the builder and reviewer, and run one change through both so you see the reviewer catch what the builder missed."
          },
          {
            "slug": "scheduling-your-agents",
            "title": "Putting Agents on a Clock",
            "label": "Scheduling",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/ai-os/scheduling-your-agents",
            "content": "---\ntakeaway: Put work on a clock\nshare: \"The best work an agent does is the work you never asked for, because it was already on the calendar. Scheduling is how your system runs while you sleep and you wake up to finished work.\"\n---\n{/* KEEP: lead-in = the best work an agent does is the work you never asked for because it was on the calendar; scheduling is how the system runs while you sleep. CODE-FORWARD, cron-simple. */}\n\nThe best work an agent does is the work you never asked for, because it was already on the calendar. Scheduling is how your system stops waiting for you and starts running on its own, so you wake up to work that is already done.\n\n{/* KEEP: until now every agent waited for you to start it; scheduling flips that: put an agent on a clock and it runs on its own, on time, whether you show up or not. The system stops being a tool you operate and becomes one that operates. */}\n\n## Agents that run without you\n\nUntil now, every agent waited for you to press go. Scheduling flips that. You put an agent on a clock and it runs on its own, on time, whether or not you show up.\n\nThat is the moment the system stops being a tool you operate and starts being one that operates, doing the rounds because the clock struck, not because you remembered.\n\n{/* KEEP: look at everything you do on a rhythm (weekly numbers, daily health check, monthly money review); each is a scheduled agent. Show a cron line (plain). Write the job once, set cadence, runs forever. The important-but-never-urgent work finally gets done because a clock doesn't procrastinate. Bold-first: cron. */}\n\n## Put the routine work on a clock\n\nLook at everything you do on a rhythm: the weekly numbers, the daily health check, the monthly money review. Every one is a scheduled agent waiting to exist.\n\nYou set its cadence with a [**cron**](https://crontab.guru) line, five fields that mean \"run at this time\":\n\n```\n# min hour day month weekday\n0 8 * * *      # every day at 08:00\n0 9 * * 1      # every Monday at 09:00\n```\n\nWrite the job once, give it a cadence, and it runs forever. The work that is important but never urgent, and so never gets done, finally does, because a clock does not procrastinate.\n\nThat format is cron, which is built into Mac and Linux. Windows has the same thing under a different name, Task Scheduler. You do not set either up by hand: you tell your agent the cadence and let it wire the right one for your machine.\n\n{/* KEEP: two ways to fire the same job: on a schedule (every morning) or on demand (one command, now). You want both. Nod that event-driven triggers come later. A job runner (Prefect-style) exists for scale, one line, swappable. */}\n\n## Schedule it, or trigger it on demand\n\nThe same job has two doors. On a schedule, it runs every morning at eight. On demand, you run one command and it goes now. You want both: the schedule for the rhythm, the manual run for the moment you need the report early.\n\nA plain cron runs this on your machine; a [job runner](https://prefect.io) handles it at scale when you have many. Same idea, bigger engine.\n\n{/* KEEP: a scheduled run without safety+observability rots silently. The three things that make an unattended run safe: it checks it's still allowed before acting (gate), it records what it did (ledger, built next chapters), and something alerts you if it STOPS firing (watchdog). The scary failure of a scheduler is silence, not a crash. Numbered flow = device variety in the back half. */}\n\n## Make an unattended run safe\n\nA job that runs while you sleep needs more than a clock, or it fails in the dark and you find out late. Three things make an unattended run trustworthy:\n\n1. **Gate:** before it acts, the agent confirms it is still allowed to (the guardrails of the next chapter). It fixes what is safe and escalates the rest, never spending or deleting on its own.\n2. **Record:** every run writes what it did to one log (you build this in the ledger chapter), so \"what ran last night\" has one honest answer.\n3. **Watchdog:** something alerts you when a run stops firing. A scheduler does not fail loudly; it goes quiet, so you alert on the missing run, not just on errors.\n\n{/* KEEP: the payoff = open your laptop and the reports are written, checks run, problems flagged or fixed; you review work that already happened. The system ran a full shift without you. */}\n\n## Wake up to finished work\n\nThis is the payoff, and it changes how the whole thing feels. You open your laptop and the reports are already written, the checks already run, the problems already flagged or fixed.\n\nYou are not starting the day's work. You are reviewing work that already happened. The system ran a full shift while you slept.\n\n{/* KEEP: prompt = take one routine task the reader does on a rhythm and turn it into a scheduled agent with a cron + a job definition. Ends with Do this now. */}\n\n## Put one job on a clock\n\nThis prompt turns a routine chore into a scheduled agent:\n\n```\nAct as a senior engineer automating my routine work. Take\nthe one task I describe below, write it as a small job my\nagent can run on its own, and schedule it at the right\ncadence using whatever my operating system provides (cron on\nMac or Linux, Task Scheduler on Windows). Show me how to run\nit once by hand to test it first.\n\nMy operating system and the routine task (and how often):\n```\n\n**Do this now:** paste the prompt, pick one thing you do on a rhythm, and let your agent turn it into a job on a clock."
          },
          {
            "slug": "autonomy-guardrails",
            "title": "What Runs Alone, What Waits for You",
            "label": "Autonomy",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/ai-os/autonomy-guardrails",
            "content": "---\ntakeaway: Safe alone vs escalate\nshare: \"Autonomy without guardrails is a loaded gun on a timer. Sort every action into safe-to-run-alone and must-wait-for-you, so the tireless work happens and only the risky calls reach you.\"\n---\n{/* KEEP: lead-in = this is the safety layer that keeps autonomy from hurting you. Autonomy without guardrails = a loaded gun on a timer. MUST come right after Scheduling. CODE-FORWARD. */}\n\nYou just put agents on a clock. That is power, and power without a brake is dangerous: an agent that can act on its own, on a schedule, can also break things on its own. This chapter is the brake, the line between what runs alone and what waits for you.\n\n{/* KEEP: not every action is equal; reading a dashboard and deleting an account are not the same risk. Sort every action into two buckets: safe to run alone (reversible, low blast radius) vs must wait for a human yes (money, real user data, security, removing a capability). The sort is the whole game. */}\n\n## Not every action is equal\n\nReading a dashboard and deleting a customer's account are not the same risk, so they must not have the same freedom. You sort every action an agent can take into two buckets.\n\nReversible, low-risk work runs on its own. Anything that touches money, real user data, security, or removes a capability stops and asks. That sort is the whole game.\n\n{/* KEEP: give agents a standing rule: fix what you're allowed to fix; for everything else, don't act, drop a card. Safe repair -> do it and log it. Risky (raise the bill, swap a vendor, change a price) -> write a short decision card, leave it in the queue. The agent never grows the stakes on its own. */}\n\n## Fix what's safe, escalate what's not\n\nGive every autonomous agent one standing rule: fix what you are allowed to fix, and for everything else, do not act, leave a card.\n\nWhen a scheduled agent finds a problem it can safely repair, it repairs it and logs it. When it finds something only you should decide, raising the bill, swapping a vendor, changing a price, it writes a short **decision card** and stops. The agent never raises the stakes on its own.\n\n{/* KEEP: the decision queue = all escalations land in ONE place, one card each (one question, one recommendation); you clear it on your schedule, approving or rejecting. This is what makes autonomy safe to live with: the system does the tireless work and hands you only the small set of real judgment calls. Show a decision card. */}\n\n## The decision queue\n\nEvery escalation lands in one place: a queue of decisions waiting on you. Each is one card, one question, one recommendation.\n\n```\n# decision: raise the DB plan?\nWhy: we hit 90% of storage twice this week.\nRecommend: upgrade one tier (+$25/mo).\nOptions: approve / hold / do something else\n```\n\nYou clear the queue on your own schedule, approving or rejecting each. An approved card becomes a line in the decision log back in your cockpit, so the call and its reason are recorded, not lost. This is what makes autonomy safe to live with: the system does the tireless work and hands you only the handful of calls that would hurt to get wrong.\n\n{/* KEEP: the hard lines can't live in your head or a mood; they live in rules files, always on, phrased as absolutes (never change billing without a yes, never delete without confirmation, never message a customer without approval). Each rule names a specific way things go wrong. Short, brutal, permanent, fires every turn. */}\n\n## Write the hard lines down, once\n\nThe guardrails cannot live in your memory. They live in a rules file, always loaded, phrased as absolutes:\n\n```\n# rules/guardrails.md\n- Never change billing or pricing without an explicit yes.\n- Never delete user data without confirmation.\n- Never send anything to a customer without approval.\n```\n\nEvery rule earns its place by naming a specific way things go wrong. The list is short, blunt, and permanent, and it fires on every turn whether the agent thinks to consider it or not.\n\nA rule the agent reads is a rule it can talk itself out of over a long, tired session. For the lines that must never break, add a second layer: a **hook**, a small script your tool runs automatically that mechanically blocks the action, no judgment involved. The rule tells the agent what not to do; the hook makes it impossible. Anything that spends money, deletes real data, or ships to production belongs behind a hook, not just a sentence.\n\n{/* KEEP: prompt = have the agent split its own actions into safe/escalate and write the guardrail rules file for the reader's system. Ends with Do this now. */}\n\n## Draw your own lines\n\nThis prompt sets your guardrails before you let anything run alone:\n\n```\nAct as a senior engineer setting safety guardrails for my\nautonomous agents. From what my system can touch, list which\nactions are safe to run alone (reversible, low risk) and\nwhich must wait for my approval (money, user data, security,\ndeletions). Then write a short always-on rules file that\nencodes the must-wait lines as absolutes.\n\nWhat my agents can do and touch:\n```\n\n**Do this now:** paste the prompt, get your safe/escalate split and a guardrails rules file, and add it to your agents' always-on rules before any of them run on a schedule."
          },
          {
            "slug": "reports-and-commands",
            "title": "Commands and What Comes Back",
            "label": "Reporting",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/ai-os/reports-and-commands",
            "content": "---\ntakeaway: Command down, report up\nshare: \"A system you cannot see is one you cannot steer. Reporting is the two-way pipe: you send one command down, the work sends truth back up, and you sit at the top of the loop, not inside it.\"\n---\n{/* KEEP: lead-in = a system you can't see is one you can't steer. Reporting = the two-way pipe: commands down, reports up. You sit at the top of the loop, not inside it. CODE-FORWARD. */}\n\nA system you cannot see is a system you cannot steer. Reporting is the two-way pipe that lets you run the whole thing from one seat: you send commands down, the work sends truth back up, and you stay at the top of the loop instead of buried inside it.\n\n{/* KEEP: the shape of control: direction flows DOWN from you as commands, reality flows UP from agents as reports. You point the system at what matters and read back what it found. Down goes intent, up comes evidence, you sit where they meet. */}\n\n## You command down, they report up\n\nThe shape of control is simple. Direction flows down from you as commands. Reality flows up from the agents as reports.\n\nYou do not do the tasks. You point the system at what matters, and you read back what it found. Down goes intent, up comes evidence, and you sit where the two meet.\n\n{/* KEEP: commands are your interface; don't operate by remembering ten folders and twenty scripts. A small set of named commands: one for the whole picture, one per area to go deeper. The command is one word; behind it is all the machinery. A good command set is the entire UI of your system. Show the command set. */}\n\n## Commands are your interface\n\nYou should not run the system by remembering ten folders and twenty scripts. You run it through a small set of named commands, each a single word that hides a mountain of machinery:\n\n```\n/status        # the whole picture, one screen\n/status growth # traffic, users, money\n/status tech   # errors, cost, agents\n```\n\nOne to see everything, one per area to go deeper. A good command set is the entire user interface of your operation.\n\nA command is a real file, not magic: in Claude Code each one is a markdown file in `.claude/commands/` (`status.md` becomes `/status`), holding the instructions the command runs. Your tool has the same idea under its own name. You write the workflow once; the slash name is the shortcut.\n\n{/* KEEP: reports are what comes back; every agent that works leaves a report (what it checked, found, did, what needs you), in a KNOWN place and KNOWN shape so you scan many fast. The report compresses a night of work into five minutes of reading, so you spend attention only on deciding. Show report frontmatter. */}\n\n## Reports are what comes back\n\nEvery agent that does work leaves a report: what it checked, what it found, what it did, and what needs you. They land in a known place, in a known shape, so you can scan many of them fast:\n\n```\n---\narea: money\nverdict: needs-you\n---\nRevenue up 4%. One failed payout, decision card filed.\n```\n\nA keyed shape like that is what turns a night of autonomous work into five minutes of reading. It compresses the doing so you spend your attention only on the deciding. Reports land in their own `reports/` folder, separate from the department status files: a status file is a department's current truth, a report is one run's findings for you.\n\n{/* KEEP: keep the loop tight = the danger is a leaky loop (reports nobody reads, commands that no longer match the folders, a gap between what the system did and what you think it did). Fight it: one command surface, one report format, one place the truth lives. The tighter the loop, the more you can trust it, and trust is what lets you let go. */}\n\n## Keep the loop tight\n\nThe danger is a loop that leaks: reports nobody reads, commands that no longer match the folders, a gap between what the system did and what you think it did.\n\nYou fight that constantly with three disciplines: one command surface, one report format, one place the truth lives. The tighter the loop, the more you can trust it, and trust is the only thing that lets you actually let go.\n\n{/* KEEP: prompt = have the agent set up the reader's command surface (one status command + a report format) over their operating base. Ends with Do this now. */}\n\n## Build your command surface\n\nThis prompt gives you one seat to run everything from:\n\n```\nAct as a senior engineer building my command surface. Over\nmy operating base, create one command that reads every\ndepartment's status file and its latest reports into a\nsingle briefing, showing what needs me. Define one short\nreport format (keyed frontmatter) that every agent writes.\n\nMy operating base and departments:\n```\n\n**Do this now:** paste the prompt, set up your one status command and report format, then run the command once and read your whole operation on a single screen."
          },
          {
            "slug": "event-ledger",
            "title": "One Log of Everything That Happened",
            "label": "Ledger",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/ai-os/event-ledger",
            "content": "---\ntakeaway: One append-only log\nshare: \"Reports tell you what happened this morning. A ledger tells you everything that ever happened, in order. The one place to answer 'what led to this' when something goes wrong.\"\n---\n{/* KEEP: lead-in = reports = this morning; a ledger = everything that ever happened, in order. LIGHTER chapter (idea + when you need it), flag that a beginner may not need it yet. One small artifact. */}\n\nReports tell you what an agent found this morning. A ledger tells you everything that has ever happened, in order. You will not need it on day one, but the first time you ask \"what led to this?\" and cannot answer, this is the missing piece.\n\n{/* KEEP: under the folders and agents, one append-only log of events (deploy happened, signup came, alert fired, job finished). Each meaningful thing writes one line. Not a report you read top to bottom, the raw record everything pulses through. Show a small event log. */}\n\n## Events are the nervous system\n\nUnderneath the folders and the agents, you keep a **ledger**: one running log of **events**. A deploy happened. A signup came in. An alert fired. A job finished. Every meaningful thing writes one line:\n\n```\n2026-07-10T08:00Z  job.finished    growth-daily ok\n2026-07-10T08:14Z  signup          user=4821\n2026-07-10T09:02Z  alert.fired     errors spiking\n```\n\nIt is not a report you read top to bottom. It is the raw record the whole system pulses through, the thing everything else can look back at.\n\n{/* KEEP: one rule makes it trustworthy: append only, never edit. A line once written stays; you never rewrite history. That's what lets you reconstruct exactly what happened weeks later, the truth, not a tidied summary. */}\n\n## Append only, never edit\n\nThe ledger has one rule that makes it trustworthy: you only ever add to it. You never rewrite a line.\n\nThat is what lets you reconstruct exactly what happened, and in what order, weeks later when something broke and you need the truth, not a summary someone tidied up. Its value is that nobody got to clean it.\n\n{/* KEEP: the power shows when local AND cloud write to the SAME ledger: the agent on your laptop and the service in production log to one stream, giving one timeline of the whole system. Ask \"what led to this\", one place to look, complete answer. */}\n\n## One place, both worlds\n\nThe real power shows up when your local work and your live service write to the same ledger. The way it works is one small rule: everything that logs an event calls the same single writer, the agent on your laptop and the app in production both append through one door, so there is one stream and not two.\n\nNow you have a single timeline of the whole system, machine and human, code and business, in one order. This is the third and last record surface, and each has one job: a department's status file is its current truth, a report is one run's findings for you, the ledger is the raw timeline for looking back. When you ask \"what led to this,\" the ledger is the one place to look.\n\n{/* KEEP: a durable event stream isn't just for looking back, it's the surface agents can WATCH and react to; once you have one honest log, you can build agents that fire the instant an event lands. This sets up Triggers. Flag WHEN to add a ledger. */}\n\n## The ledger makes reflexes possible\n\nA durable stream of events is not only for looking back. It is the surface an agent can watch, so it can react the instant a certain event lands. That is the next chapter.\n\n> **Hint:** you do not need a ledger while it is just you and a few daily agents. Add one when you cannot answer \"what led to this,\" or when you want agents that react to events. Your plain log is the simple version; at scale the industry standard for this is [OpenTelemetry](https://opentelemetry.io/docs/specs/semconv/gen-ai/), which records the same kind of timeline in a format built for it.\n\n{/* KEEP: prompt = start a simple append-only event log the reader's agents write one line to. Ends with Do this now. */}\n\n## Start your ledger\n\nThis prompt gives your system a memory of its own history:\n\n```\nAct as a senior engineer adding an event log to my system.\nCreate one append-only file my agents write a single line\nto whenever something meaningful happens (a job finished, a\nsignup, an alert). Give each line a timestamp, an event\nname, and a short detail. Never edit past lines.\n\nMy system and the events worth logging:\n```\n\n**Do this now:** paste the prompt, start the log, and have one of your scheduled agents append a line every time it runs."
          },
          {
            "slug": "triggers",
            "title": "Agents That React, Not Just Wait",
            "label": "Triggers",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/ai-os/triggers",
            "content": "---\ntakeaway: Event-driven reflexes\nshare: \"Scheduling put agents on a clock, but some work can't wait for the clock. A trigger is a reflex: an agent that fires the instant something happens, so the gap between it broke and we responded is near zero.\"\n---\n{/* KEEP: lead-in = scheduling put agents on a clock, but some work can't wait for the clock, it must happen the instant something occurs, that's the reflex. LIGHTER chapter, pairs with the ledger. */}\n\nScheduling put your agents on a clock. But some work cannot wait for the clock: it has to happen the instant something occurs. That is the other half of autonomy, the reflex, and it is what this short chapter adds.\n\n{/* KEEP: a scheduled agent asks \"is it time yet\"; a triggered agent asks \"did the thing happen\". One is a heartbeat (steady, periodic), the other a reflex (dormant until an event, then instant). A serious system has both. */}\n\n## Scheduled is proactive, triggered is immediate\n\nA scheduled agent asks, \"is it time yet?\" A **triggered** agent asks, \"did the thing happen?\"\n\nThe first is a heartbeat, steady and periodic. The second is a **reflex**, dormant until an event pokes it, then instant. A serious system has both: the heartbeat does the rounds, the reflex catches the fire the moment it starts.\n\n{/* KEEP: some problems can't wait for the next sweep: a payment fails, a key expires, an error spikes. If your only mechanism is a schedule, damage runs until the next tick. Reflexes are for events where the gap between \"it happened\" and \"we responded\" must be near zero. Smoke detector, not a morning check. */}\n\n## Some problems can't wait for the next sweep\n\nA payment fails. A key expires. An error spikes. If your only mechanism is a schedule, the damage runs until the next tick fires.\n\nReflexes exist for exactly the events where the gap between \"it happened\" and \"we responded\" has to be near zero. You do not schedule a smoke detector to check for fire each morning; you wire it to react the instant there is smoke.\n\nHere is the whole loop on one event. An error lands in the ledger. The reflex wakes, pulls the real context, the stack trace, the recent changes, the live state, and works out what broke. Then it splits on the guardrails you already set: if the fix is safe and reversible it makes it, proves it with a test, and ships it through review; if it would cost money or touch user data, it does not act, it files a decision card and stops. Investigating is always safe, so that part is fully automatic; only the risky call waits for you.\n\n{/* KEEP: a reflex = an agent watching the event stream for a specific line; when that event lands, it fires, does its one job, goes back to sleep. This is why the ledger comes first: no honest event stream = nothing to react to. Show a tiny trigger rule (on event -> run agent). */}\n\n## Triggers listen to the ledger\n\nA reflex is an agent watching the event stream for one specific line. Something has to do the watching, and the simplest version is a small watcher on a tight loop: it reads new lines in the ledger every few seconds, and when a line matches, it runs the right agent. Bigger setups let the alerting tool itself call a web address the moment it fires, but the idea is the same, a rule that maps an event to an agent:\n\n```\non event  alert.fired    -> run  incident-responder\non event  payment.failed -> run  billing-retry\n```\n\nThis is why the ledger comes first: without one honest stream of events, there is nothing to watch. With it, any event can become a trigger.\n\n{/* KEEP: heartbeat + reflex = complete coverage of time. Scheduled agents handle the rhythm (routine on a cadence); triggered agents handle the surprises (events that arrive on their own). Between them nothing falls through. Flag lighter: add reflexes when you have events worth reacting to. */}\n\n## Together they cover the whole clock\n\nHeartbeat plus reflex is complete coverage of time. The scheduled agents handle the rhythm of the work, the routine that happens on a cadence. The triggered agents handle the surprises, the events that arrive on their own and demand a response now.\n\nBetween the two, nothing falls through: if it happens on a clock, the heartbeat has it; if it just happens, a reflex catches it.\n\n> **Hint:** reach for reflexes once you have a ledger and a few events that genuinely cannot wait for the next scheduled run.\n\n{/* KEEP: prompt = wire one reflex: watch for a specific event in the ledger and run an agent when it lands. Ends with Do this now. */}\n\n## Wire your first reflex\n\nThis prompt gives your system its first reflex:\n\n```\nAct as a senior engineer adding an event-driven trigger to\nmy system. Pick the one event that most needs an instant\nresponse, watch my event log for it, and run the right agent\nthe moment it lands. Use an approach that works on my\noperating system. Keep the agent's job small and safe,\ninside the guardrails I already set.\n\nMy operating system, event log, and the event that can't wait:\n```\n\n**Do this now:** paste the prompt and wire one reflex to the single event you would hate to catch a day late."
          }
        ]
      },
      {
        "id": "architect",
        "title": "Lay out a modular codebase for your AI",
        "chapters": [
          {
            "slug": "modular-foundation",
            "title": "A Foundation That Bends",
            "label": "Modularity",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/architect/modular-foundation",
            "content": "---\ntakeaway: Modular monolith first\nshare: \"The one architecture decision that keeps an AI-built app changeable: build it as a modular monolith, clean parts inside one app, from the very first file, so it bends instead of breaks as it grows.\"\n---\n\n{/* KEEP: lead-in = you are about to have the agent generate the app; left alone it pours every feature into one growing tangle that works Friday and cannot be touched Monday. This chapter gives the one architecture decision that keeps it changeable: build it as separate parts from the first file. */}\n\nYou are about to ask your agent to build the app, and it will happily generate whatever runs. Left alone, it pours every feature into one growing tangle that works on Friday and cannot be safely touched by Monday. This chapter gives you the one architectural decision that keeps the app changeable as it grows: build it as separate parts from the very first file.\n\n{/* KEEP: separation of concerns = each part does one job and knows as little as possible about the others; payments handle payments, users handle users, neither reaches into the other's internals. You do not enforce this by hand, you tell the agent the areas are distinct and hold it to that on every feature. Bold-first: separation of concerns. */}\n\n## Separate concerns from the start\n\nThe oldest rule in software is **separation of concerns**: each part of the app does one job and knows as little as possible about the others. Payments handle payments, accounts handle accounts, and neither reaches into the other's internals.\n\nYou do not enforce this by hand. You tell the agent the app is made of distinct areas that each stay behind their own door, and you hold it to that every single time it adds a feature.\n\n{/* KEEP: modular monolith = one program you run and deploy as a single thing, split inside into clear modules with clean lines between them; one app outside, many tidy rooms inside. It is the right starting architecture for almost everyone: simplicity of one codebase plus internal boundaries. Bold-first: modular monolith. */}\n\n## Start with a modular monolith\n\nA **modular monolith** is one program you run and deploy as a single thing, split inside into clear modules with clean lines between them. One app on the outside, many tidy rooms on the inside.\n\nThis is the right starting architecture for almost everyone. You get the simplicity of one codebase to run, build, and debug, plus the internal boundaries that let you change one area without disturbing the rest.\n\n{/* KEEP: two failure modes on either side. big ball of mud = no boundaries at all, every part reaches into every other, one change ripples everywhere (link laputan.org/mud). premature microservices = splitting into separate deployed services too early, a solo builder trades a problem they have (organizing code) for ones they do not (networks, versioning, distributed failure). Link Fowler MonolithFirst. Bold-first: big ball of mud, premature microservices. */}\n\n## Avoid the mud ball and premature microservices\n\nTwo failure modes sit on either side. The [**big ball of mud**](http://www.laputan.org/mud/) is what you get with no boundaries at all: every part reaches into every other, and one change ripples everywhere at once.\n\nThe opposite mistake is splitting the app into separate deployed services too early, [**premature microservices**](https://martinfowler.com/bliki/MonolithFirst.html). A solo builder who does this trades a problem they have, organizing code, for problems they do not: networks between services, version mismatches, and failures spread across machines.\n\n{/* KEEP: clean modules let the app bend instead of break: a feature drops into the area it belongs to, a bug stays contained to one room, you hand the agent one module at a time without the whole app in its head. And if you truly outgrow the single app, clean boundaries are the seams you cut along, a module lifts out into its own service, a mud ball gets rewritten. Not choosing against scale, earning the right to it later. */}\n\n## A foundation that bends instead of breaks\n\nClean modules are what let the app bend instead of break. A new feature drops into the area it belongs to, a bug stays contained to one room, and you can hand your agent one module at a time without it needing the whole app in its head.\n\nIf you ever genuinely outgrow the single app, the boundaries you drew are the seams you cut along. A module with a clean edge lifts out into its own service; a mud ball has to be rewritten. You are not choosing against scale, you are earning the right to it later.\n\n**Do this now:** before you generate a line of the app, write down the three or four distinct areas it is made of (say accounts, billing, notifications) and tell your agent to build each as its own module with a clean boundary, not one shared pile."
          },
          {
            "slug": "folder-structure",
            "title": "Where Everything Lives",
            "label": "Structure",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/architect/folder-structure",
            "content": "---\ntakeaway: Organize by feature\nshare: Your agent scatters one feature across five type-folders and nobody can find anything. Give each feature its own folder, keep one home for shared code, and you and your agent navigate the codebase the same way.\n---\n\n{/* KEEP: lead-in = agent drops files wherever, codebase is a pile, you and the agent both lose the code for a feature and it writes duplicates. This chapter gives a folder shape organized by what the app does, one both of you navigate without a map. */}\n\nYou have been letting your agent drop new files wherever it lands, and the codebase is now a pile. You cannot find the code for one feature without opening ten folders, and neither can the agent, so it writes a second copy instead. This chapter gives you a folder shape organized around what the app does, one both you and your agent navigate without a map.\n\n{/* KEEP: the agent's default is file-type folders (components/, hooks/, services/) which smears one feature across all of them. Feature-based (a.k.a. package by feature) puts everything a feature needs in one folder, so the shape of the folders matches the shape of the app. Bold-first: file-type folders, feature-based. */}\n\n## Organize by feature, not by file type\n\nLeft alone, an agent reaches for **file-type folders**: a `components/` bin, a `hooks/` bin, a `services/` bin, everything of one kind piled together. The trouble is that one feature, billing say, ends up smeared across all of them, so touching it means hunting through five folders.\n\nOrganize the opposite way. A **feature-based** layout (also called package by feature) puts everything a single feature needs in one folder, so the shape of the folders matches the shape of the app.\n\n{/* KEEP: each feature gets its own folder under src/features/, holding its screen + logic + data access + test side by side. Adding a feature = adding a folder, removing one = deleting a folder. Show the real folder tree. Frame: your agent writes this, here is the shape. */}\n\n## One folder per feature\n\nGive each feature its own folder under `src/features/`, and keep its screen, its logic, its data access, and its test side by side inside it. Your agent writes this, here is the shape:\n\n```\nsrc/\n  features/\n    billing/\n      BillingPage.tsx\n      useInvoices.ts\n      billing.api.ts\n      billing.test.ts\n    auth/\n      LoginForm.tsx\n      useSession.ts\n      auth.api.ts\n  shared/\n    ui/           (buttons, inputs used everywhere)\n    lib/          (dates, money, formatting)\n    types.ts\n```\n\nAdding a feature is now adding a folder, and removing one is deleting a folder. Nothing about billing lives anywhere but `billing/`.\n\n{/* KEEP: some code belongs to no single feature (a button on every screen, a money formatter, a date helper): that is shared code, one home in a shared/ folder next to features/. Be strict: code stays with its feature until a SECOND feature needs it. Rule-of-thumb callout. Bold-first: shared code. */}\n\n## A home for shared code\n\nSome code belongs to no single feature: a button used on every screen, a function that formats money, a date helper. That is **shared code**, and it gets one home, a `shared/` folder sitting next to `features/`.\n\nBe strict about what earns a place there, or `shared/` becomes its own junk drawer.\n\n> **Rule of thumb:** keep code inside its feature folder until a second feature actually needs it, then move it to `shared/`.\n\n{/* KEEP: payoff = a predictable, uniform structure lets the agent know where to add new code and where to find existing code. Tell it the convention once and it stops scattering files and stops writing duplicates. The structure does the organizing. Do this now = have the agent lay out src/features + shared/ for your app. */}\n\n## Structure the agent can navigate\n\nA predictable structure is a gift to your agent. When every feature looks the same and shared code has one address, the agent knows exactly where to add new code and where to find what already exists.\n\nTell it the convention once and it stops scattering files and stops writing duplicates. The structure does the organizing, so you are not correcting placement on every task.\n\nThis prompt has your agent lay the tree out for you:\n\n```\nAct as a senior engineer laying out my codebase.\nOrganize it by feature, not by file type. Under\nsrc/features, give each feature its own folder, and\nco-locate its screen, its logic, its data access,\nand its test inside that folder. Put code shared by\ntwo or more features in a src/shared folder. Keep a\nfeature's code in its own folder until a second\nfeature needs it. Show me the folder tree first,\nthen create it.\n\nMy app and its features:\n```\n\n**Do this now:** paste the prompt above and list your app's features, so your agent lays out the folder tree for you."
          },
          {
            "slug": "coupling-and-cohesion",
            "title": "Keeping Pieces Independent",
            "label": "Coupling",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/architect/coupling-and-cohesion",
            "content": "---\ntakeaway: Low coupling, high cohesion\nshare: When one small change breaks three unrelated things, your modules are too tangled. Loose coupling and tight cohesion keep a change where you made it, and let your agent work on one piece without holding the whole app in its head.\n---\n\n{/* KEEP: lead-in = the reader's real pain, one small change breaks unrelated things because modules reach into each other. This chapter gets them to spot that tangle and ask for looser seams. Do NOT teach folders/layers/naming here. */}\n\nYour agent ships fast, and then a one-line change to how orders are priced breaks the invoice screen and the email receipt too. That happens when your modules reach into each other instead of talking at arm's length. This chapter gets you the two words that name the problem, and how to point your agent at looser seams.\n\n{/* KEEP: concept = define BOTH. coupling = how much one module depends on another's insides. cohesion = how focused a module is on one job. Bold-first: coupling, cohesion. Keep it plain, one sentence each. */}\n\n## Coupling and cohesion, defined\n\n**Coupling** is how much one module depends on the inner workings of another. Two modules are tightly coupled when changing one forces you to change the other; loosely coupled when each can change on its own.\n\n**Cohesion** is how focused a single module is. A cohesive module does one clear job and holds only the things that job needs, instead of being a junk drawer of unrelated code.\n\n{/* KEEP: why low coupling matters = a change stays contained. tight coupling = a change ripples out to places you did not touch; that ripple is the source of \"it broke and I do not know why\". Loose coupling caps the blast radius. */}\n\n## Keep a change from spreading\n\nTight coupling is why a small edit ripples into code you never opened. When one module knows how another stores its data, that knowledge is a wire, and every wire is a path for a change to travel down.\n\nLoose coupling caps the blast radius. Cut the wires and a change stays where you made it, which is the whole reason you can move fast without holding the entire app in your head.\n\n{/* KEEP: THE artifact. talk through an INTERFACE (a named contract, the promise a module makes) not internals. Bold-first: interface. Show tiny TS/pseudocode: caller reaching into order.items vs calling order.total(). The point = ask, do not reach. */}\n\n## Talk through interfaces, not internals\n\nThe fix is to make each module expose an **interface**, the short list of things it promises to do, and keep everything else private. Callers use the promise; they never reach past it.\n\n```ts\n// Reaching into internals: the caller knows how an Order\n// stores its lines, and breaks the day that changes.\nlet total = 0;\nfor (const line of order.items) {\n  total += line.price * line.qty;\n}\n\n// Talking through the interface: the caller just asks.\n// Order owns how it adds up; change it freely.\nconst total = order.total();\n```\n\n> **Rule of thumb:** if a module reads another's fields to do its work, it is reaching through the wall. Ask for a method that does the job instead.\n\n{/* KEEP: synthesis = the goal is high cohesion + low coupling together, one job per module, arm's-length between them. Ends with prompt (audit ONE module for tight coupling, propose looser seams) + Do this now. */}\n\n## High cohesion, low coupling\n\nThe target is both at once: each module does one job well, and modules stay at arm's length from each other's insides. Your agent will not aim for this unless you ask, so ask.\n\n```\nAct as a senior engineer auditing one module for\ncoupling and cohesion. Do two passes.\n\nCoupling: list every place this module reaches into\nanother module's internals (reads its fields, knows\nhow it stores data, depends on its private shape).\nFor each, propose a method or interface to call\ninstead, so the two can change independently.\n\nCohesion: name this module's one job. Flag anything\ninside it that belongs to a different job and should\nmove out.\n\nShow the current seams and the looser ones you would\nreplace them with. Do not rewrite yet; propose first.\n\nThe module I'm worried about:\n```\n\n**Do this now:** paste the prompt, name one module you keep breaking by accident, and have your agent map its wires before you cut them."
          },
          {
            "slug": "boundaries-and-layers",
            "title": "Drawing the Boundaries",
            "label": "Layers",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/architect/boundaries-and-layers",
            "content": "---\ntakeaway: Dependencies point inward\nshare: 'Inside every piece of your app, code splits into three layers: presentation, business logic, and data. Draw the boundary between them and point every dependency one way, so a change stays local instead of rippling out.'\n---\n\n{/* KEEP: lead-in = you named the five pieces, but inside one piece the code is a tangle (a screen running its own query, a rule buried in a button). Payoff: draw boundaries INSIDE a piece, three layers with one rule about which way they depend. Distinct from the-pieces-of-an-app (physical parts) and coupling (independence metrics). */}\n\nYou named the five pieces of your app, but inside any one of them the code is usually a single tangle: a screen that runs its own database query, a pricing rule buried in a button handler. When everything can touch everything, one change ripples everywhere and your agent cannot tell you what a file is for. This chapter draws the boundaries inside a piece: three layers, with one clean rule about which way they depend.\n\n{/* KEEP: define the three layers, each changes for its own reason. Bold-first: layer, presentation layer, business logic, data layer. Presentation = what the user sees/touches. Business logic = the rules that make it YOUR app. Data = storage. */}\n\n## Presentation, logic, data\n\nSlice any part of your app into three **layer**s, each with one job. The **presentation layer** is what the user sees and touches: screens, buttons, the shape of a form. The **business logic** is the rules that make it your app and not a generic one: what a discount is, who may cancel an order, when a booking is valid. The **data layer** is storage: reading and writing rows, talking to the database or a file.\n\nThe point of the split is that each layer changes for its own reason. You redesign the screen without touching a pricing rule, and you swap the database without rewriting what a discount means.\n\n{/* KEEP: a layer offers a short list of calls and hides the rest; the layer above calls one of them and never skips to reach below. Narrow boundary = you can rewrite behind it without opening the caller. Sets up the leak. Keep distinct from coupling's interfaces section: this is specifically the seam BETWEEN layers. */}\n\n## How modules talk\n\nA layer offers the one above it a short list of calls and hides everything else behind them. The presentation layer calls `placeOrder(cart)`, never sees the query the logic runs underneath, and never skips a layer to reach the one below it.\n\nKeep that boundary narrow on purpose. If the screen only ever asks for `placeOrder`, you can rewrite how orders are placed without opening a single screen file.\n\n{/* KEEP: dependencies point ONE way, inward toward the stable core (the business logic). Presentation and data depend on it; it depends on neither and must never import a screen or a db driver. Fenced sketch with both edges pointing IN. Reversing an arrow glues the durable core to the parts that change most. */}\n\n## Dependencies point one way\n\nEvery dependency points one direction: inward, toward the stable core. The business logic is that core. The presentation and data layers depend on it, it depends on neither, and it must never import a screen or a database driver.\n\n```\n  Presentation           Data\n  (screens, forms)   (database, files)\n       |                   |\n       | depends on        | depends on\n       v                   v\n       Business logic  =  the stable core\n       (the rules that make it your app)\n```\n\nBoth edges point in. Screens and storage are swappable details, and the rules at the center stay put. Reverse an arrow, so the core imports the UI, and you have glued the part of your app that should outlive every redesign to the part that changes most.\n\n{/* KEEP: layer leak = a call that jumps the boundary (UI running a query, a business rule written into a screen). Real artifact: leak vs fixed. The leak works today and rots tomorrow because the rule now lives in a click handler. Bold-first: layer leak. Do this now = paste the audit prompt. */}\n\n## Don't let a layer leak\n\nA **layer leak** is a call that jumps the boundary: the presentation layer running a query straight against the database, or a business rule written into a screen.\n\n```\n# Leak: the screen reaches into the data layer\nbutton.onClick = () =>\n  db.query(\"UPDATE orders SET status='paid' ...\")\n\n# Fixed: the screen calls the logic; logic owns the rule\nbutton.onClick = () => payForOrder(orderId)\n```\n\nThe first line works today and rots tomorrow. The rule for paying an order now lives in a click handler, so the next screen that pays an order either duplicates it or gets it wrong. Route the call through the layer that owns the rule and the leak closes.\n\nThis prompt has your agent sort your code into layers and check the arrows:\n\n```\nAct as a senior engineer auditing my architecture.\nSort my code into three layers: presentation (the UI),\nbusiness logic (the rules), and data (storage). For\neach file or module, name which layer it belongs to.\nThen check the dependency direction: the business\nlogic must not import the UI or the database library,\nand the UI must not run queries or hold business\nrules. Flag every place a layer reaches past its\nneighbor or points the wrong way, and give the one\nmove that fixes each.\n\nMy app's layers today:\n```\n\n**Do this now:** paste the prompt so your agent maps your files onto the three layers and flags every leak, then fix the one that points the wrong way."
          },
          {
            "slug": "api-design",
            "title": "Contracts That Last",
            "label": "API Design",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/architect/api-design",
            "content": "---\ntakeaway: Design the contract first\nshare: 'Your frontend and backend agree on a shape: what a request looks like, what comes back, what an error looks like. Fix that agreement once, keep it consistent, and version it carefully, so a change on one side never silently breaks the other.'\n---\n\n{/* KEEP: lead-in = frontend and backend talk over an agreed shape; the API is that agreement. Design the shape first, keep it consistent, version it so nothing breaks silently. Payoff = a contract that lasts. CODE-FORWARD. */}\n\nYour frontend asks your backend for data, and the backend answers. The exact shape of that exchange, what you send and what comes back, is a promise both sides rely on. Change it carelessly and every screen that read the old shape breaks at once. This chapter gets you a contract that stays stable as the app grows.\n\n{/* KEEP: API = the set of endpoints your app exposes; contract = the agreed request/response shape; endpoint = one URL + method. Decide the shape BEFORE the agent writes handlers, so both sides build to the same agreement. Bold-first: API, contract, endpoint. */}\n\n## Design the contract first\n\nYour **API** is the set of addresses your app exposes for others to call. Each one is an **endpoint**: a single URL plus a method (`GET /posts`, `POST /posts`) that does one job. The **contract** is the agreed shape of every request and response those endpoints exchange.\n\nDecide that shape before the agent writes a single handler. When the request and response are pinned down first, the frontend and backend can be built in parallel against the same agreement instead of guessing at each other.\n\n{/* KEEP: every response uses ONE consistent envelope: success flag, data, error, meta (pagination). Show the JSON artifact. Then the error shape reuses the same envelope. Errors carry a stable code + message + a real HTTP status. Bold-first: envelope. Link MDN status on first mention. */}\n\n## Consistent shapes and errors\n\nWrap every response in one consistent **envelope**, the same outer shape whether the call succeeds or fails. Success flag, the data, the error, and a `meta` block for pagination:\n\n```json\n{\n  \"success\": true,\n  \"data\": { \"id\": 42, \"title\": \"First post\" },\n  \"error\": null,\n  \"meta\": { \"page\": 1, \"perPage\": 20, \"total\": 137 }\n}\n```\n\nA failure reuses the same envelope, flips the flag, and fills `error` instead of `data`:\n\n```json\n{\n  \"success\": false,\n  \"data\": null,\n  \"error\": { \"code\": \"not_found\", \"message\": \"No post with id 42\" }\n}\n```\n\nGive each error a stable `code` your frontend can branch on, a human `message`, and a real HTTP status ([MDN lists them](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status)): `200` for success, `404` for not found, `422` for bad input.\n\n{/* KEEP: version so existing clients keep working. Put a version in the path (/v1/). Additive changes (a new optional field) are safe and stay in v1; anything that removes or renames = breaking = a new version (/v2), old one kept alive. Semantic versioning names this rule. Link semver.org. */}\n\n## Versioning without breaking clients\n\nPut a version in the path from day one (`/v1/posts`). Adding a new optional field is safe: old clients ignore it, so it stays in `v1`. Removing or renaming a field breaks anyone reading the old shape, so that goes in a new `/v2` while `v1` keeps running.\n\nThis is exactly the split [semantic versioning](https://semver.org) formalizes: additive is a minor change, breaking is a major one. You do not cut clients off, you give them a version to move to on their own schedule.\n\nThis prompt hands the whole contract to your agent:\n\n```\nAct as a senior engineer designing my API contract.\nDefine one consistent response envelope for every\nendpoint: a success flag, a data field, an error\nobject, and a meta block for pagination. Give me the\nexact JSON for a success and an error response, a small\nset of stable error codes, and the HTTP status each\nmaps to. Then propose a versioning scheme (path or\nheader) and the rule for what counts as a breaking\nchange versus a safe additive one, so existing clients\nnever break.\n\nMy API and what it returns:\n```\n\n**Do this now:** paste the prompt with your endpoints, let your agent define the envelope, error codes, and versioning rule, then hold every new endpoint to that one shape."
          },
          {
            "slug": "conventions-and-naming",
            "title": "Naming Things Consistently",
            "label": "Conventions",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/architect/conventions-and-naming",
            "content": "---\ntakeaway: Write the conventions down\nshare: 'Left to itself, an agent writes each new file in a slightly different style, and the codebase turns into a patchwork. Conventions are the one-hand rules you write down once so every file matches.'\n---\n\n{/* KEEP: lead-in = an agent left alone writes each file in a slightly different style; the codebase becomes a patchwork nobody can read at a glance. Chapter = pick one style, name things one way, write it in a file the agent follows so consistency is enforced not remembered. */}\n\nLeft to itself, your agent writes each new file in whatever style feels right that session: camelCase here, snake_case there, one file with tabs and the next with spaces. Each piece works, but the codebase reads like five people who never met wrote it. This chapter gets you one consistent hand across the whole project, enforced by a file, not by your memory.\n\n{/* KEEP: a convention = a repeated decision made once and reused everywhere. The specific choice matters less than sticking to it. Mechanical formatting (quotes, spacing, semicolons) is settled by a formatter/linter so nobody argues it. Bold-first: convention. Link Prettier + ESLint official only. */}\n\n## Pick one way and keep it\n\nA **convention** is a decision you make once and then repeat everywhere: quote style, indentation, how files are named, how a boolean reads. Which way you pick matters far less than picking one and never drifting. Two reasonable styles applied consistently beat one perfect style applied half the time.\n\nThe purely mechanical decisions (spacing, quotes, trailing commas) you should not decide by hand at all. A formatter like [Prettier](https://prettier.io) rewrites every file to one layout on save, and a linter like [ESLint](https://eslint.org) flags the rest, so that whole category stops being a discussion.\n\n{/* KEEP: naming is where \"one hand\" is won or lost. Standard buckets: components PascalCase, functions/vars camelCase, files kebab-case, constants UPPER_SNAKE_CASE, booleans is/has/should/can. Three-column table Category|Inconsistent|Consistent. */}\n\n## Names that read as one hand\n\nNaming is where a codebase either reads like one author or like a committee. The standard buckets are worth adopting as-is: types and components in `PascalCase`, variables and functions in `camelCase`, filenames in `kebab-case`, constants in `UPPER_SNAKE_CASE`, and booleans that start with `is`, `has`, `should`, or `can` so they read like a yes-or-no question.\n\n| Category | Inconsistent                    | Consistent              |\n| -------- | ------------------------------- | ----------------------- |\n| Function | `GetUser`, `fetch_user`         | `getUser`, `fetchUser`  |\n| Boolean  | `active`, `loaded`              | `isActive`, `hasLoaded` |\n| File     | `UserCard.tsx`, `user_card.tsx` | `user-card.tsx`         |\n\n{/* KEEP: write the conventions in a file the agent reads (ties to the rules file from last chapter). Show the real artifact: a short conventions block. Consistency becomes enforced, not remembered. */}\n\n## Write the conventions down\n\nRules you only hold in your head get applied when you remember and skipped when you do not. Put them in the rules file your agent already reads every session, so it writes to them from the first line instead of you correcting them after.\n\nKeep it short and concrete, the kind of block you can paste straight in:\n\n```\n## Conventions\n- Components/types: PascalCase. Vars/functions: camelCase.\n- Filenames: kebab-case. Constants: UPPER_SNAKE_CASE.\n- Booleans read as a question: isActive, hasLoaded.\n- Prettier owns formatting; do not hand-format.\n- One component per file; match the filename to it.\n```\n\n{/* KEEP: consistency > any one person's taste. Don't relitigate a settled style; the win is uniformity, not winning the argument. Let the formatter own mechanical style. Do this now = have the agent write the conventions into the rules file and apply going forward. */}\n\n## Consistency beats personal taste\n\nYour preference for single quotes is not worth a codebase split down the middle. Once a convention is written, treat it as settled and let the agent apply it everywhere, including code you would have styled differently. The value is uniformity, not being right.\n\nThis prompt has your agent record your conventions and follow them from now on:\n\n```\nAct as a senior engineer. Read my project and infer the\nnaming and style conventions already in use. Where they\nconflict, pick one consistent choice per category\n(casing for types, functions, files, constants; boolean\nnaming; quote and format style) and tell me the calls you\nmade. Write them into my rules file as a short Conventions\nsection, then apply them to any code you touch from now on.\nKeep it tight and concrete.\n\nMy stack and current style:\n```\n\n**Do this now:** paste the prompt so your agent writes the conventions into your rules file, then read the choices it made and change any single one you disagree with."
          },
          {
            "slug": "architecture-handoff",
            "title": "Teaching Your AI the Architecture",
            "label": "Handoff",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/architect/architecture-handoff",
            "content": "---\ntakeaway: Give the agent a map\nshare: 'Your agent knows your rules but not where things live, so new code lands anywhere. An architecture map in the file it reads every session tells it where each feature goes, so its code extends your structure instead of fighting it.'\n---\n\n{/* KEEP: lead-in = the always-read file tells the agent WHAT to do (rules) but not WHERE things live, so new code lands anywhere and fights the structure. This chapter adds the architecture map: a compact picture of the codebase in that same file so new code lands right. Builds on the memory/rules file, folder structure, layers, conventions taught earlier; point, don't re-teach. */}\n\nYour agent already reads a file every session that tells it your stack and your non-negotiables. But that file says what to do, not where anything lives, so new code lands wherever the agent guesses: a feature split across the wrong folders, a database call inside a button, a structure it invents instead of yours. This chapter gives the agent a map of your codebase in that same file, so its code extends your architecture instead of fighting it.\n\n{/* KEEP: concept = the architecture map is a compact picture of the codebase (where features live, the layer rule, the naming rule, where new code goes) added to the always-read file. It turns the agent from guessing to placing. Bold-first: architecture map. */}\n\n## Hand the agent the map\n\nAn **architecture map** is a compact picture of your codebase, added to the file your agent reads first: where features live, how the layers stack, what things are named, and where new code goes. It is the same thing a senior would tell a new hire before they touch anything.\n\nWithout it, the agent reverse-engineers your structure from whatever files it happened to open, and gets it wrong half the time. With it, the agent places code instead of guessing at it.\n\n{/* KEEP: the real artifact = show a compact map in the always-read file (AGENTS.md / CLAUDE.md). Rules plus structure in one place. Link agents.md on first mention, verified. Show the fenced example: where features live, layer rule + import direction, naming rule, \"new code goes here\". */}\n\n## Rules plus structure in one file\n\nThe map lives in the same always-read file as your rules, the one most tools share as [AGENTS.md](https://agents.md) or call `CLAUDE.md`. Rules say how to behave; the map says where the code goes. The agent needs both in front of it every session.\n\nKeep it a map, not an essay. A dozen lines that a beginner could not have written but an agent can follow exactly:\n\n```\n## Architecture\n\n- Features live in src/features/<name>/, self-contained.\n- Layers: ui -> hooks -> lib -> db. A layer imports\n  only downward, never up or sideways.\n- Shared code in src/lib/, shared UI in src/ui/.\n- Naming: components PascalCase, hooks use*, files\n  kebab-case.\n- New feature = a new folder under src/features/.\n  Nothing goes in the root. Match the nearest\n  neighbor's structure.\n```\n\n{/* KEEP: payoff = the map's \"new code goes here\" + layer rule + naming rule are what make output land correctly and read like the rest of the code. Not one-off, it holds across every session because the file is always read. */}\n\n## New code lands in the right place\n\nThat last line does the heavy lifting. \"New feature goes under `src/features/`, match the nearest neighbor\" turns a vague instruction into a placement the agent cannot miss, and the layer rule stops it from wiring a database call straight into the UI. The naming line makes the new file look like it was always there.\n\nBecause this lives in the always-read file, it holds on every session, not just the one where you happened to explain it. The agent stops inventing structure and starts filling in yours.\n\n{/* KEEP: keep it current = the map goes stale as the code grows (new layer, new top-level folder), and a stale map misroutes worse than none. Don't hand-maintain: when you add a real structural piece, have the agent update the map in the same change. Old map = old documentation, same discipline as the memory file. */}\n\n## Keep the handoff current\n\nThe map is only as good as its accuracy. Add a new top-level folder or a new layer and forget to update the map, and the agent follows the old one straight into the wrong place. A stale map misroutes worse than no map at all.\n\nDo not maintain it by hand. When you and the agent add a real structural piece, have it update the map in the same change, the way your memory file grows itself when you correct it. Treat a map written months ago like old documentation: check it against the real folders before you trust it.\n\n{/* KEEP: prompt = have the agent read the codebase and write a compact, accurate architecture map into the reader's always-read file. Senior voice, expert content fixed, one append slot at bottom, lines <65 chars. Ends with Do this now. */}\n\n## Write the map into your file\n\nThis prompt has your agent read the code and write the map:\n\n```\nAct as a senior engineer documenting my codebase for\nan engineer joining today. Read my project, then add\nan Architecture section to the file my agent reads\nevery session (CLAUDE.md, AGENTS.md, or my rules file).\nKeep it a compact map, not prose: where each feature\nlives, the layer rule and its allowed import direction,\nthe naming convention, and one line saying where new\ncode goes. Infer the real structure from the code, do\nnot invent one. Keep it under fifteen lines, and flag\nanything the code does inconsistently.\n\nMy structure, layers, and conventions:\n```\n\n**Do this now:** paste the prompt so your agent writes the map, then read it against your real folders and cut any line the code does not actually follow."
          },
          {
            "slug": "documentation",
            "title": "Writing for Humans and AI",
            "label": "Docs",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/architect/documentation",
            "content": "---\ntakeaway: README, ADR, CHANGELOG\nshare: 'An architecture only you can explain is a liability. Write it down: a README, a decision record, a changelog. Three plain files that make your system legible to the next human and to the agent.'\n---\n\n{/* KEEP: lead-in = the architecture you designed lives in your head and your spec; undocumented, the next session (agent or human) is lost. This chapter = the three files that make the system legible: README, ADRs, CHANGELOG. Last chapter of the Architect part: docs are what you carry into building. */}\nYou have the whole architecture now: the pieces, the data, the decisions behind them. Right now it lives in your head and a spec, which means the next person to open this project, a teammate, a future you, or your agent on a fresh session, starts blind. This chapter gives you three plain files that write the architecture down so nobody has to reconstruct it.\n\n{/* KEEP: README = the front door. What it is, why it exists, how to run it. First file anyone (and the agent) opens. Keep it plain. */}\n\n## The README is your project's front door\n\nA **README** is the file at the top of your project that answers the first questions anyone asks: what is this, why does it exist, and how do I run it. It is the first thing a teammate opens and the first thing your agent reads to orient itself.\n\nKeep it to what a newcomer needs on day one: a one-line description, the setup steps, how to start the app, and where the important docs live. If the README is current, someone can go from clone to running in minutes without asking you.\n\n{/* KEEP: ADR = record WHY a decision was made, dated, short. Architecture docs describe the shape; ADRs capture the reasoning so it survives. Show a real ADR template. Link adr.github.io. */}\n\n## Architecture docs and ADRs record why you chose this\n\nAn architecture doc describes the shape of the system. An **ADR**, an [architecture decision record](https://adr.github.io), captures one decision and the reasoning behind it: short, dated, written the moment you choose. Six months on, nobody remembers why you picked Postgres over a document store, and the ADR is the answer.\n\nWrite one when a choice is hard to reverse or easy to question later. The format is deliberately small:\n\n```\n# ADR 0001: Use Postgres, not a document store\n\nDate: 2026-07-20\nStatus: Accepted\n\n## Context\nWe need relational queries across users, orders, and\ninvoices, with strong consistency at checkout.\n\n## Decision\nUse Postgres as the primary database.\n\n## Consequences\nJoins and transactions are easy. We add a search\nindex later if full-text needs grow.\n```\n\n{/* KEEP: CHANGELOG = track what changed over time, human-readable, newest first. Link keepachangelog.com. Optional conventional commits link. */}\n\n## The CHANGELOG tracks what changed\n\nA **CHANGELOG** is a running, human-readable list of notable changes, newest at the top, grouped by version. It answers \"what is different since last time\" without anyone reading the git history. The widely used convention is [Keep a Changelog](https://keepachangelog.com), which groups entries under Added, Changed, Fixed, and Removed.\n\nYou do not write it from scratch each release. If your commit messages follow a convention like [Conventional Commits](https://www.conventionalcommits.org), the agent drafts the changelog from them and you edit for clarity.\n\n{/* KEEP: payoff = these docs are the agent's context. It reads README/ADRs/CHANGELOG the way it reads the memory file, so keeping them current is not admin, it directly makes the agent build correctly. Closing nod: the documented architecture is what you carry into building. */}\n\n## Your agent reads all of this before it builds\n\nThese files are not paperwork for humans alone. Your agent reads the README to orient, the ADRs to respect decisions you already made, and the CHANGELOG to see where the project stands. Stale docs mislead it exactly as they mislead a person.\n\nThat is the payoff of this whole part: a documented architecture is a system the agent can hold in its head and build against, one tested piece at a time. It will not stay clean on its own, though. Code decays as it grows, so keeping this structure healthy means refactoring it continuously, on a regular cadence and not just when something hurts, which a later part turns into a habit. This prompt gives you the starter set:\n\n```\nAct as a senior engineer documenting my project.\nCreate three starter files:\n- README.md: one-line description, setup steps, how\n  to run it, and where key docs live.\n- docs/adr/0001-*.md: one ADR for a real decision I\n  have already made, using Context / Decision /\n  Consequences, dated, marked Accepted.\n- CHANGELOG.md: Keep a Changelog format, an\n  Unreleased section grouped Added/Changed/Fixed.\nKeep each file short and skimmable. Ask me before\ninventing any fact you do not have.\n\nMy project:\n```\n\n**Do this now:** paste the prompt, name your project and one real decision you made, and let your agent write the starter README, ADR, and CHANGELOG."
          }
        ]
      },
      {
        "id": "build",
        "title": "Implement the application in working slices",
        "chapters": [
          {
            "slug": "scaffolding",
            "title": "Blank Screen to Running App",
            "label": "Scaffolding",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/build/scaffolding",
            "content": "---\ntakeaway: Running app before features\nshare: \"The gap that stalls people is not the features, it is getting an empty folder to run at all. Scaffold from a boilerplate, put one thing on screen, and commit that running skeleton as your known-good baseline before any feature.\"\n---\n\n{/* KEEP: lead-in = reader has a spec + architecture + an empty folder; instinct is to generate the first feature, but there is nothing running for it to live in. This chapter gets them from empty folder to a running app skeleton they can see in a browser and commit as a baseline. */}\n\nYou have a spec, an architecture, and an empty folder. The instinct is to open your agent and generate the first feature, but there is nothing for that feature to live in yet: no app that runs, no page that loads. This chapter gets you from that empty folder to a running app skeleton you can see in a browser and commit as your known-good baseline.\n\n{/* KEEP: concept = first goal is NOT a feature, it is a running app that does almost nothing. A skeleton that boots, serves one page, proves the toolchain works end to end. Everything after drops into something already alive. Bold-first: skeleton. */}\n\n## From blank folder to running app\n\nThe gap that stalls people is not writing features, it is the plumbing before the first one: install the tools, wire the build, get a server to boot. Your first goal skips straight past that struggle.\n\nAim for a running app that does almost nothing useful yet, a **skeleton** that boots, serves one page, and proves the whole toolchain works end to end. Once it runs, every feature drops into something already alive, so a broken feature stands out against a baseline you know works.\n\n{/* KEEP: don't hand-assemble the plumbing. boilerplate = ready-made starter project; scaffolder = command that generates one. create-next-app is the fastest path for a typical web app. Show the real command + resulting file tree. Watch out: don't customize before it boots. Bold-first: boilerplate, scaffolder. Link create-next-app official. */}\n\n## Start from a boilerplate\n\nDo not hand-assemble the plumbing. A **boilerplate** is a ready-made starter project with the build, the config, and one working page already wired together, and a **scaffolder** is a single command that generates one in seconds. For a typical web app, [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app) is the fastest path from nothing to running:\n\n```\nnpx create-next-app@latest my-app\ncd my-app\nnpm run dev\n```\n\nThat leaves you a project that already boots at http://localhost:3000, with a shape like this:\n\n```\nmy-app/\n  app/\n    page.tsx      (the home page you see)\n    layout.tsx\n  public/\n  package.json\n```\n\n> **Watch out:** get the default booting and committed before you customize anything. If you tweak first and it breaks, you cannot tell whether the boilerplate or your change is at fault.\n\n{/* KEEP: before any feature, put ONE real thing on screen: your app's name. Edit the home page, save, watch the browser update on its own. This proves the change-a-file-see-the-result loop you live in all through building. Show the hello-on-screen snippet. */}\n\n## Get one thing on screen first\n\nBefore a single feature, put one thing on screen that is unmistakably yours. Open the home page the scaffolder made and replace it with your app's name:\n\n```tsx\n// app/page.tsx\nexport default function Home() {\n  return <h1>Ledger is running.</h1>;\n}\n```\n\nSave, and the page in your browser updates on its own. Seeing your own words render proves the loop you will live in all through building: change a file, see the result. That tiny win is the entire point of the skeleton.\n\n{/* KEEP: commit the running skeleton as a baseline before touching a feature. It is the last moment the project is guaranteed to work; every later change risks breaking it, and this commit is the point you can always return to. Show the git commit. Do this now = scaffold + one thing on screen + commit. */}\n\n## Commit the skeleton before features\n\nA running skeleton is the most valuable state your project will be in for a while: it works, and nothing you have added can have broken it yet. Lock it in with a commit before you touch a feature.\n\n```\ngit add -A\ngit commit -m \"chore: scaffold running skeleton\"\n```\n\nFrom here, every feature is a change against a known-good baseline. When something breaks, and it will, you can always get back to an app that ran. Steering your agent through those features, one small slice at a time, is what the rest of this part teaches.\n\nThis prompt has your agent scaffold that skeleton for your stack:\n\n```\nAct as a senior engineer scaffolding my project.\nPick the right scaffolder for my stack and use its\nofficial generator: create-next-app for a Next.js\nweb app, Vite for a plain single-page app, or the\nframework's own CLI otherwise. Generate the\nstarter, get it booting locally, and confirm the\ndev server serves one page in the browser. Then\nreplace that page with a single line showing my\napp's name, so I see my own output render.\nDo not add any feature yet. Show me the commands\nto run and the file tree they produce first.\n\nMy app idea and stack:\n```\n\n**Do this now:** paste the prompt, name your app and stack, and let your agent scaffold a running skeleton, then commit it before you build a single feature."
          },
          {
            "slug": "directing-your-agent",
            "title": "Steering Your Agent",
            "label": "Direction",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/build/directing-your-agent",
            "content": "---\ntakeaway: Give the goal, steer early\nshare: You set the goal and the constraints, then catch the agent at its first wrong turn instead of after it has built the wrong thing. This chapter is the steering discipline that keeps AI fast without letting it run off.\n---\n{/* KEEP: lead-in = agent is scaffolded and now runs off faster than you can read; the two failure modes are micromanaging every keystroke (lose the speed) and \"build my app\" then walk away (confident and wrong). This chapter = the middle path, point at the goal and steer before it drifts. */}\n\nYour agent is set up and the app skeleton runs. You type a request and it takes off, writing files faster than you can read them. Dictate every keystroke and you lose the speed you came for; say \"build my app\" and walk away and you get something confident and wrong. This chapter is the middle path: point the agent at the goal, and steer it before it drifts.\n\n{/* KEEP: concept = the agent is strongest given INTENT + boundaries, not a procedure. You own what \"done\" means and what must not break; it knows a thousand paths there. Two-column table contrasts keystroke-level instruction vs goal + constraints. */}\n\n## Give the goal, not the keystrokes\n\nThe agent is strongest when you hand it intent and boundaries, not a step-by-step procedure. You are the one who knows what \"done\" means and what must not break. It knows a thousand ways to get there, so describe the destination and let it pick the road.\n\n| Keystrokes (don't) | Goal + constraints (do) |\n| --- | --- |\n| \"Add a `role` prop to `UserCard`, then map over it\" | \"Show each user's role on their card\" |\n| \"Write a for-loop that sums the items\" | \"Total the cart, and handle an empty cart\" |\n\n{/* KEEP: slice = one small self-contained change; give the agent ONE, not the whole feature. Small enough to check in minutes AND small enough the agent still does it well. Point forward BY TOPIC to working-in-small-steps (save points / rollback), do NOT deep-dive commits here. Bold-first: slice. */}\n\n## Work one slice at a time\n\nHand the agent one **slice**, one small self-contained change that either works or does not, never the whole feature at once. A slice is small enough that you can check it in a couple of minutes, and small enough that the agent still has room to do it well.\n\nTurning each finished slice into a save point you can roll back to is its own discipline, and the chapter on working in small steps covers it. Here the rule is only about size: keep the task small enough to steer.\n\n{/* KEEP: check before accepting = look at what changed and confirm it does what you asked, not merely that it runs. Running != correct. Point forward BY TOPIC to reviewing-the-agents-code for HOW to read a diff; here the rule is just \"never accept a slice you haven't looked at.\" */}\n\n## Check its work before accepting\n\nBefore you accept a slice, look at what it changed and confirm it does what you asked. Running without an error is not the same as correct, and the agent will happily report success on the wrong thing.\n\nReading the agent's code closely is a skill of its own, covered when we get to reviewing what it wrote. For now the rule is blunt: never accept a slice you have not looked at.\n\n{/* KEEP: steer early = cost of a wrong turn grows with every line built on top of it; catch it at the first sign (a one-sentence fix) vs after 500 lines (untangling). Watch the FIRST file it touches and the plan it proposes, not the finished result. Rule-of-thumb callout. */}\n\n## Steer early, before it drifts\n\nThe cost of a wrong turn grows with every line built on top of it. Catch it at the first sign and one sentence corrects it. Catch it after five hundred lines and you are untangling. So watch the first file the agent touches and the plan it proposes, not just the finished result.\n\n> **Rule of thumb:** correct the agent on its first wrong turn, not its tenth. A nudge now is a rewrite later.\n\n{/* KEEP: prompt = a reusable steering brief that forces the agent to restate the goal, list constraints, propose the smallest first slice, and STOP before coding. Senior voice, expert content fixed at top, ONE append slot (My next feature:) at bottom, lines <65 chars. Ends with Do this now. */}\n\n## Turn any task into a steering brief\n\nThis prompt makes the agent slow down at the one moment that matters, before it writes code:\n\n```\nAct as a senior engineer. Before writing any code\nfor the task below, do this in order:\n\n1. Restate the goal in your own words.\n2. List the constraints and what must not break.\n3. Propose the smallest first slice, one change\n   I can verify in a couple of minutes.\n4. Stop and wait for my OK before coding.\n\nAfter I approve, build only that slice, then show\nme exactly what you changed and why.\n\nMy next feature:\n```\n\n**Do this now:** take the next thing you want built, paste the prompt above, and make the agent restate the goal and propose one slice before it writes a single line."
          },
          {
            "slug": "prompting",
            "title": "Instructing the Agent Well",
            "label": "Prompting",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/build/prompting",
            "content": "---\ntakeaway: One task, plan first\nshare: \"You reword the same request five times and keep getting the wrong code. The fix is not clever phrasing: it is four habits that make the agent build what you actually meant.\"\n---\n{/* KEEP: lead-in = reader rewords a request, agent produces wrong or sprawling code, blames own phrasing. Reframe: not a magic phrase. Chapter delivers the four habits (context, one task, show an example, plan first) that make the agent build the intended thing. */}\n\nYou type a request, the agent writes two hundred lines, and half of it solves a problem you never asked about. You reword it, try again, and get a different wrong answer. The fix is not a magic phrase. This chapter gives you the four habits that make an agent build the thing you actually meant.\n\n{/* KEEP [pro]: an agent is not a keyword box; it writes what your words PLUS everything it can see add up to, so context is the high-leverage move, not clever wording. Show weak vs strong prompt as real artifacts. Cite Anthropic prompt engineering docs on first mention. Tie back to the rules file (auto-context). Soft pointer to the next chapter (the window) without deep-diving. */}\n\n## Context beats clever wording\n\nAn agent is not a search box waiting for the right keyword. It writes what your words plus everything it can see add up to, so the highest-leverage move is handing it context, not hunting for a magic phrase. [Anthropic's prompt engineering guidance](https://docs.claude.com/en/docs/build-with-claude/prompt-engineering/overview) says the same thing: be clear and direct, and give the model what it needs.\n\nThese two prompts ask for the same feature. Only one works.\n\n```\nMake the login better.\n```\n\n```\nOur login is in src/auth/. Users sign in with email and\npassword against the users table. Add a \"remember me\"\ncheckbox that keeps them signed in for 30 days. Match\nthe session code already in that folder.\n```\n\nThe rules file you set up earlier is context the agent reads on its own. A good prompt adds the rest: the files, the constraint, the definition of done. Keeping the right context in front of the agent is a skill of its own, and the next chapter is about the window it all has to fit inside.\n\n{/* KEEP: bundling 3 asks makes the agent do each half-well; give it ONE task, let it finish, then the next. Smaller finished steps are easier to check and undo. Rule-of-thumb callout: an \"and\" joining two jobs = two prompts. */}\n\n## One task per prompt\n\nAsk for three things in one message and the agent spreads its attention across all three, doing each one half-well. Give it one task, let it finish, then give the next. A small finished change is easier to check and easier to undo than one sprawling one.\n\n> **Rule of thumb:** If your prompt joins two jobs with an \"and,\" it is two prompts.\n\n{/* KEEP: fastest way to kill ambiguity = show what \"done\" looks like. Paste one example: an input and expected output, or the shape you want. A pattern the agent can match beats a paragraph describing it. Small artifact showing an example. */}\n\n## Show it an example\n\nThe fastest way to remove ambiguity is to show the agent what \"done\" looks like. Paste one concrete example: an input and the output you expect, or a sample of the shape you want. Something the agent can pattern-match against beats a paragraph describing the same thing.\n\n```\nGiven { \"email\": \"a@b.com\" }, the endpoint should return\n{ \"id\": 42, \"email\": \"a@b.com\" }. Match that shape.\n```\n\n{/* KEEP: make the agent produce a PLAN first (files it will touch, approach, unknowns), you approve, THEN it codes. Reading a 5-line plan is seconds; reviewing 200 lines of wrong code is an hour. Catch the misunderstanding for free. */}\n\n## Ask for a plan before code\n\nBefore the agent writes anything, ask it to lay out a plan: which files it will touch, the approach, and what it is unsure about. Reading a five-line plan takes seconds; reviewing two hundred lines of wrong code takes an hour. If the plan is wrong, you caught the misunderstanding for free, then approve it and let it build.\n\n{/* KEEP: a prompt that worked is an ASSET, not a throwaway. Save it as a prompt template: instructions fixed at top, one blank slot at the bottom for today's specifics. Build a small library your project trusts. Bold-first: prompt template. Ends with the copy-paste template + Do this now. */}\n\n## Keep the prompts that work\n\nA prompt that produced good work is an asset, not a throwaway line. Save it as a **prompt template**: the instructions fixed at the top, one blank slot at the bottom where you drop today's specifics. Over a few weeks you build a small library of prompts your project already trusts, and you stop rewriting the same setup every session.\n\nThis template bakes all four habits into one block you paste before any feature:\n\n```\nAct as a senior engineer pairing with me. Before you\nwrite any code, do these in order:\n\n1. Restate the one task you think I am asking for. If it\n   is really two tasks, say so and stop.\n2. Read the project's rules file and the files this\n   touches, then list what you found that matters.\n3. Propose a short plan: the files you will change, the\n   approach, and anything you are unsure about.\n4. Wait for my go-ahead. Only then write the code.\n\nAlso show me one example of the result you expect, an\ninput and the output, so we agree on done first.\n\nThe feature I want built:\n```\n\n**Do this now:** save the prompt above, paste it before your next feature, and make the agent plan before it writes a single line."
          },
          {
            "slug": "context-engineering",
            "title": "Keeping the Agent Smart",
            "label": "Context",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/build/context-engineering",
            "content": "---\ntakeaway: Feed only what's relevant\nshare: \"The agent goes dull in long sessions, not because the model is weak but because of what you let pile up in front of it. This is the core skill of working with AI: feed only what matters, point to files instead of pasting them, and reset before the thread rots.\"\n---\n{/* KEEP: lead-in = the agent starts sharp and goes dull in long sessions; the cause is almost never the model, it is what you let pile up in front of it. This chapter = the core skill: managing what the agent sees. */}\n\nThe agent starts sharp and slowly goes dull in a long session. It forgets what you decided an hour ago, contradicts itself, edits the wrong file. The cause is almost never the model. It is what you have let pile up in front of it. This chapter teaches the one skill that most separates people who get real work from AI: managing what the agent sees.\n\n{/* KEEP: concept = the context window is a fixed budget; files, your messages, its replies all count against it; when it fills, detail falls out and answers get fuzzy. More is not better. Name the discipline (context engineering) + Fowler cite on first mention. Cross-ref the memory file as ONE slice, do not re-teach it. Bold-first: context window, context engineering. */}\n\n## The context window is finite\n\nThe **context window** is the fixed amount of text the agent can hold at once. Your files, your messages, and its own replies all count against one budget. When it fills, the oldest details fall out or the whole thing goes fuzzy.\n\nMore is not better. A window stuffed with irrelevant history produces worse answers, not richer ones. Managing that budget on purpose is called **context engineering**, and it is the skill this whole chapter is about (Martin Fowler has a good primer, [Context Engineering for Coding Agents](https://martinfowler.com/articles/exploring-gen-ai/context-engineering-coding-agents.html)). The always-loaded memory file you set up earlier is just one small, permanent slice of that budget; everything else is what you hand it per task.\n\n{/* KEEP: feed only what's relevant = the takeaway. Give it the two files this task touches, not the whole repo; the one error, not an hour of logs. Real artifact: before/after of a bloated vs focused hand-off. One rule-of-thumb callout. */}\n\n## Feed only what's relevant\n\nHand the agent the two files this task touches, not the whole codebase. Give it the one error message, not the last hour of logs. The narrower the input, the sharper the output.\n\n```\nBLOATED: here is my whole codebase [40 files],\nplus yesterday's full chat and the API docs.\nNow fix the login bug.\n\nFOCUSED: the login bug is in auth/session.ts.\nHere is that file and the error. Fix the\ntoken refresh.\n```\n\n> **Rule of thumb:** if you would not put a document in front of a new hire to solve this exact task, do not paste it to the agent either.\n\n{/* KEEP: point to the spec, don't repeat it = agents read files themselves, so name the file and let it open what it needs instead of pasting 500 lines. Keeps the window lean AND the agent reads the current version, not a stale paste. Cross-ref the spec chapter by topic. */}\n\n## Point to the spec, don't repeat it\n\nYour agent can open files on its own. So do not paste a 500-line spec into the chat. Tell it where the file lives and which part to read: \"the spec is in docs/spec.md, read the billing section.\"\n\nThis keeps the window lean, and it means the agent reads the current spec every time instead of a copy that went stale the moment you pasted it. The same holds for the requirements and data model you wrote in the planning part: point, do not repaste.\n\n{/* KEEP: reset when the thread drifts = after a long rabbit hole the window is full of dead ends the agent keeps tripping over; a fresh session with a tight prompt beats fighting a poisoned one. This is hygiene, not failure. */}\n\n## Reset when the thread drifts\n\nWhen a session has wandered through three failed approaches, its window is now full of dead ends the agent keeps tripping over. It will keep re-suggesting the thing that already did not work.\n\nStart a fresh session. A clean window with a tight prompt almost always beats fighting a long one that is carrying every mistake it made. This is hygiene, not failure.\n\n{/* KEEP: summarize before continuing = when a long task is going WELL but the window fills, don't just reset and lose the thread. Have the agent write a short summary (done/next/decisions), start fresh, paste it. Real artifact: the summary snippet. Name compaction + link Claude Code. */}\n\n## Summarize before continuing\n\nWhen a long task is going well but the window is filling, do not just reset and lose the thread. Ask the agent to write a short summary first, then start fresh and hand that summary back to it.\n\n```\n## Where we are\n- Done: login + session refresh, tests pass.\n- Next: password reset email.\n- Decisions: tokens in httpOnly cookies,\n  15-min expiry. Reset link valid 1 hour.\n```\n\nSome tools do this for you: [Claude Code](https://claude.com/claude-code) calls it **compaction**. This prompt produces a clean hand-off you can carry into a new session:\n\n```\nAct as a senior engineer closing out a long\nworking session. Summarize this thread for a\nfresh start so no context is lost. Include:\nwhat we set out to do, what is done and\nverified, what is left, and every decision\nwith its reason. Name the exact files and\nfunctions touched. Keep it under 20 lines,\nfacts only, no narration. I will paste this\ninto a new session.\n\nMy task and the files that matter:\n```\n\n**Do this now:** next time a session runs long, stop, ask the agent for that summary, and continue in a fresh window instead of pushing through the bloated one."
          },
          {
            "slug": "working-in-small-steps",
            "title": "Working in Small Steps",
            "label": "Increments",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/build/working-in-small-steps",
            "content": "---\ntakeaway: Ship in small slices\nshare: \"One big AI-written change breaks in ways you cannot untangle. Work in small slices instead: one change at a time, committed after each, so rollback is trivial and the culprit is obvious.\"\n---\n{/* KEEP: lead-in = you let the agent write a whole feature in one shot, it half-works, and the change is too big to read or undo. This chapter: build in small slices, one working change at a time. */}\n\nYou let your agent write a whole feature in one shot, and it half-works. Now the change is too big to read, and you cannot tell which part broke what. This chapter fixes that at the source: build in small slices, one working change at a time, so nothing ever grows too big to understand or undo.\n\n{/* KEEP: concept = define slice (one thin complete change, does one visible thing, leaves app running); the big-bang is the opposite and loses because a large broken change is ten things braided together. Bold-first: slice. Rule-of-thumb callout distinct from version-control's. */}\n\n## Ship in slices, never big-bang\n\nA **slice** is one thin, complete change: it does a single visible thing and leaves the app running. The opposite is the big-bang, one huge change that adds everything at once and only works if every part is right.\n\nThe big-bang loses every time. When one large change misbehaves, you are untangling ten things braided together. Cut the same work into slices and each piece is small enough to read and trust before the next.\n\n> **Rule of thumb:** if you cannot name the one thing a change does, it is more than one change.\n\n{/* KEEP: step = cut along features not layers; a good slice reaches from what the user sees to where data lives and does one real thing end to end. Artifact = one feature (login) as a sequence of small commits, each runnable. */}\n\n## One working change at a time\n\nCut along features, not layers. A good slice reaches from what the user sees down to where data is stored and does one real thing, so you can run it and watch it work. A bad slice builds half of everything and runs nothing.\n\nTake a login feature. Instead of \"build login,\" your agent works it as a sequence of small changes, each one runnable and each its own commit:\n\n```\nfeat: show the login form (no logic yet)\nfeat: accept email + password, log to console\nfeat: verify credentials against the database\nfeat: start a session on success\nfeat: show an error message on failure\n```\n\n{/* KEEP: step = define green (it works: app runs, slice does its job); commit at exactly that moment, never on red (code that does not run). References the safety net from version control, does not re-teach committing. Bold-first: green. */}\n\n## Commit after each green step\n\n**Green** means it works: the app runs and the new slice does what it should. Commit at exactly that moment, never before. You already set your agent to save a snapshot after every working step; this is the discipline that makes that safety net worth having, one green slice, one commit.\n\nA commit on red, code that does not run, poisons your history. Now a \"saved\" version is broken, and it is useless as a fallback. Only green earns a commit.\n\n{/* KEEP: step = with every commit one green slice, rollback is surgical (undo one commit, not an afternoon) and the culprit is one small diff, obvious on sight. Cross-ref the later bug-hunting chapter by topic, not number. */}\n\n## Small steps make rollback easy\n\nWhen every commit is one green slice, going back is surgical. A slice breaks something, you undo that one commit, and you are on solid ground again instead of unwinding an afternoon.\n\nIt is also how you find the culprit fast. With small steps, the change that caused a bug is one small diff, the handful of lines it touched, obvious on sight. With a big-bang commit, the cause is buried in hundreds of lines. The later chapter on hunting down which change caused a bug turns this into a method.\n\n{/* KEEP: prompt (senior-engineer voice, fixed content + one append slot \"The feature to slice up:\"). Agent breaks a feature into ordered vertical slices, each runnable, commit after each. Do this now = paste with next feature, build slice by slice. */}\n\n## Hand the slicing to your agent\n\nGive it a feature and let it plan the slices for you:\n\n```\nAct as a senior engineer breaking work into small,\nsafe slices. Take the feature below and return an\nordered list of vertical slices. Rules:\n- Each slice does ONE visible thing end to end and\n  leaves the app runnable, never a half-built layer.\n- Order them smallest and safest first, each one\n  building on the last.\n- After each slice, stop, confirm it works, and\n  commit it with a short clear message before you\n  start the next.\n- If a slice is too big to read in one sitting,\n  split it again.\n\nThe feature to slice up:\n```\n\n**Do this now:** paste the prompt above with your next feature, and build it slice by slice instead of all at once."
          },
          {
            "slug": "guardrails",
            "title": "Catching Bugs Before They Happen",
            "label": "Guardrails",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/build/guardrails",
            "content": "---\ntakeaway: Automate the quality checks\nshare: 'The agent writes code that looks right and runs, until a typo or a wrong type hides a bug you cannot see by eye. Automated checks catch whole classes of mistakes before they ever reach you.'\n---\n{/* KEEP: lead-in = the agent's code looks right and runs, but small mistakes (typo, wrong type, unused variable hiding a bug) slip past because you cannot eye every line. This chapter sets up the automated checks that catch whole classes of mistakes before they reach you. Distinct from conventions-and-naming (that was consistent STYLE); this is bug-catching. */}\n\nThe agent writes code that looks right and runs, right up until a typo, a value of the wrong type, or an unused variable quietly hides a real bug. It generates faster than you can read, and you cannot eye every line. This chapter sets up the automated checks that catch whole classes of mistakes for you, before they ever reach a user.\n\n{/* KEEP: you met the formatter + linter for consistent STYLE; the linter earns its keep again here catching likely BUGS (unused variable, unreachable code, a comparison always true). Machines are tireless and consistent where your eyes glaze over. Bold-first: linter (as bug-catcher). Reference conventions chapter by topic, don't re-teach. */}\n\n## Let the linter catch the small bugs\n\nYou already set up a formatter and a [**linter**](https://eslint.org) to keep the code in one consistent style. The linter does a second job that matters more here: it flags likely mistakes. An unused variable, an unreachable line, a comparison that is always true, the small errors that read as fine but signal a real bug.\n\nThese are exactly the things your eyes glaze over on line four hundred of a diff you did not write. A machine never glazes over.\n\n{/* KEEP: THE big one. a type checker catches a whole class of errors before the code runs: passing text where a number is expected, calling something that might not exist. Bold-first: type checker. Link TypeScript as the common one. It turns a crash-in-production into a red squiggle now. */}\n\n## Turn on type checking\n\nThe single highest-value check is a **type checker**. It knows what shape each value is meant to be, a number, a name, a user, and flags the moment you use one wrong: passing text where a number belongs, reading a field that might not exist.\n\nThat catches a whole class of bugs before the code runs at all. The common one is [TypeScript](https://www.typescriptlang.org); most stacks have an equivalent. It turns a crash a user would have hit into a red underline you fix in seconds.\n\n{/* KEEP: static analysis = deeper automated scans for risky patterns, security smells, dead code, beyond style + types. Bold-first: static analysis. Optional-but-worth-it; one tool, runs in CI or on save. Keep short, it is the third layer. */}\n\n## Add static analysis for the deeper risks\n\nPast style and types, **static analysis** reads your code for risky patterns without running it: a secret left in the source, an input used without checking, a whole file nobody calls anymore.\n\nYou do not need it on day one, but one tool wired in early keeps a class of security and dead-code problems from ever accumulating.\n\n{/* KEEP: the move that makes all of it work = the agent runs the checks EVERY time, before it says done, not you remembering. Put it in the rules file / a hook (ties to earlier rules + guardrails-as-code). Show the real artifact: the check commands + the standing rule. A check you run sometimes is a check you do not have. */}\n\n## Make the agent run them every time\n\nChecks you run when you remember are checks you do not really have. The move is to make the agent run them itself, every time, before it declares a task done:\n\n```\n# The agent runs these before saying \"done\":\nformat   ->  prettier --write .\nlint     ->  eslint .\ntypes    ->  tsc --noEmit\n```\n\nWrite that into the rules file the agent reads every session, as a hard line: no task is finished until format, lint, and type checks pass. Now the gate is automatic, and broken code cannot quietly reach you in the first place.\n\nThis prompt wires the checks and the standing rule for your stack:\n\n```\nAct as a senior engineer setting up my quality gate.\nFor my stack, set up a formatter, a linter, and a\ntype checker, and one static-analysis tool if it\nfits. Give me the exact command for each. Then add a\nrule to the file you read every session: no task is\ndone until all of them pass, and you run them\nyourself before saying so.\n\nMy stack:\n```\n\n**Do this now:** paste the prompt, wire the checks for your stack, and add the \"not done until they pass\" rule so your agent runs them on every change."
          },
          {
            "slug": "reviewing-the-agents-code",
            "title": "Checking the Agent's Work",
            "label": "Code Review",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/build/reviewing-the-agents-code",
            "content": "---\ntakeaway: Never ship unread code\nshare: \"Your agent writes most of the code now, faster than you can read it. This chapter makes review the gate every change passes: read the real diff, run four checks, and never accept a line you cannot explain.\"\n---\n{/* KEEP: lead-in = the Problem. Agent writes most of the code, faster than you can read; the summary is not the code; review is the first time a human truly looks. This chapter makes that look happen every time, before the change lands. */}\n\nYour agent writes most of the code now, and it writes it faster than you can read. The temptation is to skim its summary, see the app still runs, and move on. But that summary is a claim, not the code, and review is the first time a human truly looks at what shipped. This chapter makes that look happen every time, before the change lands.\n\n{/* KEEP: Concept = the agent's summary is a claim, not evidence; open the actual diff and read the changed lines yourself. Bold-first **diff** (glossed: lines added and removed) and **code review**. Read the whole thing; the lines you skip hide the bug you can't explain later. */}\n\n## Read what the agent wrote\n\nThe agent hands you a confident paragraph describing what it did. Open the **diff**, the exact lines added and removed, and read them against what you actually asked for. That is **code review**: you deciding whether this change earns a place in your codebase, from the real evidence and not the sales pitch.\n\nRead the whole change, not just the parts you follow at a glance. The lines you skip are exactly where the bug you cannot explain later is hiding.\n\n{/* KEEP: Step = review even solo. On a team a second engineer reviews before merge; alone that engineer is you, and skipping it doesn't make it safe, it means nobody looked. Fresh eyes catch what the author can't; read it like a stranger wrote it, or hand it to a second reviewer agent (nod to builder/reviewer split, separation of blind spots). Watch out callout on silent extra scope. */}\n\n## Review even when solo\n\nOn a team, a second engineer reads the change before anything merges. Alone with an agent, that second engineer is you, and skipping the step does not make the code safe, it just means nobody looked.\n\nFresh eyes catch what the author cannot see, so give yourself them: read the diff as if a stranger wrote it. Better, hand it to a second agent whose only job is to review, the same builder-and-reviewer split you use elsewhere, so a different mind grades the work.\n\n> **Watch out:** the change that also quietly renamed, refactored, or \"cleaned up\" something you never asked about is the one that breaks a working feature. Extra scope is not a bonus, it is unreviewed risk.\n\n{/* KEEP: Step = the real artifact, a fixed checklist run on every diff. Four checks: Intent, Scope, Security, Bugs. You're not re-deriving the code, you're running it past the same questions each time. Fenced checklist is the copyable thing. */}\n\n## Four checks on every diff\n\nYou are not re-deriving the code from scratch, you are running it past a fixed set of questions. The same four, every diff:\n\n```\nReview checklist, run on every diff:\n[ ] Intent: does it do exactly what I asked,\n    no more and no less?\n[ ] Scope: did it touch files or behavior I\n    never mentioned?\n[ ] Security: is every secret, input, and auth\n    path handled with care?\n[ ] Bugs: any obvious error, missed edge case,\n    or dead code left behind?\n```\n\n{/* KEEP: Step + Action = never ship what you don't understand. A line you can't explain is a liability with a green checkmark; make the agent teach you until you could defend it. Copy-paste prompt: senior reviewer voice, checks diff against intent + security + bugs, ONE append slot at bottom. Do this now nods to carrying a reviewed running app into hardening, no re-teaching. */}\n\n## Never ship what you don't understand\n\nA change you cannot explain is one you cannot maintain, debug, or trust. If a line does what you asked but you have no idea how, that is not done, it is a liability with a green checkmark.\n\nSo make the agent teach you: have it walk you through anything unclear until you could defend it yourself. Then run the review as one command:\n\n```\nAct as a senior engineer reviewing a colleague's\nchange before it merges. Read the actual diff\nbelow, not the summary of it. Check it against\nthree things: intent (does it do exactly what was\nasked, nothing extra), security (secrets, user\ninput, auth, injection), and bugs (broken logic,\nmissed edge cases, dead or duplicated code).\nList every issue by severity, blockers first.\nExplain anything I would likely not understand in\nplain words. If it is clean, say so plainly, do\nnot pad. Do not rewrite the code, only review it.\n\nThe task I asked for and the diff to review:\n```\n\n**Do this now:** take the last change your agent made, open its diff, and run it through the four checks and the prompt before you accept it. A reviewed, running app is what you carry into hardening it for real users."
          }
        ]
      },
      {
        "id": "debug",
        "title": "Diagnose and fix what the agent breaks",
        "chapters": [
          {
            "slug": "stack-traces",
            "title": "Reading What Broke",
            "label": "Stack Traces",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/debug/stack-traces",
            "content": "---\ntakeaway: Read the error trace\nshare: \"Your app was running, then it crashed and dumped a wall of red text. That wall is a stack trace, a precise map to where and why it broke, and its top and bottom lines point you straight at the fix.\"\n---\n\n{/* KEEP: lead-in = at the end of the last part the app RAN; a change broke it and it dumped a wall of red text; the reader panics because it looks like a language they do not speak. This chapter gets them to read that wall instead of fearing it. First Debug chapter: something built earlier now broke. */}\n\nAt the end of the last part your app ran. Then you changed one thing, reloaded, and instead of your app you got a wall of red text and a crash. The instinct is panic: it reads like the machine broke in a language you do not speak. That wall is the single most useful thing on your screen right now, and this chapter teaches you to read it.\n\n{/* KEEP: concept = a stack trace is the report a program prints the instant it crashes, newest event first. Not noise, a precise record. Each indented line is a FRAME (one running function + its file/line). Bold-first: stack trace, frame. */}\n\n## What a stack trace is\n\nA **stack trace** is the report a program prints the instant it crashes. It is not noise, and it is not the machine yelling at you. It is a precise record of what the program was doing the moment it failed, printed newest event first.\n\nEach indented line under the message is a **frame**: one function that was running, plus the file and line it was on. Together the frames are the trail of calls that led to the crash, a breadcrumb path back toward the start.\n\n{/* KEEP: read it in two moves. TOP line = the error itself (type + message, WHAT broke). Frames below: each called the one above it; top frame is where it blew up, walk down toward what set it off (the cause). What at the top, why a frame or two down. */}\n\n## Top for the error, bottom for the cause\n\nRead a trace in two moves. The very top line is the error itself: its type and a message saying what went wrong, like reading a value that was not there.\n\nBelow it, each frame called the one above it. The top frame is where the program actually blew up, and walking down the frames traces back toward what set the crash in motion. What broke sits at the top; why it broke is usually a frame or two down.\n\n{/* KEEP: THE technical artifact. Show a real short trace; point out the error line and which frame is the crash site vs the calling path. Then the move that matters: skip library frames, find the FIRST frame naming a file YOU wrote (app/ or src/). Rule-of-thumb callout. */}\n\n## Find the file and line\n\n```\nTypeError: Cannot read properties of undefined (reading 'total')\n    at calculateBalance (app/lib/ledger.ts:42:19)\n    at buildSummary (app/lib/summary.ts:15:22)\n    at Home (app/page.tsx:8:14)\n```\n\nThe top line is the error: something was undefined when the code expected a value. The first frame, `ledger.ts:42`, is where it crashed, so that is the file and line to open first. The frames beneath it show the path that got there, through `summary.ts` and your home page.\n\nMost real traces bury your code under library frames you will never touch. Scan past them for the first line naming a file you wrote, usually under `app/` or `src/`. That is where you look.\n\n> **Rule of thumb:** the top line tells you what broke; the first frame pointing at your own file tells you where.\n\n{/* KEEP: the fastest fix = copy the WHOLE trace, every line, into your agent. Do NOT summarize or paste only the top line; the frames are the context that finds the cause. Then the senior-voice prompt (expert fixed top, one append slot \"The error and stack trace:\"). */}\n\n## Paste it to your agent\n\nYou do not have to diagnose it alone, and you should not try to. The fastest fix is to copy the entire trace, every line, and hand it to your agent. Do not summarize it and do not paste only the top line: the frames are the context that lets the agent find the real cause fast.\n\nThis prompt turns the trace into a targeted fix instead of a guess:\n\n```\nAct as a senior engineer debugging a crash with me.\nRead the full stack trace below. Identify the top\nline as the error type and message, then walk the\nframes down to the first one that points at a file I\nwrote, not library code. Tell me the exact file and\nline to open, explain in one plain sentence what\nwent wrong there, and give me the smallest fix.\nDo not refactor anything unrelated. If the real\ncause is in a different frame than where it crashed,\nsay so and why.\n\nThe error and stack trace:\n```\n\n**Do this now:** copy the full stack trace from your terminal or browser console, paste it under the prompt, and let your agent point you at the exact file and line."
          },
          {
            "slug": "agent-loops",
            "title": "When Your Agent Goes in Circles",
            "label": "Loops",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/debug/agent-loops",
            "content": "---\ntakeaway: Break the retry loop\nshare: \"Your agent insists it fixed the bug, hands you the same error, and quietly undoes its own work. Learn to catch that retry loop in the first two rounds and break it with the one fact it is missing.\"\n---\n{/* KEEP: lead-in = the agent claims it fixed the bug, the same error returns, it reapplies and later reverts its own edit; it is going in circles and burning the session. This chapter = catch the loop early and break it. */}\n\nYour agent says it fixed the bug. You run it, the same error comes back. It says it sees the real problem now, changes the file, and hands you the same error again. Twenty minutes later it is undoing the edit it made ten minutes ago. This chapter teaches you to catch that circling early and break it before it eats your whole session.\n\n{/* KEEP: concept = define retry loop; the tell is repetition (same edit made/reverted/reapplied, same error unchanged, confident \"now it is fixed\" that is not). Real artifact: a short transcript sketch of the loop. Bold-first: retry loop. */}\n\n## Recognize a retry loop\n\nA **retry loop** is when the agent keeps shipping variations of the same broken fix, each time sure it has it. The tell is repetition: the same edit made, reverted, and reapplied; the same error message coming back unchanged; a confident \"now it is fixed\" that is not.\n\nCatch it by the second round, not the fifth. It usually reads like this:\n\n```\nYou: still failing, same TypeError.\nAgent: I see the issue now. Fixed it.\nYou: same TypeError.\nAgent: You're right, the real fix is here. Done.\nYou: same TypeError.\nAgent: I see the issue now...   <- you are in a loop\n```\n\n{/* KEEP: why = not laziness; it lost the thread. Window is full of its own failed attempts so it pattern-matches on those, or it has a wrong mental model that one missing fact would break. Cross-ref the context chapter (poisoned context) by topic, do NOT re-teach reset here. */}\n\n## Why the agent goes in circles\n\nIt is not being lazy. It lost the thread. The window is now full of its own failed attempts, so it keeps pattern-matching on those instead of the real cause, the same dead ends you saw poison a drifting session in the chapter on managing what the agent sees.\n\nThe other cause is a wrong mental model. It is reasoning from one false assumption that nothing in the chat corrects, so every fix is internally sensible and still wrong.\n\n{/* KEEP: break = stop it before the next attempt; then do one of two things: hand it the missing fact or force a fresh diagnosis before any edit. The missing fact is usually something only you can see (real console error, actual variable value, the file that runs is not the one it edits). */}\n\n## Break the loop\n\nStop it before the fourth attempt. Then do one of two things: hand it the fact it is missing, or force a fresh diagnosis before it touches a single line.\n\nThe missing fact is usually something only you can see: the real error in the browser console, the actual value of a variable, or that the file it keeps editing is not the file that runs. One such fact ends more loops than ten more retries.\n\n{/* KEEP: reset = after three churned attempts the window carries all of them, so one more message rarely helps; start a fresh session (point to the context chapter, do not re-teach) and open with the fact + a clean ask. Then the copy-paste prompt that asks for a ranked fresh diagnosis before any edit. */}\n\n## Reset the context and retry\n\nIf it has already churned through three attempts, the window is carrying every one of them, and one more message rarely pulls it out. Start a fresh session, the way the context chapter describes, and open with the missing fact plus a clean ask.\n\nThis prompt forces a ranked diagnosis before any edit, which is what actually breaks the loop:\n\n```\nAct as a senior engineer taking over a stuck\ndebugging session. The previous attempts all\nfailed. Ignore them. Do NOT edit anything yet.\n\nFirst, state the top three things that could\ncause this, ranked by likelihood. For each,\nname the ONE observation that would confirm\nor rule it out, and tell me how to get it.\n\nDo not propose a fix until one cause is\nconfirmed. If you cannot explain why the last\nfix failed, say so instead of guessing.\n\nWhat it keeps failing to do:\n```\n\n**Do this now:** next time the same error returns twice, stop the agent, open a fresh session, and paste this prompt with the one fact only you can see."
          },
          {
            "slug": "bisecting",
            "title": "Finding the Change That Broke It",
            "label": "Bisecting",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/debug/bisecting",
            "content": "---\ntakeaway: Find the breaking change\nshare: \"A bug appears and dozens of changes have landed since it last worked. Instead of reading every one, halve the history: git bisect walks you to the exact change that broke it in a handful of tests.\"\n---\n{/* KEEP: lead-in = something worked last week, is broken now, and many changes landed since. Reading each one is hopeless. This chapter: get to the exact change fast by halving the search, not walking it. */}\n\nSomething that worked last week is broken now, and dozens of changes have landed since. Reading every one to find the guilty change would eat your whole day, and staring at the current code tells you nothing about when it went wrong. This chapter gets you to the exact change that broke it, fast, by halving the search instead of walking it.\n\n{/* KEEP: concept = binary search over history. Two markers: a point where it worked (good) and a point where it is broken (bad, usually now). Test the midpoint, throw away half the suspects. Bold-first: bisect. */}\n\n## Find when it last worked\n\nYou need two markers: a past point where the feature worked, and a point where it is broken, usually right now. Every change between them is a suspect.\n\nInstead of checking each suspect in order, check the one in the middle. Is the bug there or not? That single test tells you which half to throw away. To **bisect** is to cut the range in two, test the midpoint, and keep only the half that still contains the break. A hundred suspects fall to seven tests.\n\n{/* KEEP: step = git bisect automates the bookkeeping. You give one good commit and one bad, it checks out the midpoint for you. Link official git bisect docs. WHY it lands on a readable diff: small green commits (ref small-steps chapter by topic), do not re-teach committing. */}\n\n## Narrow it with git\n\nGit does this bookkeeping for you with [git bisect](https://git-scm.com/docs/git-bisect). You hand it one commit where the feature worked and one where it is broken, and it checks out the midpoint for you to test, over and over, until one change is left.\n\nThis is where committing in small green slices pays off. Because each saved step did one thing, the change bisect lands on is a short, readable diff, not an afternoon of braided edits you cannot untangle.\n\n{/* KEEP: step = the mechanics, mark each checkout good/bad, git narrows. Show the REAL artifact: a full git bisect session converging from many suspects to one culprit. */}\n\n## Halve the history each time\n\nYou start it, then mark each checkout `good` or `bad`. Git narrows the range every time and stops on the culprit.\n\n```\n$ git bisect start\n$ git bisect bad HEAD\n$ git bisect good a1b2c3d\nBisecting: 6 revisions left to test after this\n[e4f5a6b] feat: cache the user profile\n\n# test the app... bug is gone here\n$ git bisect good\nBisecting: 3 revisions left to test after this\n[b7c8d9e] refactor: reuse the fetch helper\n\n# test again... bug is back\n$ git bisect bad\nBisecting: 1 revision left to test after this\n[c9d0e1f] fix: trim the search input\n\n# test... still broken\n$ git bisect bad\nc9d0e1f is the first bad commit\n```\n\n{/* KEEP: step = git names the first bad commit, the exact change. Open its diff, it is a small slice you can read. Then git bisect reset to return. git bisect run <script> automates the testing. Prompt + Do this now. */}\n\n## Land on the bad change\n\nGit prints the first bad commit: the exact change that introduced the break. Open its diff and the cause is a handful of lines you can actually read, not a needle in the whole codebase. When you are done, run `git bisect reset` to return to where you started.\n\nIf testing each step by hand is tedious, hand the whole hunt to your agent:\n\n```\nAct as a senior engineer running git bisect to find\nthe change that broke a feature. Walk me through it:\n- Confirm the bug is present now (bad), and pick a\n  past commit where it worked (good).\n- Start the bisect, mark that commit good and HEAD\n  bad, and tell me exactly what to test each step.\n- After I report good or bad, give the next command\n  until we land on the first bad commit.\n- If the check can be scripted, write a one-command\n  test and use git bisect run to automate it.\n- End by showing the culprit's diff, then run\n  git bisect reset.\n\nWhat broke and when it last worked:\n```\n\n**Do this now:** on your next mystery bug, start a bisect with a known-good commit and today's HEAD, and let it walk you to the first bad commit."
          },
          {
            "slug": "verifying-fixes",
            "title": "Is It Actually Fixed?",
            "label": "Verification",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/debug/verifying-fixes",
            "content": "---\ntakeaway: Confirm it's really fixed\nshare: \"Your agent says the bug is fixed and the app still runs, but that is a claim, not proof. This chapter gets you to prove it: reproduce the bug, confirm the change kills the root cause instead of hiding the symptom, re-run the exact case that failed, and check nothing else broke.\"\n---\n{/* KEEP: lead-in = the Problem. \"Fixed\" is a claim, not proof; the app still running is not the same as the bug being dead. You never watched it die. This chapter gets the reader to prove a fix is real before trusting it. */}\n\nYour agent reads the error, changes a few lines, and reports the bug fixed. The app still runs, so it is tempting to believe it and move on. But \"fixed\" is a claim, and a claim is not proof: you never watched the bug actually die. This chapter gets you to prove a fix is real before you trust it.\n\n{/* KEEP: Concept + first step = you cannot confirm a fix you were never able to make fail. Reproduce the bug first, run the exact steps/input, watch it break. Bold-first **repro**. If the agent jumped to a fix with no repro, it was guessing. Rule-of-thumb callout: watched it fail, then watched it pass. */}\n\n## Reproduce before you trust the fix\n\nYou cannot confirm a fix you were never able to make fail. Before you trust anything, reproduce the bug yourself: run the exact steps, or feed the exact input, that triggered it, and watch it break. That gives you a **repro**, the precise steps that force the bug to happen, and it is the one case that proves the fix later.\n\nIf the agent jumped straight to a fix without a repro, it was guessing at the cause. Make it show you the bug first, then the fix.\n\n> **Rule of thumb:** you have not fixed a bug until you have watched it fail, then watched the same steps succeed.\n\n{/* KEEP: Concept = a bug is a symptom of a deeper cause. Fast wrong move = silence the symptom (swallow the error, return a safe default, special-case the one input). Bold-first **symptom** and **root cause**. The real artifact: before/after code contrasting a symptom-hiding patch vs a root-cause fix. */}\n\n## A real fix, not a hidden symptom\n\nA bug is a **symptom** of a deeper cause. The fast, wrong move is to silence it: swallow the error, return a safe-looking default, or special-case the one input that broke. The app stops complaining, and the real defect stays in, waiting for the next input.\n\nA **root cause** fix changes why the bug happened, so the same kind of input can never trigger it again. The difference shows in the code.\n\n```\n// Symptom hidden: crash gone, wrong total stays.\nfunction lineTotal(item) {\n  try {\n    return item.price * item.qty\n  } catch {\n    return 0\n  }\n}\n```\n\n```\n// Root cause: a deleted product left a dead id in\n// the cart. Drop dead ids when the cart loads, so\n// every item is real before we price it.\ncart = cart.filter(item => productExists(item.id))\n```\n\n{/* KEEP: Step = re-run the EXACT repro, unchanged, not something similar. Same steps that failed before. If it passes for the reason the fix claims, the bug is dead. If you changed the input to make it pass, you tested a different bug. */}\n\n## Test the exact case that failed\n\nNow re-run the exact repro from the first step, unchanged. Not something similar, the same steps that failed before. If it now behaves correctly, and for the reason the fix claims, the bug is dead.\n\nIf you quietly changed the input to make it pass, you tested a different bug and left the original one alive.\n\n{/* KEEP: Step = regression. A fix edits code other features rely on, so it can break something that worked. Bold-first **regression**. Re-run the neighbors: same feature, inputs on either side, the screen it lives on. Forward pointer: automated tests come later in Harden; here, checking neighbors by hand is enough. Do NOT deep-dive test suites. */}\n\n## Check nothing else broke\n\nA fix edits code that other features rely on, so it can break something that was working a minute ago. That is a **regression**: new damage caused by the change itself, not by the original bug. The fix is not done until you have looked for it.\n\nRe-run the things nearest the change: the same feature, the inputs just on either side of the one that broke, the screen it lives on. Doing this by hand every time does not scale, which is why later, when you harden the app, your agent builds automated tests that re-check everything on each change. For one fix, checking the neighbors yourself is enough.\n\nRun all four as one command, and make the agent do the proving, not the promising:\n\n```\nAct as a senior engineer verifying a fix before it\nships, not the one who wrote it. Be skeptical.\n\n1. Reproduce the original bug first: state the\n   exact steps or input that triggered it, and\n   confirm it truly failed before the change. If\n   you cannot reproduce it, say so and stop.\n2. Judge root cause vs symptom: does this change\n   fix why the bug happened, or does it hide the\n   failure (a swallowed error, a guard that\n   returns a wrong value, a special case for one\n   input)? If it hides it, name the real cause.\n3. Re-run the exact case that failed and confirm\n   it now passes for the right reason.\n4. Check for regressions: what nearby behavior or\n   input could this change have broken? Name the\n   cases to re-test, and flag any you cannot.\n\nReport each step's result plainly. If the fix is\nnot proven, say what is missing. Do not rewrite\nthe code, only verify it.\n\nThe bug and the fix to verify:\n```\n\n**Do this now:** take the last bug your agent fixed, reproduce it from before the change if you still can, then paste the prompt with that bug and fix so the agent proves it root-cause dead, not just quiet."
          },
          {
            "slug": "rollback",
            "title": "Reverting Safely",
            "label": "Rollback",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/debug/rollback",
            "content": "---\ntakeaway: Revert without losing work\nshare: A change made things worse and you cannot fix it fast. Learn to undo the one bad change without losing the good work you did after it, and to judge when to roll back instead of patching forward.\n---\n{/* KEEP: lead-in = a change made things worse, you cannot fix it fast, and you did real work after it you do not want to lose. This chapter: undo the one bad change, keep the rest, land on a version that runs. */}\n\nYou changed something and the app got worse, not better. You have poked at it for twenty minutes and it is still broken, and you have done real work since that change that you do not want to throw away. This chapter gets you out clean: undo the one bad change, keep everything else, and land back on a version that runs.\n\n{/* KEEP: concept = undoing is not all-or-nothing; history is a line of saved snapshots and you can lift out exactly one without touching the rest. The trap is deleting an hour to kill a five-minute mistake; you never pay that. */}\n\n## Undo without losing unrelated work\n\nUndoing a bad change does not mean throwing away the good work you did after it. Your history is a line of saved snapshots, and you can lift out exactly one of them while the rest stay put.\n\nThe trap is treating undo as all-or-nothing: deleting the last hour to kill one five-minute mistake. You never have to pay that price. Point your agent at the single change that broke things and leave everything else standing.\n\n{/* KEEP: step = define revert (a new commit that cancels one specific past commit, nothing else); adds to history, does not erase, so later work stays. Artifact = git revert <hash>; contrast the safe revert with the dangerous git reset --hard that wipes everything after. Bold-first: revert. Link git-revert + git-reset. */}\n\n## Revert the one change\n\nA [**revert**](https://git-scm.com/docs/git-revert) is a new commit that cancels out one specific past commit and nothing else. The work you did after the bad change stays exactly where it is, because you are adding to history, not erasing it.\n\nYour agent runs it against the commit that caused the trouble:\n\n```\ngit revert a1b2c3d\n```\n\nContrast that with the bulldozer. A hard [reset](https://git-scm.com/docs/git-reset) back to the last good commit throws away every change since, including the unrelated work you want to keep:\n\n```\ngit reset --hard 9f8e7d6   # deletes ALL work after 9f8e7d6\n```\n\nRevert is surgical and safe to run. A hard reset destroys history, so it is the move you almost never want here.\n\n{/* KEEP: step = sometimes you do not need surgery, just stop the bleeding and get onto the last version that ran. Branch path: throw away the broken experiment branch, main is untouched. On main: revert the last commit. Goal = a version that runs right now so you can think clearly. */}\n\n## Get back to a working state\n\nSometimes you do not need surgery, you need to stop the bleeding. If the last few changes are tangled and you cannot say which one broke things, get back onto the last version you know ran, then move forward from there.\n\nIf the work was on its own branch, the cleanest path is to throw that branch away and return to main, which never felt the damage. If the mess is already on main, revert the last commit to land back on solid ground without erasing anything.\n\nEither way the goal is the same: a version that runs right now, so you can think clearly instead of debugging in a panic.\n\n{/* KEEP: step = the judgment call. Fix forward = leave the change in, patch on top. Roll back = undo now, retry later calm. Rule is about time and understanding, not pride: one-line fix you know -> fix forward; stuck or do not understand it -> roll back to green first. Bold-first: fix forward. Decision table. */}\n\n## Roll back vs fix forward\n\nEvery broken change forces one call: roll back or fix forward. **Fix forward** means leaving the change in and patching the problem on top. Roll back means undoing it now and retrying later with a clear head.\n\nThe rule is about time and understanding, not pride.\n\n| The change... | Do this |\n| --- | --- |\n| broke in a way you understand, one-line fix | Fix forward |\n| has you stuck, or you cannot explain why | Roll back |\n| left you debugging in a panic | Roll back, then diagnose |\n\n{/* KEEP: prompt (senior-engineer voice, fixed content + one append slot \"The bad change I need to undo:\"). Agent finds the exact bad commit, reverts ONLY it, never hard-resets away later work, gets back to the last running commit if tangled, confirms the app runs, advises fix forward vs retry. Do this now = paste it. */}\n\n## Hand the undo to your agent\n\nWhen a change goes bad, hand the cleanup to your agent with clear rules:\n\n```\nAct as a senior engineer doing damage control.\nA recent change made things worse and I need it\nundone safely. Rules:\n- Find the exact commit that caused the problem.\n- Undo ONLY that commit with a revert, a new\n  commit that cancels it out. Never hard-reset\n  or delete work I did after the bad change.\n- If several recent changes are tangled, get me\n  back to the last commit that ran, and tell me\n  which one that is.\n- Confirm the app runs again before we move on,\n  then advise: fix forward now, or roll back and\n  retry the change later.\n\nThe bad change I need to undo:\n```\n\n**Do this now:** the next time a change makes things worse, paste the prompt above with that change, and let your agent revert just that one commit instead of throwing away your afternoon."
          },
          {
            "slug": "getting-unstuck",
            "title": "Getting Unstuck",
            "label": "Escalation",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/debug/getting-unstuck",
            "content": "---\ntakeaway: Know when to take over\nshare: \"You can loop with an agent forever on a bug it will never solve. This chapter gives you the judgment to know when to stop it, step in yourself, ask a human, or accept the problem is beyond the tool for now.\"\n---\n{/* KEEP: lead-in = the Problem. You have gone in circles on the same bug; the agent keeps trying, each attempt sounds confident, none work. The hard skill is knowing when to STOP letting it try. This chapter gives that judgment: stop, step in, ask a human, or accept it is beyond the tool for now. */}\n\nYou have been going in circles with your agent on the same bug for an hour. It keeps trying, each attempt sounds just as confident as the last, and none of them work. The hard skill here is not another fix, it is knowing when to stop letting the agent try. This chapter gives you that judgment: when to take over, when to call a human, and when to accept the problem is beyond the tool for now.\n\n{/* KEEP: Concept = the point of diminishing returns. An agent has no meta-sense that it is failing; that judgment is yours. Recognize the three signals: it has looped, its context is poisoned, it is guessing. Real artifact = the \"stop and take over\" checklist. */}\n\n## When to stop the agent\n\nAn agent has no sense that it is failing. It will cheerfully attempt the same broken idea a tenth time, because nothing in it tracks that the last nine did not work. That meta-judgment is yours, and noticing the point of diminishing returns is the whole job here.\n\nWatch for three signals: it has looped over the same two or three fixes, its context is now full of dead ends and it contradicts itself, or it has stopped reasoning and started guessing. When you see them, stop. Another attempt only buries the real signal deeper.\n\n```\nStop and take over when:\n[ ] The same error survives three or more attempts\n[ ] Each fix trades one error for a new one\n[ ] It changes lines at random, not by reason\n[ ] It contradicts what it said a few messages ago\n[ ] It edits files you never asked it to touch\n[ ] You no longer understand what it is doing\n```\n\n{/* KEEP: Step = you take over, but you do not have to fix it all. Read the actual code around the failure, form a hypothesis (bold-first, glossed = a specific testable guess at the cause), then hand the agent ONE smaller precise task. Reference retry-loop chapter for the fresh context. Rule-of-thumb callout: narrow, do not solve. */}\n\n## Step in yourself\n\nTaking over does not mean writing the fix by hand. It means reading the actual code around the failure yourself, so you stop guessing along with the agent. A fresh **hypothesis**, a specific and testable guess at the cause, is usually the exact thing a stuck agent is missing.\n\nThen hand it back a much smaller task. Not \"fix the login,\" but \"the token is null here, check whether it is set before this line runs.\" As the retry-loop chapter covered, starting the agent clean on that narrow task often breaks a loop nothing else could.\n\n> **Rule of thumb:** when you step in, your job is to narrow the problem, not to solve it. A smaller, precise task is usually all the agent needed.\n\n{/* KEEP: Step = some problems need a person, not the tool. When you and the agent are both out of ideas, ask a real human at the library's own issue tracker, Stack Overflow, or a community for your stack. Bring the specific error and what you tried. Someone has hit it before. */}\n\n## When to ask a human\n\nSometimes both of you are out of ideas, and the answer only lives in a person's head. Bring the specific error and what you already tried to where the experts are: the library's own issue tracker on [GitHub](https://github.com), [Stack Overflow](https://stackoverflow.com), or a community for your stack.\n\nSomeone has almost certainly hit your exact error before. A person who knows that tool deeply can see in one glance the thing you and the agent kept circling.\n\n{/* KEEP: Step + Action bridge = a few problems are genuinely beyond the tool for now, and naming that is judgment, not defeat. Where the cost of getting it wrong is high (money, user data, security, data loss), an hour of a real professional is cheap insurance. Funnels to calling a pro. Roll back first (rollback chapter) so you are not stuck AND broken. Then the diagnosis prompt + Do this now. Closing nods that unsticking a build is what lets you carry it into hardening, no re-teaching. */}\n\n## Know when it's beyond the tool\n\nA few problems are genuinely beyond what the tool does well right now, and saying so is judgment, not defeat. When the cost of getting it wrong is high, real money, user data, security, or work you cannot afford to lose, an hour of an actual professional is cheap insurance against a very expensive mistake.\n\nBefore you burn a day on any hard bug, roll back to the last working state, as the rollback chapter showed, so you are never both stuck and broken. Then, when you do take over, make the agent hand you a clean diagnosis instead of one more guess:\n\n```\nStop trying to fix this. Step back and act as a\nsenior engineer doing a fresh diagnosis. Treat\nevery fix we already tried as noise; it failed.\nBased only on the evidence below, state in plain\nwords the single most likely root cause, and why.\nIf you are unsure, say what you do not know and\nwhat one piece of information would settle it.\nThen propose the smallest experiment to confirm\nor kill that theory: one change, one thing to\nobserve, nothing more. Do not write the full fix.\n\nWhere I'm stuck:\n```\n\n**Do this now:** take the bug you are most stuck on, run the stop-and-take-over checklist against it, and if it trips, paste the diagnosis prompt to get one clean theory instead of another guess. Knowing how to unstick a build is exactly what lets you carry it forward and harden it for real users."
          }
        ]
      },
      {
        "id": "test",
        "title": "Prove it works, and keep it working",
        "chapters": [
          {
            "slug": "testing-and-qa",
            "title": "Why Test When AI Writes the Code",
            "label": "Why Test",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/test/testing-and-qa",
            "content": "---\ntakeaway: Test before users do\nshare: 'The agent swears the feature works, and it does, until a user does the one thing you never tried. Automated tests are the net that catches that before a real person does.'\n---\n{/* KEEP: lead-in = the agent says it works and it does on the happy path, but a user hits the case you never tried. Tests are the net that catches it first. When AI writes most of the code, tests are how you (and the agent) know it still works. Distinct from verifying-fixes (one fix); this is a standing safety net. */}\n\nThe agent tells you the feature works, and it does, right up until a real user does the one thing you never thought to try. You cannot click through every path by hand on every change, and when the agent rewrites something next week, you will not remember what to re-check. This chapter gives you the net that catches a break before a user does.\n\n{/* KEEP: a test = code that runs your app and checks it did the right thing, automatically. It is worth most exactly when AI writes the code, because it is how you AND the agent know a change did not break what worked. Machines re-check tirelessly; you do not. Bold-first: test. */}\n\n## Tests are your safety net\n\nA **test** is a small piece of code that runs part of your app and checks it did the right thing, without you clicking anything. It matters most precisely because the agent writes the code: a test is how both of you know a new change did not quietly break what already worked.\n\nYou cannot re-check everything by hand, and you will not want to. A machine re-checks every path on every change and never gets bored.\n\n{/* KEEP: don't test everything, test what would hurt most if it broke: the critical paths (log in, pay, save the thing). Cover those first; the login-and-checkout flow earns a test long before a settings toggle. Bold-first: critical path. */}\n\n## Cover the critical paths first\n\nDo not try to test everything. Test what would hurt most if it broke. The **critical paths** are the few flows your app exists for: a user logs in, saves their work, pays.\n\nCover those first and you have protected the parts that lose customers when they fail. A checkout flow earns a test long before a settings toggle does.\n\n{/* KEEP: the agent writes the tests; you say what to check, it writes them, and it can run them itself before declaring a task done (ties to guardrails/rules by topic). Show a real tiny test artifact. Name a real tool + link official. Bold-first: end-to-end. */}\n\n## Let the agent write and run them\n\nYou do not hand-write these. You tell the agent the behavior to protect and it writes the test, then runs it on every change. A quick one for a single function, an **end-to-end** test (with a tool like [Playwright](https://playwright.dev)) to drive the whole flow like a user:\n\n```\ntest(\"a user can log in\", async ({ page }) => {\n  await page.goto(\"/login\");\n  await page.fill(\"#email\", \"a@b.com\");\n  await page.fill(\"#password\", \"secret\");\n  await page.click(\"text=Log in\");\n  await expect(page).toHaveURL(\"/dashboard\");\n});\n```\n\nA unit runner like [Vitest](https://vitest.dev) covers the smaller pieces. Add the test run to the checks the agent must pass before it says done.\n\n{/* KEEP: automated tests catch REGRESSIONS: a green suite that goes red tells you the exact change that broke something, on every edit, for free. That is what lets you and the agent move fast without fear. Bold-first: regression. Then QA the whole flow like a real user before shipping (the human pass tests can't fully replace). */}\n\n## Green before you ship\n\nOnce the tests exist, they catch a **regression** the moment it appears: the suite was green, your last change turned it red, and now you know exactly what broke. That is what lets you keep changing a live app without holding your breath.\n\nTests are not the whole job. Before you ship, walk the real flow yourself once, on a phone, as a new user would, because some things (a confusing label, a broken layout) only a human notices.\n\nThis prompt sets up the safety net for your app:\n\n```\nAct as a senior engineer adding tests to my app.\nPick the few critical paths that would hurt most if\nthey broke, and write a test for each: end-to-end for\nwhole flows, unit tests for tricky logic. Then add the\ntest run to the checks you do before calling any task\ndone, and tell me which flows you covered and why.\n\nThe critical flows to test:\n```\n\n**Do this now:** paste the prompt, let your agent cover your critical paths, and make a green test run part of every change from here on."
          },
          {
            "slug": "unit-and-integration-tests",
            "title": "Testing the Pieces and Their Seams",
            "label": "Unit & Integration",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/test/unit-and-integration-tests",
            "content": "---\ntakeaway: Test the pieces work\nshare: 'A test can check one tiny piece on its own or two pieces working together at their seam. Knowing which is which tells you where bugs hide and what to hand the agent to protect.'\n---\n{/* KEEP: lead-in = you already know tests are the safety net and critical paths first (ref why-test by topic); the NEW thing is there is more than one KIND of test. One checks a tiny piece alone, another checks two pieces where they meet. Which kind decides where and how fast you catch the bug, and what you tell the agent to protect. */}\n\nYou know tests are your safety net, and that the critical paths come first. But there is more than one kind of test. One checks a single tiny piece on its own; another checks that two pieces still work where they meet. Which kind you reach for decides where you catch the bug and how fast, and this chapter gets you that difference so you can tell the agent exactly what to protect.\n\n{/* KEEP: a unit test = checks ONE small piece (a single function) on its own, input -> checked output, nothing else running (no db, no app). Isolation = fails fast, names the exact function. Show the tiny fenced unit test artifact. Name + link the official runner (Vitest, plain link) on first mention. Bold-first: unit test. */}\n\n## A unit test checks one piece\n\nA **unit test** checks one small piece of your code, usually a single function, on its own. You hand it an input, it runs that one piece, and it checks the output matches what you expected. Nothing else runs, no database, no whole app, just the piece.\n\nThat isolation is the point. When it fails, it fails fast and names the exact function, not a vague \"something broke somewhere.\"\n\n```\ntest(\"applyDiscount takes 20% off\", () => {\n  const final = applyDiscount(100, 0.2);\n  expect(final).toBe(80);\n});\n```\n\nA runner like [Vitest](https://vitest.dev) runs hundreds of these in a second.\n\n{/* KEEP: an integration test = checks two pieces work together at the SEAM (a function + the real database, two modules). Bugs hide at seams, not inside one clean function. Show the real save-then-load artifact with the actual db underneath; a unit test on either piece alone misses it. Bold-first: integration test. */}\n\n## An integration test checks the seams\n\nAn **integration test** checks that two pieces still work where they meet. The bug is rarely inside one clean function; it hides at the seam, where your code hands off to the database, or where two modules trusted different assumptions.\n\nSo you exercise the real seam. Save a record, load it back, and check you got the same thing out, with the actual database running underneath:\n\n```\ntest(\"a saved user loads back the same\", async () => {\n  await saveUser({ email: \"a@b.com\" });\n  const user = await loadUser(\"a@b.com\");\n  expect(user.email).toBe(\"a@b.com\");\n});\n```\n\nIf saving and loading ever stop agreeing, this turns red. A unit test on either one alone would sail straight past it.\n\n{/* KEEP: don't test every line or trivial getters; test the tricky/important logic that hurts if wrong (pricing math, discount rule, who-gets-in check). Rule-of-thumb callout: costs money / loses data / lets the wrong person in = earns a test; ordinary = skip. A few tests on real logic beat many on code that cannot surprise you. */}\n\n## Test the logic that matters\n\nYou do not test every line, and you do not test a function that just hands back a value. You test the logic that would hurt if it were wrong: the pricing math, the discount rule, the check that decides who gets in.\n\n> **Rule of thumb:** if a mistake there would cost money, lose data, or let the wrong person in, it earns a test. If it is ordinary, skip it.\n\n{/* KEEP: you write none of it; you name the behavior to protect, the agent writes + runs the tests. Judgment is yours (which logic, which seam); the agent fills the cases you'd forget (zero, empty, the error path). End with the senior-voice prompt, ONE bottom slot \"The logic I want protected:\", then Do this now. */}\n\n## Let the agent write them\n\nYou hand-write none of this. You name the behavior to protect; the agent writes the tests and runs them. The judgment is yours: which logic is worth the net, which seam is fragile. Give it that and it fills in the cases you would forget, the zero, the empty input, the one that should throw an error.\n\nThis prompt hands your agent the logic to protect:\n\n```\nAct as a senior engineer writing tests for me.\nFor the logic I name below, write focused unit\ntests for the tricky cases: the boundaries, the\nzero and the empty input, the case that errors.\nWhere two pieces meet (a function and the\ndatabase, two modules), add one integration test\nthat exercises the real seam, not a fake. Use my\nstack's standard runner. Report each case you\ncovered and the ones you deliberately skipped.\n\nThe logic I want protected:\n```\n\n**Do this now:** paste the prompt, name the one piece of logic that would hurt most if it broke, and let your agent write the tests that pin it down."
          },
          {
            "slug": "end-to-end-tests",
            "title": "Testing the Whole Flow Like a User",
            "label": "End-to-End",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/test/end-to-end-tests",
            "content": "---\ntakeaway: Test the whole flow\nshare: 'Every piece can pass its own test while the journey a user actually takes still falls apart between them. An end-to-end test walks that whole journey like a real person, so the few flows your business runs on never break without you knowing.'\n---\n{/* KEEP: lead-in = each piece passes its own test, but the real journey crosses all of them, and the break lives in the seam where two pieces meet. This chapter gets a handful of tests that walk each whole journey front to back like a real user. Builds on testing-and-qa (safety net, critical paths) and the unit-tests chapter (the small pieces) by topic. */}\n\nYour login works and your payment code works. Each piece passes its own test. Yet a real user still gets stuck halfway through checkout, because the break lives in the seam where two pieces meet for the first time. This chapter gets you a handful of tests that walk each whole journey, front to back, the way a real user would.\n\n{/* KEEP: an end-to-end test = code that opens a real browser and drives the whole app the way a user does (clicks real screens, types real fields), exercising every layer at once. Distinct from a unit test (one function alone) from the prior chapter. Name + link Playwright official on first mention. Bold-first: end-to-end test. */}\n\n## Drive the app like a user\n\nAn **end-to-end test** opens a real browser and drives your whole app the way a person does, clicking through real screens and typing into real fields. Where a unit test checks one function alone, an end-to-end test exercises every layer at once: the button, the code behind it, the database, and the screen that comes back.\n\nA tool like [Playwright](https://playwright.dev) runs that browser for you, invisibly and in seconds. Your agent writes the script; you just name the journey it should walk.\n\n{/* KEEP: a critical journey = one of the critical paths from the safety-net chapter walked end to end (sign up, pay, the one core action). Test THESE; a settings toggle does not earn one. Bold-first: critical journey. Show the real artifact here, a fenced e2e test of ONE journey, richer than the login one in the anchor (signup + core action across pages). */}\n\n## Cover the critical journeys\n\nYou already know your critical paths from the safety-net chapter. A **critical journey** is one of those paths walked end to end:\n\n- sign up, then land on the dashboard\n- add an item, then pay for it\n- create the one thing your app exists to make\n\nPick three or four. Here is one test, for a new user who signs up and creates their first project:\n\n```\ntest(\"a new user signs up and creates a project\", async ({ page }) => {\n  await page.goto(\"/signup\");\n  await page.fill(\"#email\", \"new@user.com\");\n  await page.fill(\"#password\", \"hunter2pw\");\n  await page.click(\"text=Create account\");\n  await expect(page).toHaveURL(\"/welcome\");\n\n  await page.click(\"text=New project\");\n  await page.fill(\"#title\", \"My first project\");\n  await page.click(\"text=Save\");\n  await expect(page.locator(\"text=My first project\")).toBeVisible();\n});\n```\n\n{/* KEEP: keep them FEW (a handful, not hundreds) because e2e tests are slow and heavier to maintain; keep them STABLE because a flaky test that fails at random trains you to ignore red, and an ignored net is no net. Flakiness usually = waiting on a fixed timer instead of a real condition. Bold-first: flaky. One Watch out callout on flakiness. */}\n\n## Keep them few and stable\n\nEnd-to-end tests are slow and heavier to maintain than the small ones, so keep them to a handful, the few journeys that lose you customers when they break.\n\nKeep them stable, too. A **flaky** test passes and fails at random on the very same code. It trains you to shrug at red, and a net you have learned to ignore is no net at all.\n\n> **Watch out:** Flakiness almost always means the test waits on a fixed timer instead of a real condition. Tell your agent to wait for the element or the URL to appear, never for a set number of seconds.\n\n{/* KEEP: the payoff = run the whole e2e set before every ship, so a break lands on your screen while you can still fix it, not in a user's message. Same gate as the agent's checks in testing-and-qa/guardrails; the automatic-on-every-change version comes in a later chapter (CI) without naming it here. Then the senior-voice prompt, ONE bottom slot \"My critical user journeys:\", then Do this now. */}\n\n## Run them before every ship\n\nA journey test is only worth writing if it actually runs. Run the whole set before every ship. Then a break lands on your screen while you can still fix it, not in a message from an angry user.\n\nMake it part of the checks your agent runs before it calls a task done, the same gate you set up for the safety net. A later chapter makes that gate run on its own, on every change.\n\nThis prompt sets your agent up to write them:\n\n```\nAct as a senior engineer writing end-to-end tests\nfor my app. For each journey I list below, write one\ntest that drives a real browser through the whole\nflow the way a user would: visit the page, fill the\nfields, click through every step, and assert on what\nthe user should finally see. Use Playwright. Keep\nthe set small and stable: wait for real elements and\nURLs, never fixed timers. Then add the suite to the\nchecks you run before calling any task done, and\ntell me which journeys you covered.\n\nMy critical user journeys:\n```\n\n**Do this now:** paste the prompt, list your three or four critical journeys, and let your agent write one end-to-end test for each, then wire the suite into the checks it runs before every ship."
          },
          {
            "slug": "test-driven-development",
            "title": "Writing the Test First",
            "label": "TDD",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/test/test-driven-development",
            "content": "---\ntakeaway: Write the test first\nshare: 'Most tests get written after the code, to confirm what already works. Flip the order and the failing test becomes a precise spec your agent codes to, one it cannot fake its way past.'\n---\n{/* KEEP: lead-in = reader knows tests as a safety net added AFTER code works; TDD flips the order: write the test first, it describes success in advance, the agent writes code until it passes. Ends by naming the payoff (how to run the loop + when it is worth it). Cross-ref the safety-net chapter by topic. */}\n\nYou have seen tests as a safety net you add after the code already works. There is a sharper move that flips the order: you write the test first, before the code exists, describing exactly what success looks like. Then the agent writes code until that test passes. This chapter shows you how to run that loop with an agent, and when the flip is worth it.\n\n{/* KEEP: concept = TDD means writing the test before the code it tests; the test IS the spec you build toward. Writing it first forces the definition of done before the feature exists. Bold-first: test-driven development. */}\n\n## Write the test before the code\n\n**Test-driven development** (TDD) means writing the test before the thing it tests. You describe the behavior you want as a test that must pass, then write only enough code to make it pass. The test is not a check you bolt on at the end. It is the specification you build toward.\n\nThe order sounds backwards until you see what it buys. You cannot write a test for \"checkout works\" without first deciding what \"works\" means. Writing the test first forces that definition before a single line of the feature exists.\n\n{/* KEEP: the three-beat cycle. Red: a failing test proves it actually checks something. Green: the least code to pass. Refactor: clean up, test stays green so you know nothing broke. Show the real red->green artifact. Bold-first: red-green-refactor. */}\n\n## Red, green, refactor\n\nThe cycle has a name, **red-green-refactor**, and three beats you repeat:\n\n1. **Red:** write one small failing test and run it. It fails because the code it needs does not exist yet, and that red proves the test really checks something.\n2. **Green:** write the least code that makes the test pass. Nothing extra, nothing clever, just green.\n3. **Refactor:** with the test passing, clean up the code. It stays green, so you know the cleanup broke nothing.\n\n```\n// 1. RED: write the test first, before the code exists.\ntest(\"splits a bill evenly\", () => {\n  expect(splitBill(100, 4)).toBe(25);\n});\n\n$ npm test\nFAIL  splitBill is not defined\n\n// 2. GREEN: the agent writes just enough to pass.\nfunction splitBill(total, people) {\n  return total / people;\n}\n\n$ npm test\nPASS  splits a bill evenly\n```\n\n{/* KEEP: the core why-AI point. A failing test is the most precise, unfakeable instruction you can hand an agent; green is a fact, not an opinion. It closes the agent's \"it's done\" escape hatch. */}\n\n## Why it fits AI so well\n\nHanding an agent a failing test is the most precise instruction you can give it. Most prompts describe what you want in words the agent can read loosely. A test is exact: this input must produce that output, and the machine runs it to check.\n\nIt also closes the agent's favorite escape hatch. An agent will cheerfully report a feature done. A red test that has to go green is a fact it cannot talk its way around, so you get a spec it codes toward and a finish line it cannot fake.\n\n{/* KEEP: judgment. Use for tricky logic with a right answer and for bugfixes (write the test that reproduces the bug, then fix to green); skip for exploratory UI / prototypes / judge-by-eye layout. Dividing line = can you state \"correct\" before building. Cross-ref the safety-net chapter for the cover-after path. */}\n\n## When to use it, when to skip it\n\nTDD shines when you can state success exactly before you build. It gets in the way when you are still discovering what you want.\n\n| Write the test first | Skip it, cover it after |\n| --- | --- |\n| Tricky logic with a right answer (pricing, dates, permissions) | Exploratory UI you are still shaping |\n| A bug: write the test that reproduces it, then fix to green | A quick prototype you may throw away |\n| A rule you must never break again | A layout you judge by eye, not by assertion |\n\nThe dividing line is that one question: can you write down what \"correct\" means before the code exists? When you can, write the test first. When the honest answer is \"I will know it when I see it,\" build first and cover it after, the way the safety-net chapter showed.\n\n```\nAct as a senior engineer practicing test-driven\ndevelopment on my app. Write one small failing\ntest that says exactly what success looks like,\nand run it so we both watch it fail. Then write\nthe least code that makes it pass, run it green,\nand refactor without breaking it. Work one test\nat a time, and never call a step done while its\ntest is still red.\n\nThe feature to build test-first:\n```\n\n**Do this now:** pick one piece of tricky logic in your app, write the failing test for it first, and let your agent turn it green before it writes anything else."
          },
          {
            "slug": "continuous-integration",
            "title": "Running Tests on Every Push",
            "label": "CI",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/test/continuous-integration",
            "content": "---\ntakeaway: Run tests on every push\nshare: 'You wrote tests, but they only guard you when someone remembers to run them. Continuous integration runs the whole suite automatically on every push and blocks the merge when it is red, so broken code can never land.'\n---\n{/* KEEP: lead-in = a test only protects you if it actually runs; on your own machine it runs only when you or the agent remember, so one skipped run lets a break slip through. This chapter makes the whole suite run itself on every push, on a machine that is not yours. Gloss push once (already met commit/branch/main/merge in version-control). */}\n\nYou wrote tests, and they pass. But a test only protects you if it actually runs, and on your own machine it runs only when you or the agent remember to. One skipped run and a break slips through anyway. This chapter makes your whole suite run itself on every push, every time your code goes up to the shared repo, on a machine that is not yours.\n\n{/* KEEP: concept = continuous integration (CI): a service that runs your entire test suite automatically on every push, on a fresh cloud machine that does not depend on your laptop or the agent remembering. Bold-first: continuous integration. Plain-link GitHub Actions to the official CI docs. A green result there means green for everyone. */}\n\n## Tests only help if they run\n\nThe fix is **continuous integration**, or CI: a service that runs your whole test suite automatically, on every push, on a fresh machine that is not yours. The common one is [GitHub Actions](https://docs.github.com/en/actions), and every major host has its own.\n\nBecause it runs from scratch in the cloud, it does not care what is installed on your laptop or whether the agent remembered to run anything. A green result there means green for everyone.\n\n{/* KEEP: the real artifact = a minimal GitHub Actions workflow (.github/workflows/ci.yml) that checks out the code and runs the test command on every push. Beginner framing: you do not memorize this, the agent writes it, what matters is the shape. Swap npm test for your command. */}\n\n## Run them on every push\n\nYou set this up with one small file in your repo. It installs your project on a clean machine and runs your tests every time you push:\n\n```yaml\n# .github/workflows/ci.yml\nname: CI\non: [push, pull_request]\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: 20\n      - run: npm install\n      - run: npm test\n```\n\nYou do not memorize this, your agent writes it for your stack. What matters is the shape: on every push, a fresh machine checks out your code and runs your test command. Swap `npm test` for whatever your project uses.\n\n{/* KEEP: running the tests is only half; the other half is refusing to merge on red. A branch protection rule requires the CI check to pass, so a branch with a red suite cannot merge into main. Mechanical gate, not discipline. Callout = rule of thumb. Do NOT overlap the deploy pipeline chapter. */}\n\n## Block the merge on red\n\nRunning the tests is only half of it. The other half is refusing to merge when they fail. Turn on a [branch protection](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches) rule that requires the CI check to pass, and a branch with a red suite simply cannot merge into main.\n\nNow broken code is not a matter of discipline. The gate is mechanical: green merges, red waits until it is fixed.\n\n> **Rule of thumb:** a test suite that cannot block a merge is a suggestion, not a guardrail.\n\n{/* KEEP: one file committed once guards every push forever, free, for every contributor, no maintenance. TEST-PART CLOSER: a tested app that stays green is one you can safely harden and change (bridge to the Harden part). Cross-ref automating-your-deployment BY TOPIC (the same gate later guards the deploy, do not teach CD here). Then prompt + Do this now. */}\n\n## Set it up once, it guards forever\n\nThat one file is committed once and then guards every push forever, for you and for anyone who ever touches the project. You do not run it or remember it. It runs on its own, free, and turns red the instant something breaks.\n\nThis closes your safety net. A suite that runs itself and a gate that blocks red give you an app you can change without holding your breath, which is exactly what the next part is for: hardening and reshaping code you can prove still works. Later, the chapter on automating your deployment puts the deploy itself behind this same gate.\n\nThis prompt wires CI for your project:\n\n```\nAct as a senior engineer setting up CI for me.\nAdd a workflow that runs on every push and on\nevery pull request: install the project on a\nclean machine and run my full test suite. Then\ntell me the exact branch protection setting that\nblocks a merge into main until that check is\ngreen. Keep it minimal and explain each line the\nfirst time.\n\nMy stack and test command:\n```\n\n**Do this now:** paste the prompt, commit the CI workflow your agent writes, then turn on the rule that blocks a merge until the suite is green."
          }
        ]
      },
      {
        "id": "harden",
        "title": "Make it a solid, complete product",
        "chapters": [
          {
            "slug": "designing-the-interface",
            "title": "Designing How It Looks and Feels",
            "label": "Interface",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/harden/designing-the-interface",
            "content": "---\ntakeaway: Give a design direction\nshare: \"Your app runs but looks like a default template. Sketch each screen, give every screen one clear action, and hand the agent a real design direction: a named style, references, and hard constraints, not 'make it nice.'\"\n---\n\n{/* KEEP: lead-in = app works but looks like a default the agent generated. Harden part OPENS here: production-grade starts with how it looks and feels, before faster/accessible (speed and accessibility come later, in the Scale part). Chapter gets reader to hand the agent a real design direction. Do NOT narrate the page. */}\n\nYour app works. Every feature does its job, but the whole thing still looks like what it is: a default the agent generated, generic type, flat gray boxes, no point of view. Making it production-grade starts at the surface, with how it looks and feels, before you make it faster and more accessible. This chapter gets you to hand your agent a real design direction, so the screens look like a product you chose to build, not a scaffold nobody styled.\n\n{/* KEEP: concept/mental model = YOU decide the layout, the agent renders it; don't let it design and build in one shot or it invents the generic. wireframe = rough box sketch of a screen. Show a monospace wireframe (Ledger, continuity with scaffolding). Bold-first: wireframe. */}\n\n## Sketch the screens first\n\nThe mistake is asking your agent to design and build a screen in one shot: you get whatever it invents, and it invents the generic. Decide the shape yourself first. A **wireframe** is a rough sketch of a screen, plain boxes for where things go, no color and no polish, just what sits where and how big.\n\nDraw it on paper or type it as labeled boxes. Name the boxes top to bottom and mark which one dominates:\n\n```\nLedger - Dashboard\n  [ topbar: Ledger ............. account ]\n  [ This month                          ]\n  [ $4,210 spent          (large, bold) ]\n  [ + Add expense     (primary button)  ]\n  [ Recent                              ]\n  [ - Groceries    $82   Jul 18         ]\n  [ - Fuel         $54   Jul 17         ]\n```\n\nHand that over and the agent builds your layout, not its best guess at one.\n\n{/* KEEP: each screen has ONE thing you most want the user to do = primary action, the loudest element; secondary actions stay quiet, competitors get demoted or cut. Rule-of-thumb callout: can't point at the one button within a second = no primary action yet. Bold-first: primary action. */}\n\n## One clear action per screen\n\nEvery screen has one thing you most want the user to do: add the expense, confirm the payment, start the trial. That is its **primary action**, and it should be the loudest element on the screen, one button styled stronger than anything near it. Secondary actions stay quiet, and anything that competes with it gets demoted or cut.\n\n> **Rule of thumb:** if you cannot point at the one button you most want tapped within a second of seeing a screen, that screen has no primary action yet. Fix that before you touch colors.\n\n{/* KEEP: three fundamentals the agent skips unless named. consistency = same styles everywhere; hierarchy = size/contrast points the eye at the primary action; feedback = every action visibly responds (working/saved/failed). Numbered list, bold-first each of the three. */}\n\n## Consistency, hierarchy, and feedback carry the polish\n\nWhat separates a professional interface from an amateur one is three habits your agent skips unless you name them:\n\n1. **Consistency:** the same spacing, colors, and buttons on every screen, so the app reads as one thing, not ten. Reuse styles, never reinvent per page.\n2. **Hierarchy:** size, weight, and contrast that make the important thing look important, so the eye lands on the primary action first.\n3. **Feedback:** every action visibly responds. A tapped button shows it is working, a saved form confirms, a failure says what went wrong.\n\nPut these three in your instructions and hold the agent to them.\n\n{/* KEEP: \"make it nice\" = no target = template average. design direction = named style + references + hard constraints, written once as a reusable design brief. Show the real design brief artifact (fenced). Material Design linked as one concrete example reference language. End with senior-voice prompt (brief + screen sketches, approve BEFORE styling) then Do this now. Bold-first: design direction, design brief. */}\n\n## Hand the agent a design direction, not \"make it nice\"\n\n\"Make it nice\" gives the agent no target, so it hands back the safe average: another template. Give it a **design direction** instead: a named style, a few references you admire (real products whose feel you want, or a design language like [Material Design](https://m3.material.io) your agent knows in depth), and hard constraints. Write it once as a short **design brief** and reuse it on every screen, because one brief everywhere is what makes ten screens feel like one app.\n\n```\nDesign brief - Ledger\nStyle: calm, precise, data-first. It should\n  feel like a sharp tool, not a landing page.\nMood: dark UI, one cool accent, generous\n  white space, square corners over rounded.\nType: one clean sans, two sizes, heavy weight\n  for the numbers.\nDo: strong hierarchy, consistent spacing, a\n  visible state for every action.\nDon't: gradients, stacked drop shadows, emoji,\n  stock-photo hero.\n```\n\nThis prompt has your agent draft that brief and lay out your screens before it styles anything:\n\n```\nAct as a senior product designer and front-end\nengineer. Before writing any styling, help me\nlock a design direction, then apply it.\n\nFirst, propose a short design brief: a named\nstyle with two or three real reference products,\na mood (color, spacing, shape), one type family,\nand an explicit do and don't list. Keep it\nopinionated and specific, never generic.\n\nThen, for each screen, sketch the layout as\nlabeled boxes and name the single primary\naction, the one thing I most want the user to\ndo, and make it the loudest element there.\n\nHold every screen to three rules: consistency\n(reuse spacing, color, and components), hierarchy\n(size and contrast point the eye at the primary\naction), and feedback (every action shows a\nloading, success, or error state).\n\nShow me the brief and the screen sketches for\nmy approval before you write any styling.\n\nMy app and the feel I want:\n```\n\n**Do this now:** paste the prompt, describe your app and the feel you want, and have your agent draft the design brief and screen sketches. Approve those before it writes a line of styling."
          },
          {
            "slug": "data-accounts-and-payments",
            "title": "Data, Users, and Payments",
            "label": "Accounts",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/harden/data-accounts-and-payments",
            "content": "---\ntakeaway: Real accounts, data, payments\nshare: 'A real app holds three things a demo never does: data that must survive, users who log in, and payments. Two of them you must never build yourself.'\n---\n\n{/* KEEP: lead-in = a real app holds 3 things a demo never does: persisted data, logins, payments. Two of the three (auth, payments) you must NEVER build yourself. Payoff = all three, plus the two hard rules that keep them from ending your app. */}\n\nYour demo runs fine on your laptop. A real app is different: it holds what people cannot afford to lose, their saved work, who they are, and their money. So it has to remember what they made after a restart, know them when they return, and charge them without ever mishandling a card. Two of those three you must never build yourself, and this chapter gets you all three plus the rules that keep them from ending your app.\n\n{/* KEEP: concept = a demo keeps data in memory and a restart wipes it; a real app PERSISTS it to a database. This is the first thing users trust you with, and the one part you cannot regenerate. Don't re-teach modeling; point to it. Bold-first: persist. */}\n\n## Store data that survives\n\nA demo keeps its data in memory, so closing it erases everything. A real app must **persist** what people create: write it to a database that survives a restart, a deploy, and a crash. The chapter on modeling your data covers the shape that data takes.\n\nTreat what you store as the most valuable thing you hold, because it is the one part you cannot regenerate. Back it up, and never run a change that could erase it.\n\n{/* KEEP: hard rule #1 = never build auth yourself. authentication = proving who a user is. It is a security specialty with many subtle failure modes; one slip exposes every account. Use a provider. Bold-first: authentication (linked to OWASP), provider. */}\n\n## Accounts: never roll your own auth\n\nThe moment people log in, you are doing [**authentication**](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html): proving each person is who they claim to be. It looks simple and is not. Password hashing, session tokens, password resets, two-factor, breach checks: each is a place to get security subtly wrong, and one mistake exposes every account at once.\n\nSo do not build it. Use an authentication **provider**, [Auth0](https://auth0.com), [Clerk](https://clerk.com), [Supabase Auth](https://supabase.com/auth), or your framework's own vetted library, whose whole job is getting this right and patching it as attacks change. You wire your app to it and never store a password yourself.\n\n{/* KEEP: hard rule #2 = a raw card number must never reach your server; you are not allowed to store one. PCI = the standard that governs it. Use a payments provider (Stripe): card goes browser -> provider -> you get a token you store instead. Show the do/don't artifact. Bold-first: PCI (linked), token. Link Stripe. */}\n\n## Payments: never touch card numbers\n\nPayments have a stricter rule: a raw card number must never reach your server, and you are not allowed to store one. Handling cards safely is governed by a standard called [**PCI**](https://stripe.com/guides/pci-compliance), and meeting it yourself is a compliance project, not a feature.\n\nSo you never touch the card. A payments provider like [Stripe](https://stripe.com) gives you a form that sends the card straight from the user's browser to them, then hands back a **token**, a harmless reference you keep in place of the card. You charge the token; the provider carries the risk.\n\n```\nDON'T let these reach your server\n  a card number, its CVC or expiry\n  a password you hashed yourself\n  login tokens you invented\n\nDO store only safe references\n  the auth provider's id for each user\n  Stripe's token in place of the card\n```\n\n{/* KEEP: synthesis = one pattern for all three, let specialists hold the dangerous parts and your app stores only references. Least access, encrypt, back up. Then the prompt hands the reader's specific case to their agent. */}\n\n## Protect what users trust you with\n\nThe pattern is the same for all three: let specialists hold the dangerous parts. The database keeps the data, the auth provider holds identities, the payments provider holds cards, and your app stores only references to them. Give each the least access it needs, encrypt anything sensitive, and back up what you cannot regenerate.\n\nThis prompt hands your case to your agent:\n\n```\nAct as a senior engineer wiring up what a real\napp needs beyond a demo: persisted data, user\naccounts, and payments. Enforce two hard rules.\nNever roll our own authentication: choose a\nprovider (Auth0, Clerk, Supabase Auth, or the\nframework's vetted library) and wire to it, so\nwe never store a password. Never let a raw card\nnumber reach our server: use a payments provider\n(Stripe) so the card goes browser to provider\nand we store only its token. For each of the\nthree, tell me what we store, what the specialist\nholds, and the least access it needs. Then list\nthe backup and encryption steps for the data we\ncannot regenerate.\n\nMy app: what it stores, who logs in, what they pay for:\n```\n\n**Do this now:** paste the prompt with your app's specifics and let your agent wire each of the three to a specialist, then confirm your server holds references, never a password or a card."
          },
          {
            "slug": "database-migrations",
            "title": "Changing the Database Safely",
            "label": "Migrations",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/harden/database-migrations",
            "content": "---\ntakeaway: Change the schema safely\nshare: \"Your app's structure keeps changing after real users exist. Change it safely through a migration: a versioned, reversible file your agent applies by command, never a hand-edit to the live database.\"\n---\n{/* KEEP: lead-in = app is live with real user data and its structure must change (new column, table split, missing field). This chapter: change a live database's structure safely, the same controlled way every time, and undo it cleanly when it goes wrong. */}\n\nYour app is live and people are using it, and now the structure has to change: a new column on `orders`, a table that should be split in two, a field that should have existed from day one. The data those users already created is sitting in the database, so you cannot just reshape it and hope. This chapter gets you changing a live database's structure safely, the same controlled way every time, and undoing the change cleanly when it goes wrong.\n\n{/* KEEP: concept = a migration is one versioned change to DB structure, written as a file, saved with the code, applied by a tool not by hand; the same numbered files replay identically across every environment. Bold-first: migration. Link the migration tool (dbmate) here on first mention. */}\n\n## Every schema change is a versioned file\n\nA **migration** is one change to your database's structure, written as a file and saved alongside your code. Add a column, rename a table, split one table into two: each becomes its own migration, numbered so the changes run in a fixed order.\n\nThat order is what makes them safe to trust. Your own machine, a test copy, and the live server all apply the same numbered files in the same sequence, so every one of them ends up with an identical structure. Your agent writes these files and a migration tool like [dbmate](https://github.com/amacneil/dbmate) applies them. You never type the changes into the database yourself.\n\n{/* KEEP: hard rule = NEVER edit the live database by hand (no console ALTER, no dashboard column). A hand-edit is unversioned, unreviewed, not replicated, invisible to the next release. Structure changes only through a committed migration file, applied by the tool. Watch out callout. */}\n\n## Never edit the live database by hand\n\nThis is the one rule that keeps the whole system honest: never open the live database and change its structure by hand. Not a quick `ALTER TABLE` in a console, not a column added through a hosting dashboard.\n\nA hand-edit exists in exactly one place, that single server. It is not in your code, so no one can review it, your test copy never receives it, and the next time new code goes live it has no idea the change ever happened. Structure changes only ever through a committed migration file, applied by the tool.\n\n> **Watch out:** The costly database disasters almost always start with a well-meaning manual fix on the live server. If it did not go through a migration, it lives on that one box alone, and it will collide with your next release.\n\n{/* KEEP: reversibility = every migration has an up (roll forward, apply) and a down (roll back, undo). Real artifact: dbmate up/down SQL file + apply/rollback commands. Bold-first: up, down. Note destructive changes (drop column) lose data, not cleanly reversible, back up first. Topic cross-ref to undoing a bad code change. Prompt (senior voice, one append slot \"The schema change I need:\"). Do this now = paste it. */}\n\n## Roll forward and roll back\n\nEvery migration has two halves. The **up** half rolls the change forward, applying it; the **down** half rolls it back, undoing it. When a change turns out bad, the tool runs the down half and the structure returns to exactly where it was.\n\n```sql\n-- db/migrations/20260720120000_add_order_status.sql\n\n-- migrate:up\nALTER TABLE orders\n  ADD COLUMN status text NOT NULL DEFAULT 'pending';\n\n-- migrate:down\nALTER TABLE orders\n  DROP COLUMN status;\n```\n\nYour agent applies it with `dbmate up` and reverses it with `dbmate down`, never by hand. The chapter on undoing a bad code change did this for your code; a migration does the same for your database's structure.\n\nOne catch: some changes cannot be undone perfectly. Dropping a column throws its data away, so its down half cannot bring that data back. Make those one-way changes deliberately, and take a backup before you run them.\n\nHand the change to your agent with the rules already set:\n\n```\nAct as a senior engineer changing a live\ndatabase safely. Turn my schema change into a\nmigration for the migration tool my stack uses.\nRules:\n- Write it as a versioned migration file, never\n  a hand-edit to the live database.\n- Give it an \"up\" that applies the change and a\n  \"down\" that reverses it.\n- If the change drops or rewrites existing data,\n  warn me and tell me what to back up first.\n- Show me the up and down before anything runs,\n  then apply it by command, not by hand.\n- Confirm the app still runs once it is applied.\n\nThe schema change I need:\n```\n\n**Do this now:** the next time your live app's structure has to change, paste the prompt with your change, and let your agent write the up-and-down migration instead of touching the database directly."
          },
          {
            "slug": "making-it-reliable",
            "title": "Handling Failure Gracefully",
            "label": "Reliability",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/harden/making-it-reliable",
            "content": "---\ntakeaway: Handle failure gracefully\nshare: \"Your app works when you click through it, then real users hit the dropped connection, the full disk, the service that is down. This chapter gets it to fail gracefully: handle every error instead of swallowing it, cover the empty and huge and concurrent cases your agent skipped, and never leave a half-written record behind.\"\n---\n{/* KEEP: lead-in = the Problem. The app works because you only ever run the happy path; real users hit the failure paths (network drop, full disk, service down), and an agent writes the happy path by default. Bold-first **happy path**. This chapter = make the app stay trustworthy when things fail. */}\n\nYour app works every time you click through it yourself. That is the **happy path**, the run where nothing goes wrong. Real users hit the paths where something does, and an agent writes only the happy one by default. This chapter gets your app to stay trustworthy when the rest of reality shows up.\n\n{/* KEEP: Concept = every reach outside the code can fail and eventually will; treat failure as a normal branch you handle, not a rare accident you ignore. List the three failure points you hit first (network, disk, dependency service). The agent assumes all three succeed. */}\n\n## Assume every call can fail\n\nEvery time your code reaches outside itself, that reach can fail, and eventually it will. Treat failure as a normal branch you handle, not a rare accident you ignore.\n\nThe three you hit first:\n\n- The network drops or times out mid-request.\n- The disk is full, or the file you expected is gone.\n- A service you depend on is down, or slow enough to look down.\n\nYour agent assumes all three succeed. Your job is to assume they will not.\n\n{/* KEEP: the core. Swallowing = a catch that returns a default/empty and moves on, so the failure vanishes with no record and the user is left blind. Handling = two jobs, always: log the real cause for you AND show the user a message they can act on. Real artifact: before/after code (empty-array swallow vs log + thrown user message). Rule-of-thumb callout: every catch does both jobs or it is a bug. */}\n\n## Catch the error, never swallow it\n\nWhen a call fails, an agent often buries it: a `catch` that returns an empty value and moves on. The app does not crash, so it looks fine, but the failure vanished with no record and the user is left staring at nothing.\n\nHandling an error means two things, always: log the real cause so you can find it, and show the user a message they can act on.\n\n```\n// Swallowed: the failure vanishes and the\n// user sees an empty list, never knowing why.\nasync function loadOrders() {\n  try {\n    return await api.getOrders()\n  } catch {\n    return []\n  }\n}\n```\n\n```\n// Handled: log the real cause for you, show\n// the user a message they can act on.\nasync function loadOrders() {\n  try {\n    return await api.getOrders()\n  } catch (err) {\n    logger.error('getOrders failed', err)\n    throw new Error(\n      'We could not load your orders. Try again.'\n    )\n  }\n}\n```\n\n> **Rule of thumb:** every `catch` earns its place by doing two jobs, a log for you and a message for the user. An empty one is a silent failure you will hear about from a confused user instead.\n\n{/* KEEP: define **edge case** = a condition at the boundary of what the code expects. The agent tests the comfortable middle and skips the edges, so name them and hand them back. Table of four: empty, huge, concurrent, malformed, and what breaks when ignored. Forward pointer (by topic) to QA turning these into tests. */}\n\n## Cover the edge cases the agent skipped\n\nAn **edge case** is a condition at the boundary of what the code expects, the empty input or the huge one, not the tidy middle it was demoed on. Your agent tests the comfortable middle and skips the edges, so name them and hand them back.\n\n| Edge case | What breaks when you ignore it |\n|---|---|\n| Empty (no rows, blank field, zero items) | The layout breaks, or the math divides by zero |\n| Huge (a 2 GB file, 10,000 rows at once) | The page freezes or the server runs out of memory |\n| Concurrent (two people save the same second) | One save silently overwrites the other |\n| Malformed (letters where a number goes) | The code crashes on the first bad value |\n\nLater, QA turns these same cases into tests that re-check them on every change. For now, handling them by hand is the win.\n\n{/* KEEP: the worst failure stops halfway (charge the card, crash before saving the order) = a half-written state, worse than a clean crash because nothing tells you it happened. Fix: group all-or-nothing steps into one **transaction** (the database undoes everything on failure). For non-DB steps (charge, email) make each safe to run twice. */}\n\n## Leave data consistent when it breaks\n\nThe worst failure is the one that stops halfway. A checkout charges the card, then crashes before it saves the order: the money moved and the record did not. That half-written state is far worse than a clean crash, because nothing tells you it happened.\n\nGroup the steps that must all happen, or none, into one **transaction**. The database runs them as a single unit and undoes everything if any step fails, so a crash leaves the data untouched. For steps outside the database, like charging a card or sending mail, make each one safe to run twice, so a retry never doubles the damage.\n\nThis prompt hardens one feature without changing what it does:\n\n```\nAct as a senior engineer hardening one feature\nfor reliability. Do not add features or change\nwhat it does.\n\nWork through this feature and:\n1. Find every call that can fail: network,\n   disk, database, or another service. Make\n   sure each is handled, not swallowed.\n2. For each failure, log the real cause for me\n   and return a message the user can act on.\n   Flag every empty catch or silent default.\n3. List the edge cases it ignores, empty,\n   huge, concurrent, and malformed, and handle\n   each one.\n4. Find any write that happens in several\n   steps. Wrap the steps that must all succeed\n   in one transaction, so a mid-way failure\n   leaves no half-written record.\n\nGive me the risks first as a short list, then\nthe fix for each. Touch behavior only where it\nwas unsafe.\n\nThe feature to make reliable:\n```\n\n**Do this now:** paste the prompt with your riskiest feature, the one that touches money, files, or another service, and let the agent turn its happy-path code into code that survives a failure."
          },
          {
            "slug": "refactoring",
            "title": "Paying Down AI Debt",
            "label": "Refactoring",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/harden/refactoring",
            "content": "---\ntakeaway: Delete more than you add\nshare: \"Your agent keeps over-building: extra layers, dead code, and abstractions for callers that never came. This chapter makes cleanup mostly subtraction, you refactor by deleting more than you add, with your tests as the safety net.\"\n---\n\n{/* KEEP: lead-in = the Problem. App runs but the agent quietly bloated the codebase; cleanup here means deleting more than adding. Ends naming the payoff: clean it up safely. Do NOT re-teach coupling or testing here. */}\n\nYour app works, but the code behind it has quietly bloated. Every feature you asked for, the agent answered with more structure than the job needed: an extra layer, an abstraction nobody calls twice. The codebase is harder to move through now, and you are not always sure which parts even run. This chapter gets you cleaning it up safely, by deleting more than you add.\n\n{/* KEEP: Concept = define refactoring (improve structure, behavior unchanged). Bold-first + link on **Refactoring** (Fowler, refactoring.com). Fowler's discipline: change structure and behavior separately. Tests are the safety net; reference testing BY TOPIC, not number. */}\n\n## Improve without changing behavior\n\n[**Refactoring**](https://refactoring.com) is improving the structure of code without changing what it does. The behavior your users see stays identical; only the shape underneath gets clearer. Martin Fowler named the practice and gave it one core discipline: change structure and behavior in separate steps, never both in the same move.\n\nThis is why tests come first. The suite that pins your app's behavior down is what lets you reshape the code without fear: change the structure, run the tests, and if they stay green, only the shape moved.\n\n{/* KEEP: Step = with AI the refactor inverts to subtraction. The delete-list (dead code, unused options, duplicate helpers, speculative abstraction). Rule-of-thumb blockquote: cannot justify a layer in one sentence, delete the layer. */}\n\n## Delete more than you add\n\nA human refactor usually means adding structure: extracting a shared function, adding a layer that was missing. With an agent the debt runs the other way. It already over-produced, so your cleanup is mostly subtraction, and every line you delete is a line you never have to debug.\n\nHunt for and delete:\n\n- dead code and branches that never run\n- options, parameters, and config nothing sets\n- duplicate helpers doing the same job\n- abstraction built for a second caller that never arrived\n\n> **Rule of thumb:** if you cannot say in one sentence why a layer exists, delete the layer, not the sentence.\n\n{/* KEEP: Step + Action = simplify what is used but overbuilt. THE artifact: before/after, factory+interface+config collapsed to one function, same behavior. Senior prompt (one append slot: The code that feels overbuilt:) then Do this now. */}\n\n## Simplify what the agent overbuilt\n\nDeletion removes what is unused. Simplification shrinks what is used but overbuilt. The agent reaches for enterprise patterns on a problem that wants a plain function: a factory to build one object, an interface with a single implementation. Here it was asked only to join a first and last name.\n\n```ts\n// The agent's version: a factory, an interface, and a\n// config object, all to join two strings.\ninterface NameFormatter {\n  format(user: User): string;\n}\n\nclass DefaultNameFormatter implements NameFormatter {\n  constructor(private config: FormatConfig = {}) {}\n  format(user: User): string {\n    const sep = this.config.separator ?? \" \";\n    return [user.first, user.last].filter(Boolean).join(sep);\n  }\n}\n\nexport function createNameFormatter() {\n  return new DefaultNameFormatter();\n}\n```\n\n```ts\n// Same behavior. Nothing else set the separator, so\n// it is gone too. One function does the whole job.\nexport function fullName(user: User): string {\n  return [user.first, user.last].filter(Boolean).join(\" \");\n}\n```\n\nSame inputs, same output, a quarter of the code and one obvious place to change it. Have your agent run that same pass on your worst file:\n\n```\nAct as a senior engineer refactoring for simplicity,\nnot adding features. Behavior must not change.\n\nFirst, state in one sentence what this code does, so\nwe have a fixed target to preserve.\n\nThen find everything safe to remove:\n- dead code and branches that never run\n- options, params, and config nothing sets\n- duplicate helpers that do the same job\n- factories, interfaces, or strategies with a\n  single implementation\n\nPropose the smallest version with identical behavior.\nShow a before/after diff and say what you removed and\nwhy removing it was safe.\n\nDo not add features, comments, or new abstraction. If\ntests exist, keep them green; if not, tell me what to\ntest before I delete anything.\n\nThe code that feels overbuilt:\n```\n\n**Do this now:** open the file in your app you least want to touch, run the prompt on it, and let your agent show you a smaller version that keeps every test green."
          }
        ]
      },
      {
        "id": "secure",
        "title": "Protect your app, data, and users",
        "chapters": [
          {
            "slug": "security",
            "title": "Working Code Isn't Safe Code",
            "label": "Why Secure",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/secure/security",
            "content": "---\ntakeaway: Working code isn't safe\nshare: 'Your app works, so it feels finished. But working and safe are two different things, and your agent only aimed for the first. You do not need to be a security expert, you need to know what to protect and hand each piece to your AI.'\n---\n{/* KEEP: lead-in = the app works so it feels done, but working and safe differ and the agent optimized for working. Reassure: you do not become a security expert, you learn what to protect and make the AI lock it down. This is the AWARENESS anchor of the Secure part; auth, attacks, audit are their own chapters, point forward by topic, don't deep-dive. */}\n\nYour app works. Every feature does what you asked, so it feels finished. But working and safe are two different things, and your agent only ever aimed for the first. This part gets your app safe, and the good news is you do not have to become a security expert to do it. You have to know what you are protecting, and make your AI lock each piece down.\n\n{/* KEEP: AI turns a request into code that RUNS, not code that is SAFE; you never asked for safety, so nothing guarantees it. The agent optimizes for the visible goal (it works) and skips the invisible one (it is safe). */}\n\n## AI writes working code, not safe code\n\nAsk your agent for a login and you get a working login. You did not ask it to stop someone from skipping that login, reading another user's data, or pasting in something that breaks your database, so nothing guarantees it does.\n\nThe agent optimizes for the goal you can see: the feature works. Safety is the goal you cannot see until it fails, so unless you name it, it gets skipped.\n\n{/* KEEP: security is the requirement nobody writes down; invisible until someone finds it missing. Unlike a broken feature you notice immediately, a security hole sits quiet until it is exploited. So you name it on purpose. */}\n\n## Security is the requirement nobody asks for\n\nA broken feature shows itself the moment you click. A security hole shows nothing. It sits quiet and working until the day someone finds it, and by then the damage is done.\n\nThat is why security has to be a deliberate step, not something you hope the agent included. You name the requirement out loud, the same way you named what the app should do.\n\n{/* KEEP: reassurance = you do not need to be an expert or learn to hack. You need to know WHAT to protect and hand it to the AI, which is genuinely good at applying known defenses once you point at them. The judgment is yours, the implementation is the agent's. */}\n\n## You don't need to be an expert\n\nThis is not a hacking course, and you will not be writing cryptography. You need one thing the agent does not have on its own: the judgment to say \"protect this.\"\n\nOnce you point at a risk, the agent is genuinely good at applying the known, standard defense for it. Your job is knowing what to point at. The rest of this part hands you that list.\n\n{/* KEEP: name what you're protecting = the four surfaces. Show the artifact. Your DATA (what you store), your ACCOUNTS (who gets in), your INPUTS/SURFACES (everything a stranger can reach and send), your SECRETS (keys). The part walks each; forward-point by topic to auth, attacks, audit. */}\n\n## Name what you're protecting\n\nAlmost every risk lands on one of four things. Name them, and you know what to hand your agent:\n\n```\nProtect:\n- Your DATA      what you store about users\n- Your ACCOUNTS  who is allowed in, and as whom\n- Your SURFACES  every input a stranger can reach\n- Your SECRETS   the keys that unlock everything\n```\n\nThe next chapters take these in turn: locking the doors with real auth, guarding your surfaces against the common attacks, and running a full audit before you launch. Start by having your agent map the risks for your specific app:\n\n```\nAct as a senior security engineer reviewing my app\nbefore launch. In plain language, no jargon, list the\nmain things I need to protect: the user data I store,\nwho can log in and what they can reach, the inputs a\nstranger can send, and the secrets that must stay\nhidden. For each, name the single biggest risk and the\nstandard way to defend it. Do not fix anything yet,\njust give me the map.\n\nMy app and what it handles:\n```\n\n**Do this now:** paste the prompt, get your app's plain-language risk map, and keep it open as you work through the rest of this part."
          },
          {
            "slug": "auth-and-tokens",
            "title": "Authentication, Sessions, and Tokens",
            "label": "Auth & Tokens",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/secure/auth-and-tokens",
            "content": null
          },
          {
            "slug": "validating-input",
            "title": "Trusting No Input",
            "label": "Validation",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/secure/validating-input",
            "content": null
          },
          {
            "slug": "common-attacks",
            "title": "The Attacks to Know and Audit For",
            "label": "Common Attacks",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/secure/common-attacks",
            "content": null
          },
          {
            "slug": "security-audit",
            "title": "Audit It, Then Get a Review",
            "label": "Audit",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/secure/security-audit",
            "content": null
          }
        ]
      },
      {
        "id": "protect",
        "title": "Handle user data responsibly and legally",
        "chapters": [
          {
            "slug": "owning-user-data",
            "title": "You Own the Data You Collect",
            "label": "Responsibility",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/protect/owning-user-data",
            "content": "---\ntakeaway: You own collected data\nshare: \"The moment real users arrive, their data is your responsibility, not just an asset. Collect only what you need, know exactly what you hold, and you carry far less risk.\"\n---\n{/* KEEP: lead-in = first real users mean their data is now something you are on the hook for, not just a feature you built. Sets up the Protect part. Ends by naming the payoff: treat collected data as a responsibility you manage, starting by knowing what you hold. */}\nYour app just got its first real users, and every signup writes something real into your database: a name, an email, maybe a card number. Until now that data was a feature you were building. The moment a stranger trusts you with theirs, it becomes something you are on the hook for. This chapter gets you to treat collected data as a responsibility you manage, and it starts by knowing exactly what you hold.\n\n{/* KEEP: concept = their data is a liability, not an asset. Every record is something to protect, keep accurate, and be ready to delete. A leak is their exposed address or reused password, not your inconvenience. Bold-first: liability. */}\n## Their data is your responsibility\n\nIt is tempting to think of user data as an asset, a pile of value you are accumulating. Flip that. The moment it is real people's data, it is a **liability**: something you now have to protect, keep accurate, and be ready to hand back or delete.\n\nThat changes who a mistake hurts. A leak is not your inconvenience, it is a real person's home address in the open or their reused password in an attacker's hands. You are holding it on their behalf, so their exposure is your responsibility.\n\n{/* KEEP: step = collect only what you actually need. Less data = less risk + less liability. Data minimization is the cheapest control: data you never stored cannot be stolen. Be strictest with PII. Rule of thumb: if you cannot name why, do not collect it. Bold-first: data minimization, PII. */}\n## Collect only what you need\n\nEvery field you collect is one more thing to secure, keep correct, and answer for if it leaks. So collect the least you can. This is **data minimization**, and it is the cheapest security control you have: data you never stored cannot be stolen.\n\nBe strictest with **personally identifiable information (PII)**, the data that points at a specific human, like their email, phone, or home address. Ask of each field whether the app truly needs it now, or whether you kept it just because it was easy.\n\n> **Rule of thumb:** If you cannot name why you store a field, do not collect it.\n\n{/* KEEP: step = know what you hold and why. The data inventory artifact: what we store, why, how sensitive. Ties back to the data model (modeling-your-data) by topic: the inventory adds the why and the risk. Show the fenced inventory. Bold-first: data inventory. */}\n## Know what you hold and why\n\nYou cannot protect what you have not written down. A **data inventory** is one plain list of every kind of data you store, why you keep it, and how sensitive it is. You already sketched the things your app stores when you modeled your data; the inventory adds the why and the risk.\n\nHave your agent produce it and keep it in your system. It looks like this:\n\n```\nData            Why we store it       Sensitivity\nemail           login and receipts    medium\npassword hash   login                 high\ncard last 4     show the saved card   medium\nIP address      rate limiting         low, drop at 30 days\n```\n\nThat one page tells you where to spend your protection effort, and it is the first thing you reach for when something goes wrong.\n\n{/* KEEP: closing = the same clean inventory pays twice. Forward-link (one clause, by topic) to observability/Operate: clean, well-kept data is what powers AI-driven decisions and debugging once the app is live and you are watching it run. Then the prompt + Do this now. */}\n## Clean data pays you back twice\n\nKeeping this data lean and labeled is not only about risk. The same clean, well-kept data is what your AI reads to make decisions and trace bugs once the app is live. That is the second payoff the Operate part builds on, when you are watching the app run. Kept clean now, it pays twice.\n\nThis prompt has your agent draft the inventory from your app:\n\n```\nAct as a senior engineer auditing what my app\ncollects. List every kind of data it stores as a\ndata inventory: the field, why we store it, and how\nsensitive it is (low, medium, high). Flag any field\nthat is personally identifiable, and call out\nanything we collect without a clear reason so I can\ndrop it. Keep it one plain table.\n\nMy app and the data it collects:\n```\n\n**Do this now:** paste the prompt, let your agent draft your data inventory, and delete any field it cannot give a clear reason for."
          },
          {
            "slug": "privacy-and-consent",
            "title": "Privacy and Consent",
            "label": "Privacy",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/protect/privacy-and-consent",
            "content": null
          },
          {
            "slug": "encrypting-data",
            "title": "Encrypting the Data You Hold",
            "label": "Encryption",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/protect/encrypting-data",
            "content": null
          },
          {
            "slug": "data-retention-and-rights",
            "title": "Keeping, Deleting, and Handing It Back",
            "label": "Retention",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/protect/data-retention-and-rights",
            "content": null
          },
          {
            "slug": "compliance-and-the-legal-line",
            "title": "Compliance and the Legal Line",
            "label": "Compliance",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/protect/compliance-and-the-legal-line",
            "content": null
          }
        ]
      },
      {
        "id": "ship",
        "title": "Deploy to production on real infrastructure",
        "chapters": [
          {
            "slug": "deploying-to-production",
            "title": "Going to Production",
            "label": "Deployment",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/ship/deploying-to-production",
            "content": "---\ntakeaway: Ship to real infrastructure\nshare: \"Your app runs on your laptop and nobody else can reach it. Going to production means putting it on real infrastructure the world can use, rebuildable from files, not clicked together by hand.\"\n---\n{/* KEEP: lead-in = your app works on your machine, no one else can open it. Production = the leap to a real server the world reaches, carried by ONE mindset: the whole setup is rebuildable from files, not clicked by hand. This chapter frames the whole Ship part. */}\n\nYour app works on your machine, and nobody else can open it. Production is the leap to a real server the world can reach. This chapter gets you there, and hands you the one mindset that makes every later step in this part sane: the whole setup is rebuildable from files, never clicked together by hand.\n\n{/* KEEP: concept = production is not a place you configure by hand once; anything set by clicking a dashboard is lost the day the server dies. Real production is described in files you re-run to recreate the whole thing. Bold-first: production. */}\n\n## Production is rebuildable, not clicked\n\n**Production** is not a machine you configure by hand and hope never dies. Anything you set by clicking around in a dashboard is gone the day that server does, and you are rebuilding it from memory.\n\nReal production is described in files. You run those files and the whole setup comes back, identical, on a fresh machine. That single idea is what the rest of this part builds, so treat every by-hand click as a note you still have to turn into a file.\n\n{/* KEEP: pick where it runs; honest default = cheap own-the-metal, not the big-cloud maze. A small box on a provider like Hetzner costs a fraction of the hyperscaler equivalent and you don't pay a tax for 100 services you'll never use. Rule of thumb: avoid the AWS maze unless you have a reason; if you must, raw EC2. Link Hetzner. */}\n\n## Pick a host you can afford to keep\n\nThe honest default is cheap, own-the-metal hosting, not the big-cloud maze. A small server on a provider like [Hetzner](https://www.hetzner.com) costs a fraction of the same box on a hyperscaler, and you are not paying a tax for a hundred managed services you will never touch.\n\n> **Rule of thumb:** skip the AWS managed maze unless you have a specific reason to be in it. If you must be on AWS, a plain server (an EC2 box) beats wiring ten services together to run one app.\n\n{/* KEEP: do it by hand ONCE so you see every piece (a server, a domain, the app running, a way in); automating something you've never done by hand hides the parts that break. Then never again: capture each step as files, a deploy becomes a command not an afternoon. Prompt has agent draft the deploy plan for their host. */}\n\n## Deploy by hand once, then never again\n\nThe first time, do it by hand. Stand up a server, point a domain at it, get the app running, and open it from another device. Doing it once shows you every moving part, and automating a thing you have never done by hand only hides where it breaks.\n\nThen you never do it by hand again. Every step you just took becomes a file in the chapters that follow, and from then on a deploy is one command, not a lost afternoon.\n\nThis prompt turns your first deploy into a written plan:\n\n```\nAct as a senior engineer planning my first production\ndeploy. For my stack and chosen host, lay out the smallest\nreal path to live: the server, the domain and TLS, how the\napp runs, and how I reach it. Keep it manual for now, but\nnote which step becomes a file I automate later.\n\nMy stack and host:\n```\n\n**Do this now:** paste the prompt, get your manual deploy plan, and stand the app up on a real host once, by hand."
          },
          {
            "slug": "dev-staging-production",
            "title": "Dev, Staging, Production",
            "label": "Environments",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/ship/dev-staging-production",
            "content": null
          },
          {
            "slug": "infrastructure-as-code",
            "title": "Infrastructure as Code",
            "label": "IaC",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/ship/infrastructure-as-code",
            "content": null
          },
          {
            "slug": "containers-and-kubernetes",
            "title": "Package and Run Your App",
            "label": "Containers",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/ship/containers-and-kubernetes",
            "content": null
          },
          {
            "slug": "secrets-in-production",
            "title": "Keys and Passwords in Production",
            "label": "Secrets",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/ship/secrets-in-production",
            "content": null
          },
          {
            "slug": "scripts-for-every-operation",
            "title": "One Command per Operation",
            "label": "Scripts",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/ship/scripts-for-every-operation",
            "content": null
          },
          {
            "slug": "automating-your-deployment",
            "title": "Automating the Deploy",
            "label": "CI/CD",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/ship/automating-your-deployment",
            "content": null
          },
          {
            "slug": "safe-releases-and-rollbacks",
            "title": "Safe Rollouts and Rollbacks",
            "label": "Releases",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/ship/safe-releases-and-rollbacks",
            "content": null
          }
        ]
      },
      {
        "id": "operate",
        "title": "Run and maintain it in production",
        "chapters": [
          {
            "slug": "logging",
            "title": "Leaving a Trail",
            "label": "Logging",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/operate/logging",
            "content": "---\ntakeaway: Leave a useful trail\nshare: 'When something breaks in your live app, clean structured logs are the difference between an answer in seconds and a guess. Learn what to log, what never to log, and how to make it searchable.'\n---\n{/* KEEP: lead-in = app is SHIPPED and live (opens Operate); you cannot see inside it; scattered print statements are noise you cannot search when it counts; chapter gets logs you can read back later and actually trust. */}\n\nYour app is shipped and running, and right now you cannot see inside it. A user hits an error, a nightly job dies, a page crawls, and you have no idea why. Scattered `print` statements are not a trail, they are noise you cannot search when it counts. This chapter gets you logs you can read back later and actually trust.\n\n{/* KEEP: log meaningful events with context (which user/record/what failed); NEVER secrets/passwords/tokens/API keys/card numbers/full personal data; OWASP link for what-never-to-log. Watch out = whole request/response objects smuggle secrets, log named fields not the raw blob. Do NOT re-teach the event-ledger. */}\n\n## Log the events, never the secrets\n\nA log is the app narrating what it just did. Log the moments that matter: a request handled, an error caught, a payment taken. Give each one enough context to answer \"what happened\" later, which user, which record, what failed.\n\nThe harder discipline is what never goes in. Never write sensitive data to a log: passwords, access tokens, API keys, full card numbers, anyone's private information. Logs get copied, forwarded to other services, and read by people who should never see any of it. The [OWASP logging guidance](https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html) spells out the full list; the rule of thumb is log that a login happened and whose, never the password typed.\n\n> **Watch out:** the fastest way to leak secrets is logging a whole request or response object. Tokens and personal data ride along inside it. Log named fields, never the raw blob.\n\n{/* KEEP: bold-first log level (debug/info/warn/error) as a filter; bold-first structured logging = emit each log as data (level + named fields) not a glued sentence, so it is searchable/countable. Artifact = before/after: bare print string vs structured JSON line with level+fields, no leaked data. */}\n\n## Levels and fields make logs searchable\n\nGive every line a **log level** so you can filter by how much it matters:\n\n- `debug`: detail you only want while building\n- `info`: a normal event worth recording\n- `warn`: something off but handled\n- `error`: something broke\n\nIn production you keep `info` and up, and stay quiet below it.\n\nThen use **structured logging**: emit each log as data, a level plus named fields, instead of gluing a sentence together. A sentence you can only skim by eye. Structured lines you can search, filter, and count across millions of them.\n\n```\n# Before: a sentence you cannot search, leaks data\nprint(\"user \" + email + \" logged in with \" + pw)\n\n# After: structured, leveled, safe to ship\nlog.info({ event: \"auth.login\", userId: 4821, ok: true })\n# -> {\"level\":\"info\",\"event\":\"auth.login\",\n#     \"userId\":4821,\"ok\":true,\"ts\":\"2026-07-10T08:14Z\"}\n```\n\n{/* KEEP: bold-first observability = ask any question of the running system, get an answer from what it emits; clean structured logs are the base every metric/alert/timeline in the rest of Operate sits on (cross-ref by topic, don't name later chapters). Ends with senior prompt (one structured logger, levels, redaction, stack-appropriate; ONE append slot) then Do this now. */}\n\n## Logs feed observability\n\nClean, structured logs are the raw material for **observability**: asking any question of your running system and getting an answer from what it already emits. Why is this slow? How often does this fail? What did this one user hit?\n\nRandom print strings answer none of that. Structured events you can search, count, and alert on, and that is the base every metric, alert, and timeline in the rest of this part sits on.\n\nSet that foundation now, one structured logger your whole app writes through:\n\n```\nAct as a senior engineer setting up logging for\nmy running app. Replace scattered print and\nconsole statements with one structured logger the\nwhole app calls through.\n\nRequirements:\n- Emit each log as JSON with a level (debug, info,\n  warn, error), a timestamp, an event name, and\n  named fields.\n- Log meaningful events, caught errors, and slow\n  or failed operations, each with enough context\n  to trace what happened.\n- NEVER log passwords, tokens, API keys, card\n  numbers, or full personal data. Redact those\n  fields before anything is written.\n- Pick the standard structured logger for my\n  stack and give me the exact setup and commands.\n\nMy app and what I need to see:\n```\n\n**Do this now:** paste the prompt, replace your `print` statements with one structured logger, and write your next log line with a level and named fields, no secrets in it."
          },
          {
            "slug": "observability",
            "title": "Seeing Inside Your App",
            "label": "Observability",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/operate/observability",
            "content": null
          },
          {
            "slug": "monitoring-and-alerts",
            "title": "Monitoring and Getting Paged",
            "label": "Alerting",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/operate/monitoring-and-alerts",
            "content": null
          },
          {
            "slug": "backups-and-disaster-recovery",
            "title": "Disaster Recovery",
            "label": "Backups",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/operate/backups-and-disaster-recovery",
            "content": null
          },
          {
            "slug": "maintenance-and-incidents",
            "title": "Maintenance When Things Break",
            "label": "Incidents",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/operate/maintenance-and-incidents",
            "content": null
          },
          {
            "slug": "watching-the-bill",
            "title": "Watching the Bill",
            "label": "Cost",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/operate/watching-the-bill",
            "content": null
          },
          {
            "slug": "automation",
            "title": "Letting the System Run Itself",
            "label": "Automation",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/operate/automation",
            "content": null
          }
        ]
      },
      {
        "id": "scale",
        "title": "Grow it to handle real traffic and data",
        "chapters": [
          {
            "slug": "bottlenecks",
            "title": "Finding the Limits",
            "label": "Bottlenecks",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/scale/bottlenecks",
            "content": "---\ntakeaway: Find the real bottleneck\nshare: \"Your app is live and growing, and something will buckle first under the load. It is almost never the part you would guess. This chapter gets you to find the one real bottleneck by measuring, so you fix what actually moves and ignore the rest.\"\n---\n{/* KEEP: lead-in = opens the Scale part. The app is live and healthy (shipped, monitored, operating); now it is growing and something buckles first under load, almost never the part you would guess. This chapter = find the one real limit before touching anything. */}\n\nYour app is live and healthy: shipped, monitored, running without you babysitting it. Now it is growing, more users and more data arriving at once, and something in it will buckle first. That part is almost never the one you would guess. This chapter gets you to find the one real limit before you change a single line.\n\n{/* KEEP: concept = a bottleneck is the single slowest part that sets the ceiling for the whole system, the pipe's narrowest point. A request touching ten parts is only as fast as its slowest one, so the system's speed is the slowest part, not the average. Bold-first **bottleneck**; gloss **throughput**. */}\n\n## A bottleneck caps the whole system\n\nA **bottleneck** is the single slowest part that caps everything around it, the narrowest point in the pipe. Water moves only as fast as the tightest section allows, no matter how wide the rest of the pipe is.\n\nYour app works the same way. A request that touches ten parts is only as fast as its slowest one. The system's real speed, and its **throughput**, how much it can handle at once, is set by that single part, not by the average of the parts.\n\n{/* KEEP: never guess at what is slow. Slowness rarely lives where it feels like it does; under load the part that buckles is usually invisible from the outside (a query, not the code you stare at). Measure first: reproduce real load, watch where time goes. Cross-ref by TOPIC to the page-speed chapter (same rule, now system-wide) and to the observability already set up. */}\n\n## Measure before you optimize\n\nThe rule that saves you the most wasted effort: never guess at what is slow. Slowness rarely lives where it feels like it does, so a day spent speeding up the wrong part moves nothing. You met this rule tuning page speed; at scale it governs the whole system, not one screen.\n\nThe part that buckles under load is usually invisible from the outside: a database query, not the code you stare at. So measure first: put the app under realistic load, many requests at once, not the single idle click you test by hand. Read where the time goes from the traces and logs you already have from making the app observable.\n\n{/* KEEP: find the slowest single part with real data: a profiler breaks one request into timed spans; simple timing; the database's slow query log (its record of queries that ran too long). The artifact = a request timing breakdown where ONE span dominates (db list = 814 of 842ms), the rest is noise. Bold-first **profiler**, **slow query log**. */}\n\n## Find the slowest part\n\nThree tools turn \"it feels slow\" into a number. A **profiler** breaks a single request into timed spans and shows how long each part took. Simple timing does the same by hand. The **slow query log** is the database's own record of every query that ran too long, and it is where scale problems hide most often.\n\nRun one and you get something like this:\n\n```\nGET /orders            842 ms total\n  auth check             3 ms\n  render page           11 ms\n  db: list all orders  814 ms   <- bottleneck\n  db: look up user       9 ms\n```\n\nOne span dominates. The 814 ms query is the bottleneck; everything else is rounding error. You are not guessing anymore, you can see it.\n\n{/* KEEP: fix ONLY the top one; fixing anything else moves the total by nothing (make everything but the 814ms query twice as fast, save a handful of ms). Rule-of-thumb callout: speeding up anything but the bottleneck barely changes the total. Then re-measure: the bottleneck moves to a new part; repeat, or stop when fast enough. Bridge opens the rest of the part. Prompt = whole loop, senior voice, one append slot. */}\n\n## Fix the one that matters\n\nFix that one query and nothing else. It is tempting to tidy the render step or the auth check while you are in there, but do the math: make everything except the 814 ms query twice as fast, and you save a handful of milliseconds out of eight hundred.\n\n> **Rule of thumb:** speeding up anything but the bottleneck barely changes the total. Finding it beats optimizing on instinct every time.\n\nThen measure again. The bottleneck does not vanish, it moves: fix the query and the next-slowest part becomes the new ceiling. Repeat until the app is fast enough for the load you have, then stop.\n\nThe rest of this part is how you fix each kind of bottleneck you find; this prompt finds the first one without letting your agent guess:\n\n```\nAct as a senior engineer hunting a performance\nbottleneck. Do not guess and do not optimize on\ninstinct. Work from measurement only.\n\n1. Reproduce the slowness under realistic load,\n   not a single idle request. State how you load\n   it and what you measured.\n2. Profile it: break the slow path into timed\n   parts (request spans, function timing, the\n   database slow query log). Show where the time\n   actually goes.\n3. Rank the parts by share of total time. Name\n   the single slowest one. That is the bottleneck.\n4. Fix only that one. Explain the change and why\n   it attacks the real cause, not a symptom.\n5. Measure again and show before and after. Say\n   what the new slowest part is, and whether it is\n   worth chasing or already fast enough.\n\nReport numbers, not adjectives. If you cannot\nmeasure a step, say so instead of guessing.\n\nWhat feels slow and when:\n```\n\n**Do this now:** pick the one page or action your users call slowest, paste the prompt, and let your agent measure where the time actually goes before you change a single line."
          },
          {
            "slug": "accessibility-and-performance",
            "title": "Speed and Accessibility",
            "label": "Performance",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/scale/accessibility-and-performance",
            "content": "---\ntakeaway: Fast and reachable for everyone\nshare: \"Your app can be correct and secure and still lose people: too slow to wait for, or impossible to use by keyboard or screen reader. This chapter gets you fast enough that users stay and reachable by everyone, measured, not guessed.\"\n---\n{/* KEEP: lead-in = an app can be correct, secure, and reliable and STILL fail two ways: too slow (users leave before it loads) or unreachable (no keyboard/screen reader = locked out). This chapter = fast enough that users stay + reachable by everyone, both measured not guessed. Last Harden chapter. */}\n\nYour app is correct, secure, and reliable. It can still fail two ways nobody warned you about: it loads so slowly that people leave before they see it, and it locks out anyone who cannot use a mouse or read a small gray label. This chapter gets you fast enough that users stay, and reachable by everyone, both measured rather than guessed.\n\n{/* KEEP: concept = speed is retention, not vanity; every extra second loses people and costs trust. The industry yardstick = Core Web Vitals (load, responsiveness, visual stability), the same numbers search ranks on. Cross-ref by topic to the speed targets set in Plan (\"quality bars\"), never by number. Bold-first + link: Core Web Vitals. */}\n\n## Fast enough that users stay\n\nSpeed is retention, not vanity. Every extra second before your app responds, more people give up and leave, and the ones who stay trust it less. The industry yardstick is [**Core Web Vitals**](https://web.dev/articles/vitals): three numbers for how fast the page loads, how quickly it reacts to a tap, and whether it jumps around while loading.\n\nThese are the same measures search engines rank on, so they pay off twice. You set speed targets back when you wrote your quality bars. This chapter is where you actually hit them.\n\n{/* KEEP: THE discipline of the chapter: never guess at what is slow. Slowness rarely lives where it feels like it does, so guessing wastes a day and moves nothing. Run an audit tool (Lighthouse), fix the single biggest offender, measure again, loop. The fenced checklist = the usual suspects the audit points at, handed to the agent. Link Lighthouse. Gloss N+1 in one clause. */}\n\n## Measure before you optimize\n\nThe one rule that saves you the most wasted effort: never guess at what is slow. Slowness rarely lives where it feels like it does, and a day spent optimizing the wrong thing moves nothing. Run an audit first, fix the single biggest offender it names, then measure again.\n\nThe tool for this is [Lighthouse](https://developer.chrome.com/docs/lighthouse/overview), built into your browser. It scores the page and points at the real problem, almost always one from a short list you can hand your agent as an audit:\n\n```\nPerformance audit, usual suspects:\n[ ] Images too large or not sized: compress, resize, lazy-load\n[ ] Too much JavaScript: ship less, load heavy parts on demand\n[ ] A query run once per row (the N+1 trap): fetch in one go\n[ ] No caching: serve repeat reads from cache, not recompute\n[ ] Fonts or third-party scripts blocking the first paint\n```\n\nFix the top hit, measure again, and repeat until you land inside your targets.\n\n{/* KEEP: accessibility = the app works for people who cannot use it your way: keyboard instead of mouse, a screen reader, or high contrast. Not a favor, it's a published standard (WCAG) and often a legal requirement. The lift is small because the agent knows the rules: semantic markup, labels, focus order, contrast. Bold-first: accessibility. Link WCAG plain. */}\n\n## Reachable by everyone\n\n**Accessibility** means the app works for people who cannot use it the way you do: moving by keyboard instead of a mouse, hearing the page through a screen reader, or needing high contrast to read it at all. This is not a favor. It is a published standard, [WCAG](https://www.w3.org/WAI/standards-guidelines/wcag/), and in many places a legal requirement.\n\nThe lift is smaller than it sounds, because your agent already knows the rules. Ask it for semantic markup, a label on every control, a sensible focus order, and text that meets contrast, and most of the work is already done.\n\n{/* KEEP: both speed and accessibility are TESTABLE, so test them the way a real user hits them: run the audit for a score, then put the mouse away and drive the whole app by keyboard. If you get stuck, so does someone with no other way in. Prompt hands the full audit + top fixes to the agent. Closing nod: fast + secure + reliable + accessible = production-grade, ready to ship. No re-teaching. */}\n\n## Test it like a real user\n\nBoth of these are testable, so test them the way a real user would hit them. Run the audit for a score, then put your mouse down and try to reach every button, form, and menu by keyboard alone. If you get stuck, so does someone who has no other way in.\n\nThen let your agent do the heavy lifting. This prompt runs the full audit and fixes what hurts most:\n\n```\nAct as a senior engineer auditing my app for speed and\naccessibility. Run a performance and accessibility audit,\nthen list the biggest issues in order of user impact.\nFor each, name the cause and the smallest fix, and fix\nthe top few. Confirm the whole app works by keyboard and\nmeets AA contrast. Measure again and show before/after.\nDo not chase a perfect score, chase real problems.\n\nMy app's slow or hard-to-use spots:\n```\n\n**Do this now:** paste the prompt, let your agent measure and fix the top issues, then walk the app once by keyboard yourself. Fast and reachable for everyone is what keeps the users your growth depends on."
          },
          {
            "slug": "caching-and-the-database",
            "title": "Easing the Database",
            "label": "Caching",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/scale/caching-and-the-database",
            "content": null
          },
          {
            "slug": "scaling-out",
            "title": "Adding Capacity",
            "label": "Horizontal Scaling",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/scale/scaling-out",
            "content": null
          },
          {
            "slug": "capacity-planning",
            "title": "Staying Ahead of Growth",
            "label": "Capacity Planning",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/scale/capacity-planning",
            "content": null
          },
          {
            "slug": "the-improvement-loop",
            "title": "The Improvement Loop",
            "label": "Optimization",
            "status": "published",
            "access": "paid",
            "url": "https://zalt.me/guides/vibe-coding/scale/the-improvement-loop",
            "content": null
          },
          {
            "slug": "when-to-call-a-pro",
            "title": "When to Call a Pro",
            "label": "Expertise",
            "status": "published",
            "access": "free",
            "url": "https://zalt.me/guides/vibe-coding/scale/when-to-call-a-pro",
            "content": "---\ntakeaway: Know when to hire help\nshare: \"Some of the work in a real product is genuinely beyond what AI does well right now: security judgment, high-stakes architecture, legal calls. This closing chapter helps you spot that line and hand the risky part to a human without feeling like you failed.\"\n---\n{/* KEEP: lead-in = the Problem. Reader carried an idea to a scaling product real people use, almost all of it with AI. Now hits the exception: a call where the stakes are money / user-data / legal and he cannot tell if the AI answer is right. This final chapter gives the judgment to know when to bring in a human, what to hand off, where to find one. */}\n\nYou took an idea all the way to a product real people use, one that holds up as it grows, almost all of it built with your agent. Now you hit the exception: a security call, an architecture decision at real scale, a legal question where being wrong costs money or trust. And you cannot tell whether the answer in front of you is right. This closing chapter gives you the judgment to know when to bring in a human, what to hand them, and where to find one.\n\n{/* KEEP: Concept = AI is excellent at the well-trodden 80% and dangerous on the hard 20%, where it is confidently wrong in a way you cannot catch, and that slice is where the stakes are highest. Name the four kinds of work that sit there as a list (4 parallel items). Bold-first: compliance (glossed). */}\n\n## What AI can't do for you\n\nYour agent is excellent at most of the work, the well-trodden 80%. The risk is the other slice, where it can be confidently wrong in a way you cannot catch, and that slice is usually where the stakes are highest.\n\nFour kinds of work sit there today, and no amount of prompting reliably fixes them:\n\n- **Deep security and compliance judgment.** Whether you actually meet the rules, not just whether the code runs. **Compliance**, what a law or industry demands of how you store data, is a legal question in technical clothing.\n- **High-stakes architecture at real scale.** Design calls that are cheap now and ruinous to undo once millions of records depend on them.\n- **Hard debugging with real money or data on the line.** A fault in billing or a migration, where one wrong guess corrupts data you cannot get back.\n- **Legal and regulatory calls.** What a contract, a privacy law, or a license actually requires, where a confident wrong answer is your liability.\n\n{/* KEEP: Step = three signs. First is from the debugging chapters (stuck past where the agent helps). The other two matter more: stakes turn into money/user-data/legal, and you cannot judge whether the answer is right so looping cannot help. Real artifact = the \"Call a pro when\" fenced checklist. Cross-reference debugging by topic, not number. */}\n\n## Signs you need a human\n\nOne sign you know from the debugging chapters: you are stuck well past where the agent is still helping. The other two are quieter and matter more.\n\nWhen the stakes become real money, user data, or a legal outcome, your margin for a wrong answer drops to near zero. And when you cannot judge whether an answer is right, running the agent again does not make it right, it only makes you tired.\n\n```\nCall a pro when:\n[ ] The stakes are real money, user data, or a\n    legal outcome, and a wrong call is costly\n[ ] You cannot tell whether the AI's answer is\n    right, so you could not catch it being wrong\n[ ] You are stuck far past where the agent\n    stopped being any help\n[ ] A breach, outage, or bad migration here would\n    hurt real people, not just a side project\n[ ] The choice is cheap to make now and painful\n    or impossible to reverse later\n```\n\n{/* KEEP: Step = hand off the narrow high-risk slice, keep everything around it that you now understand well enough to own and to check the expert's work against. Two-column table (Hand off / Keep) for device variety. */}\n\n## What to hand off\n\nYou do not hand off the whole product, and should not want to. Hand off the narrow, high-risk slice; keep everything around it, which you now understand well enough to own and to check the expert against.\n\n| Hand off | Keep |\n| --- | --- |\n| The security audit before real money moves | The features and screens you built |\n| A schema millions of rows will soon hit | The daily changes on top of it |\n| Billing or migration code that moves data | Running it, watching it, iterating |\n| The legal read on privacy and compliance | Acting on that read once you have it |\n\n{/* KEEP: Step + Example + Action. A good engineer is findable: referral from someone whose software you respect, a vetted network; pick the one who shipped the exact risky thing and explains the tradeoff plainly. Author offers this consulting, ONE plain factual sentence, link /services (verified 200), NOT a hard sell. Rule-of-thumb callout = paying for the risky 20% is the mark of a serious builder, not failure. Prompt = split keep vs hand-off + write a crisp brief for a pro. Close congratulates the whole idea-to-scaling-product journey. */}\n\n## Where to find a good engineer\n\nA good engineer is easier to find than you would think. Ask for a referral from someone whose software you respect, or hire through a vetted network. Pick the one who has shipped the exact risky thing you are handing off, payments, auth, or scale, and can explain the tradeoff in plain words.\n\nThis is also the kind of work I take on: architecture reviews and the high-stakes calls before launch. If it helps to bring in someone who has done it many times, that is what my [services](https://zalt.me/services) are for.\n\n> **Rule of thumb:** paying an expert for the risky 20% is not the moment you failed as a builder, it is the moment you started acting like one.\n\nThis prompt turns your case into a decision and a brief you can hand over:\n\n```\nAct as a senior engineer and technical advisor.\nI am deciding which part of my project to hand to\na human expert and which to keep doing with you.\n\nFrom the situation below, do three things.\n\nFirst, split the work into two lists: the narrow,\nhigh-risk slice a human should own (security,\ncompliance, high-stakes architecture, billing or\nmigration code, anything legal), and the rest I\ncan safely keep running myself.\n\nSecond, for each item in the expert's list, say\nin one line why it is beyond a confident AI\nanswer and what it costs to get wrong.\n\nThird, write a short, professional brief I can\nsend to that expert: what the system does, the\nexact decision or review I need, the stack, the\nconstraints, and how they will know they are\ndone. Keep it tight and free of filler.\n\nMy situation and what feels beyond me:\n```\n\n**Do this now:** paste the prompt, let it split your work into what to keep and what to hand off, and turn the expert list into a brief you can send. You carried an idea from one sentence to a product real people rely on and that grows, nearly all of it with AI beside you. Handing that last hard slice to someone who has done it a hundred times is not where the story ends. It is what a serious builder does to make sure the thing lasts."
          }
        ]
      }
    ]
  },
  {
    "id": "building-ai-agents",
    "title": "Designing Reliable\nAgentic AI Systems",
    "subtitle": "Architecting, orchestrating, and shipping autonomous agents that do real work, not demos.",
    "tagline": "Build scalable AI agents from scratch",
    "description": "Architecting, orchestrating, and shipping autonomous agents that do real work, not demos.",
    "author": "Mahmoud Zalt",
    "authorRole": "Principal AI Architect",
    "status": "coming-soon",
    "url": null,
    "coverImage": "/images-optimized/blog/auto/building-ai-agents-cover-medium.webp",
    "edition": "2027 Edition",
    "version": "v0.0",
    "parts": []
  }
]
