2026-08-07 · ~18 min
Ten Times Zero Is Still Zero
"Vibe coder" gets used as an insult: prompt and pray, ship a house of cards. I claim it anyway, with one word in front: professional. The receipt is the portfolio you're reading this on, where nearly half of everything written is words rather than code.
One of my students? If what you actually want to know is how I feel about you using AI, I wrote that post for you: I Nearly Quit Teaching, So I Automated the Part That Was Killing Me. Read that first; this one is the belief underneath it.
There's a talk version of this, built for a room full of students, and it's live: tjakoen.github.io/talks/ten-times-zero. Same argument, thirty-four slides, and the figures are real rather than pictures of figures. Drag the multiplier to zero, run six sessions with and without the loop and watch the bill arrive, or open a cold session with no brief and see what it does first. Arrow keys move it.
"Vibe coder" is used as an insult
You know the tone. Vibe coder: someone who types a wish into a chat box, pastes whatever comes back, and ships a house of cards they couldn't explain if you held their coffee hostage. Prompt and pray. No tests, no idea, no receipts.
I call myself one anyway. I just add a word in front: professional. And because "professional" is the kind of claim that should show up with evidence and not just vibes, I did the one thing the label almost never does: I counted.
But before the numbers, the belief they're proving. Because the reason I'm allowed to let a machine do the typing fits on a napkin.
AI is a multiplier, not an addend
AI doesn't add to what you can do. It multiplies it. And ten times zero is still zero.
Give a strong developer an AI and you get a frighteningly fast strong developer. Give a beginner the same AI and you get a beginner who ships bugs faster and can't tell which ones they are. Same tool. Wildly different results, because the tool multiplied what was already there.
That's why "just use AI" is terrible advice for a beginner, and "never use AI" is terrible advice for a professional. The honest version is in the middle and it has an order of operations: become worth multiplying first.
Drag it. Same figure as the one in the talk, and the same law: the AI is 10 in every row, and the only thing that changes is what you brought.
Take it to zero. The output does not get smaller, it disappears.
I was the zero, for years
Here's the part I don't usually lead with. For most of my twenties I was a talented amateur with just enough instinct to talk my way into rooms I had no business being in. Coding in school came easy, so I coasted on easy. Then when I decided the real value was in business, I kept running the same play: Say yes to running the thing before I actually knew how to run the thing.
It worked. And that's the trap. It worked just enough to keep me doing it, but never enough to get me what I was actually after. I helped grow my family's little beachside campsite into a real resort, then a pandemic flattened it. I ran marketing for a company until I learned that I can't stand doing marketing for anyone but myself. I started a cafe with friends, a salon with friends, a marketing firm on my own. One broke even, one sold at break-even, one went nowhere, and it turns out you can't tell your friends what to do. Then I built and launched an events platform, got it to a real product with real money moving, and then watched it end badly.
Add it all up and it cost me real money and a few years I can't get back. But through it all, I don't regret a moment of it, because it taught me the one thing every single failure had in common. It wasn't the ideas. It was me. I kept starting things I wasn't the right person to start yet.
You have to be the right person before you try the thing. I paid the tuition to learn that so you don't have to.
So I did the unglamorous thing. I stopped chasing the next launch and started working on the one variable I could actually fix: me. I slowed down. I went back and learned the foundations properly, not just enough to pass, and I shoved myself into the parts I was worst at, like standing in front of a room full of students or doing talks on a stage. That's the actual plan: work my butt off, grind, so that by my thirties I'd get to a point where the work I do will be something I picked, on my terms.
That, to me, is the definition of the word professional. Not the title, but the journey I had to take to be able to write this in the first place.
So we're clear who's talking
I wrote that backstory to buy one sentence: I could have written every line of this project by hand. I run a team of developers for a living, I teach software engineering part time using a platform I built, and I built the thing serving you this very sentence on a framework and stack I built from the ground up.
As a side note: going frameworkless wasn't ignorance of the alternatives. I lived inside frameworks for years, but I walked away for my own projects because I learned that the platform the web is written on grew up while we weren't looking, and to be honest, I'd rather build on the thing than on the thing on top of the thing.
None of that is there to impress you. It's there to establish me as a multiplier and to reinforce that: A beginner vibe codes because they can't write the code. I vibe code because I can. I've learned the typing was never the valuable part. The value is knowing what to build, how it should be shaped, and when the machine has quietly done something dumb, catch it in the act and correct. I've made those same mistakes by hand, expensively, enough times to smell them coming.
So when I hand the typing to a machine, I'm not skipping the hard part. I did the hard part years ago, the boring way and the expensive way.
While I'm being honest: I should probably put in the record that I don't hold a single credential in AI. I stay updated in trends, I follow the right youtubers and journalists. But what I've got instead is a long list of ways I've been wrong, and I personally think that this has taught me more than any syllabus would, I still fully intend to go earn the real thing. But for now we'll just chalk that up as the next foundation I haven't laid yet.
A decade of rehearsal, billed as management
Directing an AI isn't a new skill I had to go learn. It's the skill my whole twenties turned out to be building: the ventures that failed, the team I run, the classroom. I'd been rehearsing for a decade. I just didn't know what for.
Think about what a dev manager actually does: hand work to people whose hands you don't control, give them enough context to succeed, then read what comes back and catch the problems before they ship. A tech lead sets the conventions and keeps a room full of people pulling the same direction. An educator explains a thing clearly enough that someone with zero context can do it, then checks their work and corrects the misunderstanding, not just the wrong answer. Now describe running an AI. It's the same list, top to bottom.
So I stopped treating the model like a magic oracle or a fancy search box, and started treating it like what it actually resembles: a fast, capable, occasionally overconfident junior on my team, or a student rushing through an activity for the sake of getting it done. The instant I framed it that way I got better at it overnight, because I already knew how to do that.
Four jobs, one muscle: give direction, verify anyway, then fix the reasoning instead of the output.
Some evenings I've got five to ten Claude sessions open at once, and it feels exactly like running a team: delegate the work, keep each agent productive, and stitch what comes out into something that works. When I read through what they produce, it's the same muscle I use reviewing a junior's pull request or grading a student's submission. I ask the same questions: "Does this meet the objectives?", "Do they understand what they shipped?", "Is it in line with our established standards and architecture?". My one rule for students turns out to be my one rule for the machine: if you can't explain it, you didn't build it.
I'm not learning a new skill with AI. I'm spending the one I already paid for. The AI is just the newest, fastest, least-tenured member of a team I already knew how to run.
The receipts
Everything below is pulled straight from the git history of the thing you're reading this on: my portfolio, built on three tools I made for it. My own no-build framework (BATCH), my own design system (GRAIN), and a little Markdown CMS that renders these very words into the page (MILL).
Not a demo, not a tutorial repo. The thing I actually build on, and it's live now, so you're reading this on it. Every count below was measured on 19 August 2026 from committed files only, because those are the ones you can go and check. Counting my working tree, where the plans and the agent-facing notes live deliberately uncommitted, gives me a friendlier number. I'm not using it.
The one number that matters
Let me get the headline out of the way, because it reframes everything else:
More than half of this repo is writing, not code. 36,837 lines of words against 34,595 lines of code. Ask a room to guess and they say twenty percent.
Sit with that for a second, because it's the opposite of what vibe coding is supposed to look like. The caricature is a person who writes no docs, who couldn't produce a spec if you begged. This repo has the inverse problem: there's about as much specification as there is implementation.
I didn't tidy that up after the fact. It's the method. When you work with an AI at speed, the scarce resource stops being code and becomes intent. The model can't know what you meant, what the rules are, or what "done" looks like, unless you wrote it down. So I do. The documentation is the work, the code is the cheap part the machine handles.
About as much spec as implementation: the inverse of what vibe coding is supposed to look like.
One honest wrinkle, since I keep the receipts either way. When I first published this the same count read 56%, and the writing was ahead. It has since evened out, for a boring reason: the stack moved into its own repos and took a big slice of its own documentation with it, while the site's code kept growing. Across all seven repositories I work in, one line in three is still documentation. The ratio moved. The habit didn't.
Now the "vibe" part, because I do go fast
If the docs number makes me sound slow and fussy, the tempo says otherwise. Here's the shape of the build:
- 503 commits on this repo alone, and every single one of them was co-authored with an AI. That's not the machine writing while I watched, I still write plenty of the code myself. Co-authored is the honest word: two sets of hands, on every commit.
- Across the seven repositories I keep, 1,246 commits between 27 June and 22 August. Eight weeks.
- One night in the middle of that, 4 July: 50 commits between 9pm and half past three, 4,269 lines added before it got light. One every eight minutes. That's vibe coding at full tilt, the machine keeping up with the ideas as fast as I could aim them.
- Small commits, on purpose. Each one is a thing that already works, which is what makes the next one cheap to check. Fifty tiny commits you can verify beat five big ones you can't.
- 88,018 lines added to this repo since the first one. Real output.
Both things are true at once, and that's the entire point: fast and documented. The reason I can sprint for ten hours and not wake up to a pile of nonsense isn't that I'm careful instead of fast. It's that I did the careful part first: I built the rails before I opened the throttle.
Six and a half hours, fifty commits: fast is safe when the rails went up first.
The part that actually makes it professional
The distinction that matters has nothing to do with whether you use AI or how fast you type. Amateur vibe coding is unstructured improvisation. Professional vibe coding is structured delegation. Same tools, same speed, wildly different amounts of scaffolding around the model.
And the clearest way to see it is to split the job in two:
The AI can generate a thousand lines an hour. It cannot decide the layering rule, the token architecture, the single write door, or what "done" means. Those were mine. The machine typed; I engineered.
The taste, the architecture, the judgment about what's worth building and what's a trap: that's the human. I don't make the AI reliable by writing cleverer one-off prompts. I make it reliable by building it an environment where good work is the path of least resistance.
That environment is a system, and it's copyable.
The playbook
1. Write the rails before the features. Before I built much of anything, I wrote the rulebook. The repo has a conventions doc, the build standard: layering rules, how components are structured, the testing bar. And an onboarding doc written for the AI, so any model (or human) joining the project reads the same "here's how this works, here's what not to touch" before it writes a line. The AI goes fast because the guardrails are already up. You don't tell a fast driver to be careful; you build the track.
2. Give the AI a memory. The model forgets everything between sessions, so I keep a running set of decision notes, twenty-odd small records of why things are the way they are. We went through the single write door because of this. The theme is decoupled because of that. Next session the AI inherits the reasoning instead of relitigating it, or worse, quietly undoing it. A professional keeps a paper trail. So does my repo.
3. Make change propagate: keep a sync map. The failure mode of fast AI work isn't bad code, it's drift: you change one thing and six related things silently fall out of step. So I keep an explicit "when you change this, also update that" table in the onboarding doc. Touch an action verb? Here are the five places that have to move with it. It turns "don't forget" into a checklist a machine can actually follow.
4. Audit relentlessly. Never assume the AI knew what it was doing. You review a junior's pull request for a reason, and a model earns more of that scrutiny, not less, because it's fluent enough to sound right exactly when it's wrong.
Most of my prompting isn't "build this." It's "check this."
Does it still match the goal? Does it honor the docs? Did it quietly break a rule to get something working? That's where most of the hours actually go: not building, checking. Then before the big commits there's a repeatable runbook: tests green? Layers still importing in one direction only? Any hardcoded values that should be design tokens? Going fast is only safe when there's a cheap, repeatable way to prove you didn't bend the shape of the thing.
What that adds up to, counted the same day as everything else here: 184 test files across the seven repositories, roughly 2,159 test cases behind them, and 44 end-to-end specs on this site alone, all of it behind one command that has to come back green before anything counts as done. There's even a linter for the prose, which sounds absurd until you remember that half of what's in here is prose. If the writing is part of the product, the writing gets a gate.
One rule I'd hand a student before any of the tooling: the second pass has to be done by someone who didn't write the code. A session that just wrote something already believes it, and will defend it convincingly. A cold session won't. That isn't a clever trick, it's the same reason you don't mark your own exam.
5. Tests are part of the work, not a phase after it. Three tiers (unit, integration, end-to-end) written as features, not bolted on later. The type-checker and the test suite have to be green before I call anything done. It's the least glamorous rule and it does the most work: it's the seatbelt that lets me drive fast.
6. Document for the machine, on purpose. Underneath the other five is one mindset: the AI is a first-class reader, so I write for it, not around it. It reads the same onboarding and conventions a new teammate would. And I've started taking that down to the component level in my design system, where the pieces the AI actually operates get a second doc written only for the machine: how to drive the thing, not how a human reads about it. You onboard this collaborator every time you spin up a new session, so you build the onboarding, make it excellent, and polish it as you go.
The loop is what keeps fast from turning into fragile.
Two of those rules have proper names, and I didn't invent either
Worth saying plainly, because I read my way here and you can too.
Context engineering is the one everybody gets wrong first. Your prompt is not the input. The model reads the room: the files it was pointed at, the conventions it found, whatever the last session left behind. It arrives with no memory of yesterday and none of the things somebody told you in person. So the instinct to fix a bad answer by writing a cleverer prompt is the amateur move, and I made it for months before the penny dropped. I stopped prompting harder and started leaving better notes.
There's a free test for this and it takes two minutes. Open a cold session, no chat history, give it one sentence of instruction, and watch what it does first. If it guesses the stack, invents a folder, or writes a framework into a project that has never had one, that gap is yours and not the model's. Alfonso Graziano's Learning AI-Native Software Engineering is where most of my thinking on this got sharpened, along with spec-driven work like GitHub's Spec Kit.
Loop engineering is the other one, and it's Addy Osmani's term and framing: durable AI work is built out of a few reusable parts rather than one clever prompt. His Beyond Vibe Coding carries the harder half of it, the seventy percent problem, where the machine gets you most of the way and the last stretch is exactly where unmanaged work rots.
The shape I run per change is four moves. Brief it, which is what to build and what not to touch. Build, where the machine types and I read every line. Verify, and that word is doing all the work here. Then record what changed, where the next session will find it.
Verify is not a feeling. It's the type checker, the tests, an end-to-end run against the real thing, and a pair of eyes that isn't mine. Skip it once and nothing happens, which is the trap. Skip it for a week and you can't answer a single question about your own repository, and neither can the machine, because nothing you agreed five sessions ago survived anywhere it could read.
The best trick of all: the AI's mistakes are a measurement
Here's the move I'm proudest of. While I was building one of GRAIN's showcase pages, I kept making small mistakes: the grain texture not showing on the AI's text, a "chat" that rendered as two stacked boxes instead of a conversation, an AI "Send" click that didn't actually look like a click. Normal bug-fixing territory. The boring move is to squash each one and move on.
Instead we stopped, the machine and I, and started asking questions: why do I keep tripping on my own system? Am I the problem? Is the architecture I planned out the problem? I am designing my framework in a way that's supposed to be easy for a human to use. Much less an AI. So why is it tripping up on trivial things?
It's the same reflex I use giving feedback on a code review or grading a student's project: when the same mistake keeps coming back, you stop blaming the person making it and start suspecting the thing they're using. When half a class flubs the same question, the problem is the question, not the class.
If the person who designed it keeps slipping, the architecture isn't clear, and it's meant to be easy for a human, never mind an AI.
So the mistakes stopped being bugs and started being data.
We ran a proper review, and the mistakes weren't random. Every one clustered in a single failure mode: silent-failure contracts. Mechanisms that quietly do nothing when you use them slightly wrong: no error, no warning, the documented way just doesn't take. That's the most dangerous kind of design there is, especially for a system whose whole pitch is "legible to a human and an AI." A thing that fails loudly, you fix. A thing that fails silently, you ship.
The AI surfaced it in a way I couldn't have alone, because an AI working from your docs is the ultimate stress-test of clarity. It has no tribal knowledge, no "oh, everyone knows you do it this way." So when it trips on your system, that isn't the machine being dumb; it's a free audit telling you a tired human on a Friday will trip on the exact same spot. Read the signal instead of just correcting the output.
So we didn't document around the traps or bolt on a test to catch them later. We designed them out: made the grade work on any element, shipped the chat as a container so messages can't misalign. "Can't be done wrong" beats "we'll catch it if it is." Then we backstopped it with tests that assert the design system is used correctly, not merely that the page renders, including one that proves a human action and an AI action produce the exact same result. Those tests are executable contracts: the next person, human or AI, gets caught the moment they misuse the thing, long after I've left the conversation. And every fix got proven against reality: computed styles, geometry, screenshots, real end-to-end runs, never "it probably looks right."
The one-sentence version: where the AI keeps slipping is a map of where my design isn't clear yet, and I harden the design so neither of us slips there again.
Every trip the AI takes is a map of where the design isn't clear yet, and a chance to harden it.
The tell is the ratio
Strip all of it down and you land right back at that first number. 52% writing. About as much spec as implementation, in a repo whose whole reputation is supposed to be the opposite.
An amateur and I could type the exact same request into the exact same model and get back the exact same code. The difference isn't in that moment. It's in everything I built around the moment: the conventions the model read first, the memory it inherited, the tests it has to pass, the audit it has to survive. That surrounding structure is the profession. The code is just the code.
And it's the same law as the napkin at the top: the model multiplied what I brought. I brought conventions, memory, tests, and taste, so it multiplied those.
Nothing in the middle row is better. The whole difference is what got built around it. Play with this one.
Don't work for the AI. Run it.
If you take one idea out of all of this, take this one, because it decides which of the two developers you become.
Working for it looks like this. You ask, it answers, you paste. The shape of the project ends up being whatever the machine happened to produce that afternoon. When something goes wrong you ask it again, louder. And you spend a lot of your day sitting in front of one chat window, waiting.
Running it looks different from the first minute, before a single prompt gets typed. You decide what should exist. You cut it into pieces small enough to check in a minute each. You point the machine at one piece and read every line that comes back. And you accept nothing you couldn't defend with the tab closed.
Same tool, same day, same model. One of those is a job. The other is a habit that does not survive contact with anything real. Some evenings I have several sessions open at once and it feels exactly like running a team, because it is the same skill: brief, delegate, verify, and never sign off on work you can't explain.
Where to start, if you're early
Concrete, and in this order, because everybody wants to start at three.
1. Pick a project you already understand. Not a new one. You can't judge the output of a machine on a codebase you've never read, and judging the output is the entire skill.
2. Write the brief before the feature. One file at the root: what this is, how it's built, what not to touch. Twenty minutes, once. Every session after it starts smarter, and you'll feel it on day two.
3. Turn on the cheap gates first. Types on and strict, one test that actually runs, no empty catch blocks. All three before lunch, and they'll keep catching things all year.
4. Make it explain, then explain it back. Out loud, tab closed, to nobody. The moment you can't, stop. You've just found the exact thing to go and learn, and it's worth more than the feature was.
5. Every correction you give twice becomes a line in that file. That's how the brief writes itself, and it's the whole method minus my tooling. The second time you correct a machine about the same thing, that correction has stopped being a conversation and started being a rule.
And then the part most people skip: record it. In your docs, in your audit, in the file the model reads first. Let it learn the same lesson you just did, so tomorrow you get to hold it to that lesson instead of teaching it again.
If you're early in your career and using AI to produce, you're buying output. If you're using it to learn, you're buying reps. Same hour, and only one of those makes you a bigger number.
The point
So yes: I'm a vibe coder. I let the machine write nearly everything, I work in fast overnight bursts, and I'll tell anyone who asks that this is the most productive I've ever been. But I can also hand you the receipt (the commits, the ratio, the runbook) and show you it wasn't luck.
That's the whole word. Professional. It just means I keep the receipts. Get so good at the fundamentals that when you finally pick up the power tool, it multiplies something real. It's the same discipline I drill into my students when I teach, and the same rule I turn on my own AI when it grades their work: the console I built to stop drowning.
Bring nothing and it multiplies nothing: ten times zero is still zero. So become a bigger number first, then go make it dangerous.
I don't prompt and pray. I prompt and prove.
What I read to get here
None of the ideas underneath this are mine. I read these, argued with them, and built something that runs, which is a method you can copy today.
- Loop Engineering, Addy Osmani. The case that durable AI work is built from a few reusable parts rather than a clever prompt, plus the warning about comprehension debt that I wish I'd read two years earlier.
- Beyond Vibe Coding, Addy Osmani. The seventy percent problem, plan first over prompt and pray, and quality gates as non-negotiable. This one is why my session heartbeat and my gates exist at all.
- Learning AI-Native Software Engineering, Alfonso Graziano. Context engineering, spec-driven development, verification gates.
- addyosmani/agent-skills. The loop in executable form, as skills an agent loads on its own. Worth reading even if you never run them.
- GitHub Spec Kit. Spec-driven development, formalized and free. The most direct way to try "write the rails first" without inventing your own process.
- llms.txt and MCP. How a project tells an agent what to read first, and how an agent reaches your tools. An evening each.
- My own standards, which are the written version of everything above as I actually run it. Take what's useful and ignore the rest.
The judgment is human. The typing, by design, is not. On this one, nearly all of it.