Three months. Zero code review. Just specs, demos, and trust in the system. Here's what happened when I stopped reading diffs and started reading requirements instead, and why I'm rethinking how much code review actually matters for delivery quality.
I've spent a good chunk of my career being the person who reads the code. Code review was never a checkbox for me, it's where I caught the subtle stuff: the off-by-one in a Kafka consumer offset, the transaction boundary that didn't cover a rollback case, the null check someone forgot in a Debezium event handler. That instinct doesn't go away easily. But I've also spent the last year building agent-assisted workflows around resume parsing and Job Fit scoring at ITJobOpportunities, and something kept bugging me. Every time I reviewed AI-generated code, I was doing it the old way, eyeballing diffs, trusting my gut, moving on. That's fine when a human wrote the code and I trust their intent. It's a different problem when the "author" has no continuity between sessions and no memory of why it made a decision five minutes ago. So I asked myself an uncomfortable question: what if I removed my eyeballs from the loop entirely? Not because code review is useless, but because I wanted to know what would break if the safety net I'd relied on for two decades simply wasn't there. The rule I set for myself Simple on paper: specs define tests, tests control the code, code is a black box. I write or approve the spec. An agent (I ran this mostly on Claude models, some Sonnet, some Opus, depending on the ticket) writes tests from that spec. The code exists to satisfy those tests. I never open the code files. In practice, this forced me to build a system similar in spirit to what some people in the agent-tooling space call "backlog-as-data." I built my own small CLI in Python, nothing fancy, but the shape converges because the constraint is the same: when nobody's reading the code, the process itself has to carry the trust a human reviewer normally provides. The backlog had to stop being a document The first thing that broke, almost immediately, was my old habit of tracking tickets in a markdown to-do list, a ## Done section, an ## In Progress section, lines cut and pasted between them. When an agent is the one moving tickets around, "cut this line and paste it under a different heading" isn't an operation, it's a document mutation that can go wrong several ways. I watched an agent duplicate a ticket across two sections once because it appended instead of moved. Small thing, but it told me the model was wrong. So every ticket became its own file, with status living in YAML frontmatter, not in its position on a page: --- id: FIT-14 title: Cap Job Fit summary at 3 sentences for mobile card type: ticket status: todo priority: should exec: model: sonnet effort: think review: light matured: 2026-06-02 --- # FIT-14 — Cap Job Fit summary at 3 sentences for mobile card Everything below the frontmatter is the actual spec, contract, edge cases, sample inputs and outputs. Everything above it is data owned by the CLI, never hand-edited. Same file, so the two can't drift apart, which was my biggest fear early on. Moving a ticket from todo to wip becomes a one-line, idempotent field update instead of a text relocation. Boring sentence, real fix. Deciding how hard to think, as a first-class decision Here's the part that surprised me most. Once I committed to never reading code, I realized I was still making an important judgment call on every ticket, just earlier in the process: how much reasoning does this actually deserve? A ticket to rename a CSS class doesn't need the same scrutiny as one that touches how candidate emails get deduplicated during Easy Apply on ITJobOpportunities-style flows (experiments like this stay in a sandboxed branch, never near the real platform). So I made that decision explicit, versioned, and dated, right in the frontmatter. Three tickets, three different bets The best way to explain the effort dial is to walk through three real tickets from the last quarter, because the differences between them are the whole point. Ticket one, near zero stakes. A button on a mobile card said "View Job" and I wanted it to say "See Details" for consistency with another screen. --- id: UI-31 title: Rename mobile card CTA to "See Details" status: todo priority: could exec: model: haiku effort: none review: none --- No reviewer, no deep reasoning, just a fast model executing a literal string change validated by a snapshot test. It shipped in about four minutes. If it had been wrong, the cost of finding out was one screenshot and a follow-up ticket. Spending a reviewer agent on this would have been like hiring a structural engineer to hang a picture frame. Ticket two, moderate stakes. Pagination on the job listings page was returning duplicate results when a search query landed exactly on a page boundary, an off-by-one in how the offset was calculated against a filtered count. --- id: SEARCH-52 title: Fix duplicate results at pagination boundary status: todo priority: must exec: model: sonnet effort: think review: light --- One reviewer, light dosage, meaning it read the diff and the tests but didn't go hunting for adjacent problems outside the ticket's blast radius. It caught something I hadn't specified: the fix worked for the reported case but broke when the result set was empty, because the new offset math divided by a count that could be zero. That went back as a fix, not an escalation, because it was a gap in my spec's edge cases, not a disagreement about behavior. Fair catch, cheap to fix, exactly the level of scrutiny the ticket deserved. Ticket three, real stakes. This was the sandboxed Easy Apply-style experiment I mentioned, specifically the logic that decides whether two applications from the same person are duplicates when the candidate used two different email addresses that both resolve to the same phone number on file. --- id: DEDUPE-08 title: Cross-reference phone number when emails differ on duplicate application check status: todo priority: must exec: model: opus effort: think review: deep --- Three reviewers, deep dosage. One flagged that the spec itself was ambiguous about what happens when the phone number field is present but malformed, not missing, just badly formatted, like extra characters from a copy-paste. That's not a code defect, it's a spec defect, and it got escalated back to me with the reason stated plainly: "spec doesn't define behavior for malformed phone strings, implementer picked a default, flagging for spec owner decision." I hadn't thought about that case at all when I wrote the ticket. That's the disposition working exactly as intended, a problem surfaced early instead of buried in behavior nobody would notice until a real candidate hit it. Same CLI, same lifecycle, three completely different risk postures. That's the entire point of making the effort dial a field in the file instead of a feeling in my head. Automating the boring half of the lifecycle I used to move tickets by hand between states, and I lied to myself about how consistent I was. I wasn't. Some tickets sat "in progress" for days after they'd shipped because I forgot to update the doc. Now hooks handle it. Launching a ticket sets wip . A successful merge, only if the commit referencing that ticket ID is actually on the branch, which took an embarrassing amount of debugging to get right, sets merged . Deploy sets shipped . Nobody, human or agent, touches the back half of that lifecycle by hand anymore. One lesson that cost me an afternoon: hooks should never block a delivery, even if the lifecycle update itself fails. A hook once threw a non-zero exit because a status file was locked, and it stopped a clean deploy. Every hook now exits 0 no matter what. Lifecycle bookkeeping is a nice-to-have. Shipping isn't. A smaller, dumber lesson: don't run git add specs/ on a shared checkout with multiple parallel worktrees. I did this once running three tickets concurrently, and it swept a neighboring session's uncommitted spec changes into the wrong commit. Every commit is now scoped to the exact files that ticket touched. A third scenario, less dumb but mor...