# I Haven't Written a Test in a Year ## And My Test Suite Is Better for It *Draft for public journal - January 2026* --- I haven't written a test in a year. For a developer who built an entire philosophy around "Bugs as Missing Test Cases," this sounds like professional negligence. Like I've abandoned the craft. The tests exist. I just didn't write them. --- ## What Actually Happened AI writes the tests now. I review them. This isn't vibe-testing. I don't blindly accept whatever the AI generates because it looks plausible and the green checkmarks feel reassuring. This is a systematic workflow: 1. I implement a feature (or AI does, and I verify) 2. I describe what the tests should cover 3. AI generates the test file 4. I review the generated tests 5. I run them, verify they test what matters, request changes if needed 6. Tests get committed Most days this works. But step 4 is where it breaks down more than I'd like to admit. Sometimes I'm three reviews deep and realize the AI keeps generating tests that pass but don't actually prove anything. They're testing the happy path six different ways while ignoring the one boundary condition that caused last month's production incident. That's when I have to dictate the test structure myself. The 20-30 minutes I used to spend writing a test file? Now I spend 10 minutes reviewing one. The output is the same. Often better, because AI doesn't get bored writing the tedious edge cases. But the skill has fundamentally changed. I used to write good tests. Now I catch bad tests. Same outcome. Different process. Different expertise. --- ## What I Review For When AI generates a test file, I'm checking judgment, not syntax. **Does it test the right thing?** AI tests what it sees. I know what the code is *supposed* to do, which isn't always what it does. The test needs to verify intent, not just current behavior. **Does it catch the edge cases I know about?** I've lived in this codebase for years. I know where the dragons are. If the AI-generated tests don't poke at the known failure modes, they're not good enough. **Is it testing behavior or implementation?** Bad tests break when you refactor. Good tests break when you break functionality. AI often couples too tightly to implementation details. I push back toward behavior. **Will it break for the wrong reasons?** Date-dependent tests. Order-dependent assertions. Hardcoded values that become false in six months. AI doesn't feel the pain of maintaining tests over time. I do. This review process takes domain knowledge. It takes experience with what breaks. It takes judgment that AI doesn't have. ### A Review in Practice I asked Claude to generate tests for a service that processes subscription renewals. The first version came back with something like this: ```ruby test "processes renewal successfully" do subscription = create(:subscription, status: "active", expires_at: Time.current + 1.day) result = RenewalService.call(subscription) assert result.success? assert_equal "active", subscription.reload.status end test "handles expired subscription" do subscription = create(:subscription, status: "active", expires_at: Time.current - 1.day) result = RenewalService.call(subscription) assert result.success? assert_equal "renewed", subscription.reload.status end ``` Both tests pass. Both miss the point. The first test renews a subscription that isn't due yet. Why would you renew something that doesn't expire for another day? It should either be a no-op or an error. The second test asserts `success?` for an expired subscription, but the actual business rule is that subscriptions expired for more than 30 days should *fail* renewal and require manual intervention. The AI tested the code paths. I know the business rules. After review, the tests looked like this: ```ruby test "renews subscription expiring within 7 days" do subscription = create(:subscription, status: "active", expires_at: 3.days.from_now) result = RenewalService.call(subscription) assert result.success? assert_equal "active", subscription.reload.status assert subscription.expires_at > 3.days.from_now end test "skips subscription not yet due for renewal" do subscription = create(:subscription, status: "active", expires_at: 30.days.from_now) result = RenewalService.call(subscription) assert result.skipped? end test "fails renewal for subscription expired over 30 days" do subscription = create(:subscription, status: "active", expires_at: 45.days.ago) result = RenewalService.call(subscription) refute result.success? assert_includes result.errors, "requires manual review" end ``` Same service. Completely different tests. The difference isn't syntax. It's knowing that "expired" means different things depending on *how long ago* it expired. This is what test review looks like. The AI wrote plausible tests. I caught the ones that would have let real bugs through. --- ## What I've Gained **Coverage I wouldn't have written.** How many tedious edge cases do you skip because life is short? AI doesn't skip. It will generate the boring permutation tests that you technically know you should write but never do. My coverage is higher because AI has more patience than I do. Sometimes it tests something and I think: "Oh. Yeah. That could definitely break." **Faster iteration on test quality.** "This test is too coupled to implementation. Rewrite it to test behavior." That sentence takes three seconds to type and gets me a better test faster than rewriting it myself. **More time for test strategy.** What should we test? How should the test suite be organized? What's the failure mode we're not covering? These strategic questions matter more than the syntax. Offloading the typing lets me think at a higher level. --- ## What I've Lost **Muscle memory for test syntax.** I used to type `assert_equal` without thinking. Now I sometimes pause on the argument order. The syntax has faded because I don't practice it. **The design pressure of writing tests first.** Last month I built a service to retry failed payments. I wrote the implementation first, then generated tests. The tests passed. Three days later, I needed to add partial refunds. The test structure fought me—I had to rewrite three tests to accommodate the new flow. If I'd written tests first, I would have noticed the inflexible design before I built it. That was the moment I realized: TDD isn't just about coverage, it's about design feedback loops. I'm not sure I've replaced that loop with anything better. Something happens when you manually type out test cases. You think of cases you wouldn't have thought to ask for. The writing *is* the thinking. Reviewing someone else's tests (even AI-generated ones) doesn't trigger the same discovery process. And if I had to write tests without AI tomorrow, I'd be slower than I was a year ago. If my internet went down and a critical test needed writing, I'd need to look up assertion syntax I used to know cold. That dependency is real. **There's a more dangerous version of this story: what if I'm not just losing syntax memory, but losing the ability to even recognize bad tests?** So far, my review skills are sharp. But reviewing is a downstream skill. If I stop writing entirely, does my review quality decay? The muscle you don't use eventually can't even judge the muscles that do. I don't think I'm there yet. But it's the risk I track most carefully. --- ## The Uncomfortable Truth **I'm a worse test-writer than I was a year ago.** If you put me in a room with a whiteboard and asked me to write test cases from scratch, I'd be rustier than I was in early 2025. The skill has atrophied from disuse. **I'm a better test-reviewer than I was a year ago.** I've spent a year reading and critiquing AI-generated tests. I know what bad AI tests look like. I know what's missing, what's over-specified, what will break in six months. My review skills are sharper than ever. **These are different skills.** Writing and reviewing are not the same expertise. The market doesn't recognize this yet. Job interviews still ask you to write tests on a whiteboard. They don't ask you to review AI-generated tests for quality. But the job has changed. The interview just hasn't caught up. --- ## The Team Question Last month, a junior dev wrote tests for a new API endpoint. I reviewed them and requested five changes. They all passed. Two days later, I realized we needed a sixth test case—a subtle race condition under concurrent load that I've seen break production twice. I wrote that test myself. The junior dev learned the pattern. But they also learned that my AI-generated tests wouldn't have caught it either. The asymmetry is teaching them something about my process, and I haven't decided if that's good or bad. When I review a teammate's pull request, I'm now evaluating their hand-written tests with a reviewer's eye trained on AI output. I notice different things. I'm more attuned to implementation coupling and fragile assertions because that's what I catch in AI-generated tests all day. But the reverse is also true: when my AI-generated tests go through code review, does anyone notice? Should they? I don't hide it, but I also don't label every test file "generated by Claude." The tests are committed under my name, reviewed by me, and I stand behind their quality. If we both lost AI access tomorrow, they'd be better prepared than I am. I've traded that resilience for throughput. That's a bet, and I haven't fully reckoned with the downside. --- ## What This Means Testing expertise isn't obsolete. It's transformed. The knowledge of *what* to test matters more than ever. Knowing which edge cases break, understanding the domain, recognizing when a test is too fragile. That judgment is exactly what AI lacks. The expertise is the same; the expression is different. This is quality judgment at level 1—knowing what 'correct' means for this business, these users, this risk profile. AI can generate passing tests, but it can't feel which tests are worth having. That judgment requires the embodied experience I describe here. **Knowing what to test matters more than knowing how to type it.** I still apply "Bugs as Missing Test Cases" every day, a principle I've followed for over a decade: every production bug reveals a test that should have existed. When something breaks, I still ask: "What test would have caught this before users did?" The philosophy is unchanged. But the hands that write the test are different now. **Quality judgment can't be automated (yet).** AI can generate a test that passes. It can't generate a test that will still be valuable in two years. It can't weigh the maintenance cost against the coverage benefit. It can't feel which tests are worth having. That judgment is human. **The skill transfer is real.** A developer who never learned to write tests won't be a good test reviewer. You need to have written bad tests to recognize them. The foundation is still required. It's just not the daily practice anymore. This is part of the broader implementor-to-orchestrator shift (a transition I explored in The Orchestrator Identity). The pattern holds for code, for tests, for documentation. The skill is verification and judgment, not production. --- I haven't written a test in a year. But I've reviewed a thousand of them. And when I interview for my next job, they'll ask me to write tests on a whiteboard. They won't ask me to review AI-generated tests for correctness, spot the business logic gaps, or evaluate whether a test suite will survive the next refactor. They'll test the skill I've stopped practicing and ignore the one I've been sharpening every day. The job has changed. The interview hasn't. Yet. And I'm not sure which side of that gap I want to be on. --- ## Revision Notes (v5) **Key improvements from v4:** 1. **Added explicit framework connection** — Positioned test review as "quality judgment at level 1" linking to Judgment Over Execution Premium framework, making the article feed the broader knowledge infrastructure. 2. **Grounded TDD loss in concrete example** — Replaced abstract uncertainty about design debt with specific scenario: retry service that required test rewrites when adding partial refunds, showing real design feedback loop loss. 3. **Added verification collapse counterargument** — Surfaced the deeper risk: not just losing syntax memory, but potentially losing the ability to recognize bad tests over time. Demonstrates sophisticated risk awareness. 4. **Expanded "The Team Question" with concrete scenario** — Added junior dev/race condition example showing skill asymmetry in action rather than abstract concern. Makes team impact tangible. 5. **Line-level polish**: - "I'm checking judgment" → "I'm checking judgment, not syntax" (line 41) - "Both tests are wrong" → "Both miss the point" (line 89, stronger) - "What test would have caught this?" → "What test would have caught this before users did?" (line 184, connects to production reality) - "The interview hasn't" → "The interview hasn't. Yet." (line 204, adds hope without softening) **Previous versions:** - **v4**: De-AI'd (removed em-dashes, "here's"/"let me" constructions, varied formatting patterns) - **v3**: Added concrete examples, trimmed abstractions, made Orchestrator Identity cross-reference explicit - **v2**: Removed TL;DR, reordered Gains/Losses, added subscription renewal test example, deepened TDD uncertainty - **v1**: Initial draft **Status: Strengthened framework integration, grounded abstractions in concrete examples, demonstrated deeper risk awareness. Ready for publication review.