Not "AI-assisted." Not "copilot suggestions I edited." I made a rule: for 30 days I don't type application code. The AI does. My job is to describe, review, and approve.
I shipped a real product this way — a small SaaS with auth, Stripe billing, a dashboard, and a public API. It works. It's in production. And along the way I found out exactly where the "AI writes everything now" dream holds up and where it quietly falls apart.
This isn't a hype post and it isn't a doom post. It's a field report.
The rules
To keep myself honest:
- I don't write application logic by hand. I prompt, the AI writes.
- I can read every line, reject it, and ask again — but I can't fix it myself in the editor.
- Config, secrets, and "click the button in the dashboard" steps are mine (the AI can't do those anyway).
- If I get truly stuck for more than an hour, I log it as a "break" and write the code myself.
By the end I had 9 logged breaks. Those breaks are the interesting part.
What worked shockingly well
Let me be fair to the machine first, because a lot of this genuinely surprised me.
Greenfield scaffolding is basically solved. "Set up a Next.js app with Postgres, Drizzle, and auth" — done, correctly, in one shot. The first 40% of the project flew by faster than I've ever built anything.
Boilerplate it never gets wrong. CRUD endpoints, form validation, Zod schemas, a table with sorting and pagination. The stuff I hate writing, produced perfectly, every time, no notes.
It's a phenomenal rubber duck. "Why is this query slow?" got me a better answer than I'd have reasoned to alone — it spotted a missing index and an N+1 in the same breath.
For the first week I genuinely thought I'd write the "developers are obsolete" post. Then week two happened.
What broke — the 9 breaks, honestly
Break 1–3: It can't hold the whole system in its head
Brilliant at the file in front of it, blind to the one three folders over. It wrote a second formatCurrency helper because it didn't know the first existed. Re-implemented my auth check inline instead of the middleware I already had. Introduced a subtly different User type in a new module.
None of these are "bugs" — everything compiled, everything passed. They're architecture drift, and architecture drift is invisible until the day it costs you a week.
The AI optimizes locally. Coherence across a system is still a human job.
Break 4–5: It confidently writes plausible, wrong code
The scariest failures weren't crashes. They were code that looked right and ran fine on the happy path.
The billing webhook it wrote acknowledged Stripe events before persisting them. Flawless in testing. In production, one DB blip = a paying customer locked out, no record, no trail. I only caught it because I've been burned by exactly this before. A junior copying this output would not have caught it. That's the part that keeps me up.
Break 6: Debugging its own code is a hall of mirrors
When something broke that the AI couldn't see, asking it to fix it produced changes, not fixes. It would rewrite the function, swear the bug was gone, and reintroduce it two prompts later. Without dropping into the debugger myself to understand the real state, we'd have spun forever. Biggest time sink of the month.
Break 7–8: Taste, and saying "no, less"
Asked for a settings page, it gave me 14 options nobody wanted. Asked for error handling, it wrapped everything in try/catch and swallowed the errors. The AI's instinct is always to add. Knowing what to leave out — the product-design part of engineering — it has none.
Break 9: The last 10% is still 90% of the work
"Demo works" took a week. "Handles a real user doing something weird at 2am" took the other three. Edge cases, race conditions, the empty state, the error state, the double-click state — the AI does none of it unless you know to ask, and knowing to ask is the job.
The uncomfortable synthesis
AI didn't replace me. It replaced the *tasks I used to give juniors.* The scaffolding, the boilerplate, the first draft — the exact work a junior becomes a senior by doing. So if the AI does all the entry-level work, where does the next senior come from? You can't skip the 10,000 hours; you can only move where they're spent.
The skills that mattered every single day were senior ones: knowing the code was subtly wrong, knowing what not to build, holding the whole system in my head to catch the drift. AI didn't flatten the hierarchy — it made the top more valuable and sawed off the ladder to get there.
What I actually changed
I didn't stop using AI — I use it for 100% of the typing now and always will. I changed the guardrails, and every one maps to a break above:
- The thing that writes the code is never the thing that reviews it. A separate reviewer, prompted to refute the diff, catches the plausible-but-wrong code the author always waves through.
- Nothing merges without a human who can see the blast radius the model can't.
- "It compiles and the tests pass" is the start of review, not the end — the model writes tests that agree with its own wrong mental model.
Author here, skeptic there, human on the merge button — that separation is the whole reason I could hand the typing to a machine and still sleep. It's also exactly how we build xenition: agents that do the work, a different agent that tries to tear it down, and a person who owns the decision.
Would I do it again?
For a prototype? In a heartbeat — I'd never scaffold by hand again. For production? AI for 100% of the typing and 0% of the thinking. The typing was never the hard part. It just felt like it was.
Top comments (7)
Breaks 4 and 5 are the ones I keep coming back to, because the Stripe webhook acknowledging events before persisting them is exactly the kind of bug that looks perfect in a demo. I had the same reaction to the second formatCurrency — nothing failed, nothing crashed, and yet the system quietly got a little less coherent. Your reviewer rule makes sense to me, but I'm wondering whether a refuting reviewer actually catches that webhook ordering, or whether only a human who's been burned by it before would spot it in the first place?
My real answer: a refuting reviewer catches the webhook bug more often than you'd expect, but not reliably, and not for free. The difference isn't intelligence — it's the job description. The authoring agent is asked "make this work," so it reasons toward the happy path and stops when the path is green. A reviewer explicitly prompted to refute — "assume this is broken; where does it lose data; what happens if the process dies between line 3 and line 4?" — is reasoning toward failure, and webhook-ack-before-persist is a textbook answer to "what if we crash mid-handler." When I framed the reviewer that way, it flagged the ordering unprompted in a majority of runs.
But — and this is your real point — it only gets there because someone who'd been burned wrote "what if it dies between persist and ack?" into the reviewer's instructions. The model has the pattern-matching to apply the lesson; it doesn't reliably have the instinct to go looking for it on a diff that compiles and passes. So the burned-human knowledge doesn't disappear in my setup — it moves from "person reading every diff" to "person encoding failure modes into the skeptic once." That scales better, but it's not the same as the skeptic inventing the concern from nothing.
So I'd put it this way: the refuting reviewer catches it if a burned human has ever taught it to. The human's scar tissue is still load-bearing — it just gets amortized instead of re-spent on every PR. Which is also why payment and DB paths still get a human on the merge regardless: for the failure modes nobody's encoded yet, there's no substitute for someone who remembers the 2am outage.
Great field report. Your three guardrails match what I landed on, and a few things made them stick for me:
"Unable to merge, not just told not to" is the one I under-sold in the post. Totally agree: a reviewer you instruct to hold the line will eventually wave something through. Making it read-only and letting branch rules enforce the merge moves the guarantee outside the model, where it belongs. That's the whole game.
The knowledge-base point reframes it well — you're right that most "drift" is really just missing context, not the model being dumb. A short repo-level "use X, don't re-implement it" file killed most of my duplicate-helper problem too. The hard part is keeping that doc honest as the conventions evolve, but even a stale one beats nothing.
And your test guard is the sharpest of the three: every case was one where it should fire. That's exactly the trap — the model writes tests that confirm its own mental model, so green means "consistent with my assumptions," not "correct." Requiring a case that must fail first is a great forcing function; I'm stealing that.
Love that you landed on human-before-merge for payment and DB paths regardless of the diff. Same here — those are the paths where plausible-but-wrong is most expensive and least visible. Thanks for taking the time to write this out.
The rule is clear. I'd add one number to it: the input each session sent per day. A long run's bill sits in what each call re-sends, not in what you typed.
You're right that the bill lives in the re-send, not the typing. Every turn drags the growing context — files, prior diffs, the reviewer's back-and-forth — back across the wire, so a long session's input tokens climb super-linearly even while my actual prompts stay tiny. The author/skeptic split I'm advocating makes this worse, honestly: two agents each re-reading the diff is double the input for the same patch. Worth saying out loud since I'm recommending it.
So tracking input-tokens-per-session-per-day is a genuinely better health metric than "number of prompts" or wall-clock. It's the thing that quietly decides whether this workflow is economical or just impressive. The levers that actually moved it for me were scoping each agent to the smallest relevant slice instead of the whole repo, and resetting context aggressively between unrelated tasks rather than letting one session accumulate all day. Curious whether you've found the re-send dominated more by file context or by conversation history — in my runs it was the files.
@infoinlet1 I haven't split the two in my runs, so I won't give you a number I can't back. Past a point, history probably overtakes files, since it grows every turn. Splitting the input per session would settle it.