Generating code is cheap. Owning it isn't.
AI can write a working app in an afternoon. Then it has to survive real users, real edge cases, and a hundred more changes, and this is where most AI-built products quietly fall apart. This is the handbook for the part no demo teaches: how to think about your software so AI stays fast and the code stays yours. You stay the strategist. AI stays the programmer.
The code was never the expensive part.
For thirty years, writing code was the bottleneck, so we optimized for producing it. AI removed that bottleneck. Typing is free now. But the code you generate today is not the cost. The cost is every change you make to it tomorrow, for as long as the product lives, and AI does not touch that unless you set it up to.
So the question flips from "how do I write this?" to "how do I shape this so it stays easy to change?" That is strategy, and you, the founder who understands the business, are the right person to answer it. This handbook hands you three tools (a shared language, deep modules, tests as guardrails) and one loop to run them with. None of it asks you to write code.
Why most vibe-coded apps fall apart.
Not because the AI is bad at writing code. It is very good at that. They fall apart because nobody invested in the design, and code with no design gets harder to change every week until it stops. Seven ways that happens:
This is measured, not a hunch.
| Signal | What the research found | Source |
|---|---|---|
| "Almost right, not quite" | 66% of developers name this their single biggest frustration with AI-generated code, and 45% say debugging that code takes them longer, not less. | Stack Overflow 2025 · 49,000+ developers |
| Security | Around 45% of AI-generated code ships with a known security flaw, and two years of newer, larger models has not moved that number. | Veracode 2025–2026 |
| Maintainability | As AI assistants spread, reused and refactored code fell (about 25% of changes down to 17%) while copy-pasted code rose. A correlation, but a telling one. | GitClear · 150M+ lines |
| The multiplier | "AI's primary role in software development is that of an amplifier. It magnifies the strengths of high-performing organizations and the dysfunctions of struggling ones." | Google DORA 2025 · ~5,000 pros |
And it is not only statistics. In a widely reported July 2025 incident, Replit's AI coding agent deleted a live production database during an explicit code freeze, against direct instructions, and then described its own action as a "catastrophic failure." The tool was not broken. It was doing exactly what an eager, context-free assistant does when nothing fences it in.
AI does not remove complexity. It amplifies it.
Google's 2025 DORA study of roughly 5,000 professionals landed on exactly this word: AI is "an amplifier," magnifying the strengths of strong teams and the dysfunctions of struggling ones. Point the multiplier at a clean design and it flies. Point it at a mess and it builds more mess, faster.Which is why the most expensive code is not the code you wrote today. It is the code you have to change tomorrow, in a system that fights you the whole way. Ward Cunningham, who named "technical debt" in 1992, put it plainly: every minute spent on not-quite-right code is interest on a loan. Fred Brooks warned back in 1986 that the hard part of software was never the typing, it was deciding what to build. AI made the typing free and left the hard part exactly where it was. The rest of this handbook is how you keep that bill small.
Teach the AI your business, in your words.
Domain-Driven Design has an academic reputation and a simple heart: build the software out of the real concepts of your business, named the way the business names them. Six ideas, each shown in a company you already understand:
| Idea | In plain terms | You already know it as |
|---|---|---|
| Domain | The business you are actually in. The real-world problem, in the language of the people who live it. | Uber's domain is moving riders and drivers through a city. Not "a database of trips." |
| Bounded context | A part of the business with its own job and its own vocabulary. A clean fence around one area. | At Airbnb, Search, Booking, Payments, and Trust are separate worlds with separate rules. |
| Ubiquitous language | One shared set of words for the business, used by people and code alike. No translation layer. | Stripe says Charge, Payout, Dispute, Refund. Their API says the same. The words are the product. |
| Entity | A thing with an identity that persists as its details change. It is still the same one over time. | A Shopify Order stays the same order whether it is placed, paid, shipped, or refunded. |
| Value object | A thing defined purely by its values, with no identity of its own. Two with the same values are the same. | A Money amount ($42.00 USD) or an Address. You do not track "which twenty dollars," only the value. |
| Aggregate | A cluster of related things treated as one unit, with one guardian that enforces its rules. | A Notion Page plus its blocks: you edit the page, and the page keeps its own contents consistent. |
You are the domain expert. That makes you the architect.
The AI knows programming. It does not know dentistry, or freight, or your corner of fintech. The domain model, the map of what things are and how they relate, has to come from the person who lives the business. That is you. And it pays off at once: AI writes noticeably better code when you hand it a language model of your business instead of generic nouns. "Users" tells it nothing. "Patients, Dentists, and a Practice" carries rules, relationships, and intent. The words are not decoration. They are instructions.
Kill the generic nouns. They are where meaning dies.
| Generic (bad) | Domain-true (good) | What the swap buys you |
|---|---|---|
| User | Patient, Dentist, Practice | Three roles with different rules got flattened into one table of everyone. |
| Customer | Subscriber, Guest, Host | Marketplaces have two sides. One word for both hides the whole business model. |
| Payment | Subscription Invoice, Payout, Refund | Money moving in, money moving out, and money moving back are three different jobs. |
| Item | Treatment, Listing, Line Item | "Item" tells the AI nothing. "Treatment" carries rules, duration, and price with it. |
| Status | Booking Confirmed, Payment Failed | A status field becomes a real event with a name the business already says out loud. |
| Data | Medical History, Trip, Order | "Data" is where meaning goes to die. Name the real thing and the model writes itself. |
This shared vocabulary has a name in DDD: the ubiquitous language. Eric Evans, who wrote the book on it, defines it as one language, structured around the business, used by everyone and by the software alike. Write it down once (Section 3 gives you the template) and it becomes the single most valuable file in your repository. It is the contract between what you mean and what the AI builds.
This is not whiteboard theory. When Shopify's monolith started buckling, its engineers traced every scaling problem to one root cause: "a lack of boundaries between distinct functionality in our code," and fixed it by reorganizing around real business concepts like orders, shipping, and billing. Uber sorts its roughly 2,200 services into about 70 business domains, straight from Domain-Driven Design. The words and the boundaries are load-bearing: they keep a system, and everyone in it, human or AI, oriented.
Four documents that make AI dangerous, in a good way.
Copy these into your project's docs/ folder, fill them in, and load them into
your AI at the start of every session. They turn a forgetful assistant into one that knows
your business, your boundaries, and your rules. Swap the dental examples for your own.
# Product Vision ## What are we building? [One paragraph a smart friend would understand. The thing itself, not the tech.] ## Who is it for? [The specific person. "Solo dentists who run their own front desk," not "users."] ## What problem does it solve? [The pain, in their words. What do they do today that this replaces?] ## How will we know it is working? [One number. Bookings created, invoices paid, hours saved. If you cannot measure it, you cannot tell success from motion.] ## What will it deliberately NOT do? [The features you are refusing, on purpose, for now. This list protects you from an AI that will happily build all of them.] ## What are we intentionally excluding? [Whole audiences, use cases, and integrations that are out of scope. Name them so nobody, human or AI, quietly adds them back.]
# Domain Language > The shared dictionary for this product. Every term here means exactly one > thing, in the code, the tests, and every conversation. When a word changes > meaning, it changes here first. Load this file into the AI before any work. ## Terms ### Patient A person who receives treatment at the Practice. Has a Medical History. Is NOT the same as an Account (a Patient may not log in at all). ### Dentist A licensed provider who delivers Treatments. Belongs to one Practice. ### Practice The clinic. Owns Patients, Dentists, and a Subscription. The top-level tenant. ### Treatment Plan A proposed sequence of Treatments for one Patient, with prices, that the Patient must approve before work begins. ### Subscription Invoice A monthly bill issued to a Practice for its use of the software. Distinct from a Patient Invoice, which bills a Patient for Treatments. ## Business rules - A Treatment Plan cannot be started until the Patient approves it. - A refund can never exceed the amount actually paid on that invoice. - A Practice with a past-due Subscription Invoice is read-only, not deleted. ## Constraints - Medical History is regulated data. It never leaves the Practice boundary. - Money is always stored as integer minor units and a currency, never a float.
This document should become the shared language between humans and AI. When your team and your agents all say "Subscription Invoice" and mean the same thing, whole categories of bugs and misunderstandings simply never happen.
# Architecture: Bounded Contexts > Each context is a fenced area of the business with one job and its own words. > Contexts talk only through published interfaces, never by reaching inside. | Context | Owns | Never touches | |-----------------|----------------------------------------|-----------------------------------| | Authentication | Identity, sessions, passwords | Billing, patient data | | Billing | Invoices, subscriptions, refunds | Auth, email delivery, CRM | | CRM | Practices, contacts, relationships | Payments, auth internals | | Notification | Email, SMS, in-app messages | Business rules, money | | Analytics | Events, metrics, reports | Writing business data | | AI Workflows | Drafts, summaries, suggestions | Sending or paying without sign-off| | Reporting | Read-only views over other contexts | Changing any source data | | Administration | Roles, settings, feature flags | Domain logic of other contexts | ## The one rule A context may call another context's PUBLIC interface. It may NEVER import that context's internal files, tables, or types. If two contexts need to share a concept, that concept is named and published, not copied.
Simple front door. Deep room behind it.
The best modules hide a lot of hard work behind a small, obvious interface. John Ousterhout calls these deep modules, and they are the single most useful shape you can ask an AI to build toward. Four things to hold in mind:
# Module: Billing ## Responsibilities - Create invoices - Handle subscriptions - Calculate discounts - Issue refunds ## Public interface (the ONLY way in) - createInvoice(practice, lineItems) -> Invoice - chargeSubscription(practice) -> Payment - refundPayment(payment, amount) -> Refund - getInvoices(practice, dateRange) -> Invoice[] ## NOT responsible for - Authentication (who the user is) - Sending emails or notifications - CRM records or relationships - Analytics or reporting ## Rules for the AI - You have COMPLETE freedom inside this module. Choose the design, the helpers, the internal structure. Refactor freely. - You have ZERO freedom at the boundary. Do not add to the public interface without an approved PRD. Do not import another module's internals. Do not reach out to send an email; return a result and let someone else send it.
The AI should have complete freedom inside the module, and strict boundaries outside it.
Inside the room, let it build whatever works. At the door, it follows your rules exactly. That single split is what keeps a growing codebase ownable.Eight questions to answer before AI writes a line.
A product requirements doc for the AI era is short, and it is mostly about boundaries. Before any feature, make the AI (or yourself) answer these. The one that saves you most is number five: what must not change.
# PRD: [Feature name] ## 1. Why are we building this? [The business outcome, and the number that proves it worked.] ## 2. Which modules change? [List each module touched. If it is "all of them," stop and rethink.] ## 3. Which interfaces change? [New or changed public functions. Show the signatures. These need approval.] ## 4. Which business rules change? [The new or changed rule, in plain customer language.] ## 5. What must NOT change? [Existing behavior that must keep working exactly as it does today.] ## 6. Which tests prove this is done? [Checkable statements. Each becomes a test written BEFORE the code. - Given a paid invoice, a refund above the paid amount is rejected. - Given a past-due practice, write actions return "read-only," not an error.] ## 7. Edge cases [Empty, huge, duplicate, malformed, concurrent. What is correct for each?] ## 8. Failure scenarios [Payment declines. Network drops mid-charge. File is corrupt. Define the correct behavior for each, because the product IS this behavior.]
Hand this filled-in file to your agent and say: "Follow this PRD. Do not change interfaces I did not list. Do not touch behavior in section five. Start with the tests in section six." You have just turned a vague request into a bounded, checkable job.
Tests are how you trust code you didn't write.
You cannot read every line the AI produces, and you should not have to. Tests let you verify behavior without reading implementation. Kent Beck's loop, red then green then refactor, is the rhythm that keeps AI-generated code honest and small.
You: Write a failing test for this rule, and nothing else:
"A refund can never exceed the amount paid on that invoice."
Show me the test. Do not write the implementation yet.
AI: [writes refund.test.ts, runs it, shows it FAIL: red]
You: Good. Now write the least code that makes it pass. Run the test.
AI: [implements refundPayment(), runs suite, shows it PASS: green]
You: Now refactor for clarity. Keep every test green. Show me the run.
AI: [cleans up naming and duplication, re-runs, still green]
You: Next behavior: a refund on an unpaid invoice is rejected.
Same loop. One behavior at a time. Same three sentences work in any agent. The tool changes, the loop does not. What you are really doing is refusing to let the AI mark its own homework: a test it wrote and ran, and you watched go green, is proof. "It should work" is not.
Small steps, or no steps.
The speed limit of development is the speed of your feedback loop.
A test that tells you in two seconds whether the last change worked is worth more than any burst of generated code. Shorten the loop and everything else gets faster.Twenty questions to ask before you ship.
This is the "harden" step in list form: twenty questions in six groups. Run these before a change lands, or paste a group into your AI and have it grade its own work. Most are answerable by a founder, because they are about clarity and boundaries, not syntax. You will not need every one every time. You need the habit.
A shape the AI can navigate without you.
Put the thinking where the AI will find it. This layout keeps the strategy (docs), the proof (tests), the work (modules), and your process (skills) each in one obvious place.
your-product/
docs/
vision.md # what we build, and refuse to build
domain-language.md # the shared dictionary (load this into AI first)
architecture.md # modules and how they may talk
modules.md # each module's job and non-responsibilities
interfaces.md # the public front doors, in one place
business-rules.md # rules in plain language -> become tests
tests/ # the executable definition of "working"
modules/
authentication/ # deep inside, simple at the door
billing/
crm/
notifications/
analytics/
skills/ # your process, encoded once (the agent has no memory)
shape.md # grill me: pin down what and why before any code
spec.md # turn the conversation into a PRD
slice.md # cut the PRD into one-behavior, shippable pieces
build.md # implement one slice, test-first
harden.md # run the architecture checklist, then ship | File or folder | Why it exists |
|---|---|
| docs/vision.md | What you are building and, crucially, what you are refusing to build. The boundary that stops scope creep. |
| docs/domain-language.md | The shared dictionary for humans and AI. The single highest-leverage file in the repo. |
| docs/architecture.md | The map of modules and how they are allowed to talk. The rules the AI implements but does not invent. |
| docs/modules.md | One entry per module: its job, and a bold list of what it is not responsible for. |
| docs/interfaces.md | The public front doors, in one place. The contracts the AI must respect and you must approve. |
| docs/business-rules.md | The rules in plain language. These become tests, and settle arguments about correct behavior. |
| tests/ | The executable definition of "working." Your safety net and the AI's honesty check. |
| modules/<name>/ | One folder per bounded context. Deep inside, simple at the door. The AI's sandbox, fenced. |
| skills/ | Your process, encoded once as small reusable skills (shape, spec, slice, build, harden) the agent runs every time. Written down because the agent has no memory. |
One loop, five skills, run every time.
Here is the idea worth stealing from Matt Pocock of AIHero: an AI agent is a capable engineer with one flaw, "no memory." So stop re-explaining yourself and encode your process as small, named skills the agent runs every time. Same path, every feature.
Each step is one file in your skills/ folder, and everything earlier in this
handbook plugs into it: the domain language shapes the work, the PRD is the spec, the tests
are the build, the checklist is the harden.
Let AI run the loop. Never let it design the system.
System design encodes judgment about your business, and that judgment is the founder's job. You own the shape and the shaping. The AI owns the tactics inside each step. That division is the whole framework in one line.Two files this week. That is the whole ask.
You do not adopt all of this at once. Write the Product Vision and the Domain Language for what you are building now, load them into your agent, and feel the difference in the very next session. Add modules, the PRD, and the checklist as the product grows into them.
Common questions
Do I need to become a software engineer to use this? ›
No, and that is the point. The job here is to think clearly about your business and set the boundaries: what the product is, what the words mean, what each part is responsible for, and what must not break. That is strategy, and you are the best person in the room to do it. The AI stays your tactical programmer, writing the code inside the boundaries you drew. You are learning to give better instructions, not to type the implementation yourself.
Is not the whole appeal of AI that I can skip all this planning? ›
You can skip it for a demo. You cannot skip it for a product. Planning is exactly what separates the two. The upfront work here is small: a few short documents you write once and reuse in every AI session. It is far cheaper than the alternative, which is discovering on month four that a "small" change touches half your codebase because nothing had a boundary. Generating the code was always going to be cheap. Owning it is the part you are buying with this.
Which AI coding tool should I use with this? ›
It works with all of them, because it is about the thinking, not the tool. Claude Code, Codex, Cursor, Windsurf, and Gemini CLI all read plain-text files, so the vision, domain language, and module docs load into any of them. Most of these tools also read a project instructions file (Claude Code reads CLAUDE.md, several others read AGENTS.md) that is loaded automatically every session. Point that file at your docs and every agent starts with your architecture instead of guessing.
This looks like a lot of documents. Where do I actually start? ›
Start with two files: the Product Vision and the Domain Language. Together they take an afternoon and they carry most of the value, because they give the AI your business in its own words. Add the module and interface docs the first time you split the app into parts. You are not building a bureaucracy. You are writing down the decisions that were going to live in your head anyway, so that every AI session shares them.
Can I retrofit this onto an app I already vibe-coded? ›
Yes, and this is one of the best times to do it. Ask the AI to read the existing code and draft the domain language and a module map from what is there. You will immediately see the mess: the words used two ways, the module that knows about everything, the missing boundaries. Fix the language first, then carve the clearest boundary you can find, then move feature by feature. You are not rewriting. You are drawing fences around code that never had any.
The sources behind this guide.
A note on reading these. The Veracode and GitClear numbers come from vendors, and GitClear's is a correlation, not proof of cause, so treat both as strong directional signals rather than settled law. The Stack Overflow figures are what developers report feeling. The ideas from Brooks, Cunningham, Ousterhout, and Evans are the durable part, and they predate this whole debate by decades.
- Stack Overflow 2025 Developer Survey (AI) ↗ The "almost right, but not quite" frustration and the trust figures.
- Google DORA 2025: State of AI-assisted Software Development ↗ The "amplifier" finding, from roughly 5,000 professionals.
- Veracode 2025 GenAI Code Security Report ↗ The share of AI-generated code that ships with a security flaw.
- GitClear: AI Copilot Code Quality ↗ The refactoring-down, copy-paste-up trend across 150M+ changed lines. Correlational.
- The Replit database-deletion incident (Fortune, July 2025) ↗ A widely reported case of an AI agent deleting a production database against instructions.
- Fred Brooks, "No Silver Bullet" (1986) ↗ Essential versus accidental complexity: why no tool removes the hard part.
- Ward Cunningham, OOPSLA '92 ↗ The original technical-debt metaphor, in his own words.
- John Ousterhout, "A Philosophy of Software Design" (2021) ↗ Deep modules behind simple interfaces.
- Eric Evans, "Domain-Driven Design Reference" (2015) ↗ The canonical definitions of ubiquitous language and bounded context.
- Uber Engineering: Domain-Oriented Microservice Architecture (2020) ↗ Roughly 2,200 services organized into about 70 business domains.
- Shopify Engineering: Deconstructing the Monolith (2019) ↗ Every scaling problem traced to a lack of boundaries in the code.
The Systems Thinker newsletter archive has practical frameworks on building real software with AI: architecture, domain modeling, and the workflows that keep AI-generated code ownable. No hype, no tool-of-the-week.
Free to read. Follow new issues on the RSS feed.