CLAUDE LABJP
FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as OctoberFORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Articles/Claude Code
Claude Code/2026-04-26Advanced

Test-Driven Development with Claude Code — A TDD Workflow for the Agent Era

Adding the constraint of 'write tests first' to Claude Code stops the agent's drift and dramatically changes the quality of generated code. Here's the TDD workflow I arrived at after six months of trial and error.

Claude Code197TDD4Test-Driven DevelopmentAgents2Workflow8Software Design

About six months into using Claude Code seriously, I kept hitting the same wall. My instructions seemed clear, but when I opened the git diff, files I never asked about had been modified. Tests were green, but on review the agent had subtly misread the spec. Or I would ask for a refactor and the function's behavior would change along with it.

At first I figured the prompts were the problem and started writing more detailed specs. Eventually I landed on a different hypothesis: agents need a mechanical way to know what "done" looks like.

Human developers carry a mental image of "the code in its correct state" and constantly check against it as they type. Prompts give Claude Code an image too, but it's fuzzy and shifts mid-conversation. Tests, on the other hand, are a spec whose pass/fail can be judged mechanically. Write them first, and the agent has an unambiguous target to aim at.

After this realization, I switched almost all my Claude Code work to a TDD-first style. Drift dropped sharply, the time I spend in code review was cut in half, and — more importantly — I now feel safe handing things off. Here's the workflow I use, including the failure modes I learned from.

Why TDD Hits Differently in the Agent Era

One reason classic TDD never went mainstream is the psychological cost of writing tests first. The implementation is already vivid in your head — turning it into tests feels like duplicate work.

Claude Code breaks that assumption. Writing tests is the work most easily delegated to an agent. Describe the spec in plain English, and Vitest, Jest, or pytest cases come back in 30 seconds. Tests that would take me 30 minutes by hand show up after a three-turn exchange.

Those generated tests serve another role. They become a spec for Claude Code itself. With the test file open and an implementation request submitted, the agent reads the tests first and locks onto the required behavior precisely. Spec drift through prompt rewording is much smaller when the spec is communicated through tests.

A second effect surprised me: the agent stops over-reaching. With the unambiguous target of "make these tests pass," Claude Code stops poking around in unrelated files. This isn't documented anywhere in Anthropic's official material — it's behavior I noticed only after running the loop many times.

The 5-Step TDD Loop I Settled On

My current workflow has five steps. Each step has a clear split between what Claude Code handles and what a human must judge.

The first step is "lock down the spec through conversation." I describe the feature loosely, then ask Claude Code to enumerate boundary conditions, edge cases, and likely failure modes. Reading the list helps me sharpen ambiguities I had been glossing over. This is the human's job.

In the second step, I have the agent write failing tests against the now-clear spec. Following the test-first principle, the implementation file either doesn't exist yet or contains only a function signature. Running npm test should produce red. This is the Red phase.

The third step asks the agent for the minimal implementation needed to turn the tests green. The crucial prompt trick here: explicitly say "Write a minimal implementation in src/foo.ts to pass the tests below. Do not modify the test file or any other files." This locks the agent to the implementation file and prevents the "cheating green" of editing tests to pass.

The fourth step is refactoring. With tests green, I ask Claude Code to "refactor for readability and maintainability while keeping the tests green." Tests stay off-limits. Tests passing before and after refactor proves the behavior is preserved.

The fifth and final step is human code review — but the review covers both the implementation and the newly-added tests. I check whether the tests themselves capture the right spec and whether the edge cases are sufficient.

Real Prompt Templates I Use

Abstractions only get you so far. Here are the actual prompt templates I use day-to-day, with TypeScript and Vitest as the example. They translate easily to other stacks.

For the spec exploration phase:

I want to implement an "email address validator" function.
List the following:
1. Three or more valid input examples
2. Five or more inputs that should be rejected
3. Boundary cases the spec is ambiguous about
   (e.g., subaddressing, internationalized domain names)
4. Cases that should be tested but are easy to overlook

Don't write any implementation. Reply as a list.

I read the answer and fill in spec gaps in my own head. For instance, "do we accept quoted local parts?" gets a definite answer.

Once the spec is set, I move to the test-generation prompt:

Write Vitest tests in `src/utils/email-validator.test.ts` for the following spec.

Spec:
- Function: validateEmail(input: string): { ok: true } | { ok: false; reason: string }
- Accept only RFC 5321 dotted-form local parts
- Accept subaddressing (foo+bar@example.com)
- Accept IDN domains only in their punycode form
- On rejection, return reason as one of "missing-at" / "invalid-local" / "invalid-domain"

Constraints:
- Create `src/utils/email-validator.ts` as a skeleton with only the export statement
- Minimum 12 cases, grouped with describe blocks
- Tests are expected to be red since nothing is implemented yet

Running npm test -- src/utils/email-validator.test.ts after this naturally fails everything. That's our Red.

Now the implementation prompt:

Implement validateEmail in `src/utils/email-validator.ts`.
All tests must turn green.

Strict rules:
- Treat the test file `src/utils/email-validator.test.ts` as read-only.
  Do not change a single character.
- Do not modify other files (package.json, other utils, etc.)
- No new external dependencies. Use only Node.js standard library and TypeScript stdlib.

When done, run `npm test -- src/utils/email-validator.test.ts` and report the result.

Without the strict-rules section, Claude Code occasionally says "the test looks wrong, let me fix it" and rewrites the test. This violates the first principle of TDD, so I forbid it explicitly.

Hooks and Permissions to Stop the Drift

Claude Code's Hooks feature lets you run scripts before and after file writes. I have a TDD-specific hook wired into .claude/settings.json.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "node .claude/hooks/protect-test-files.mjs"
          }
        ]
      }
    ]
  }
}

protect-test-files.mjs reads JSON from stdin and exits with an error if the target file ends with .test.ts or .spec.ts and the TDD_PHASE environment variable is anything other than red. This structurally prevents accidental test-file edits during the implementation or refactor phases.

#!/usr/bin/env node
import { readFileSync } from "node:fs";
 
const input = JSON.parse(readFileSync(0, "utf-8"));
const filePath = input?.tool_input?.file_path ?? "";
const phase = process.env.TDD_PHASE ?? "implementation";
 
const isTestFile = /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filePath);
 
if (isTestFile && phase !== "red") {
  console.error(
    `[TDD Guard] Blocked edit to test file ${filePath}.\n` +
    `Current phase: ${phase}\n` +
    `If you genuinely need to add or change tests, restart with TDD_PHASE=red claude.`
  );
  process.exit(2);
}

Before this guard, I had several incidents where Claude Code "helpfully tidied up the tests" mid-refactor and a bug that should have been caught slipped through. Now it's prevented at the system level.

Refactoring with the Agent: The Sharpest Edge of TDD

Refactoring — the last step of TDD — is where Claude Code shines brightest, but also where prompt design demands the most care.

One failure I went through: I wrote "rewrite this into better code while keeping the tests green." The agent kept tests green, but it changed the function signature and rewrote all the call sites in one pass. Formally correct — tests passed — but API compatibility was broken and downstream modules were busted.

These days I structure refactor prompts like this:

Refactor `src/utils/email-validator.ts`.

Goals:
- Reduce nesting through early returns
- Make regex intent obvious through descriptive variable names

Strict rules:
- The signature and public API of validateEmail must not change
- Do not modify the test file
- Adding new helper functions is fine, but renaming or removing existing ones is not
- Do not modify other files

When done, list your changes as bullets and confirm all tests still pass.

Two keys to refactor success: narrow the goals, and lock the outer shape with strict rules.

Metrics Before and After Going TDD

I spent three months converting a mid-sized site I run personally (about 40,000 lines of TypeScript) to TDD. Areas previously covered by "well, it works" got tests retroactively. Claude Code made this manageable.

The numbers shifted visibly. The number of times I asked the agent "what is this code doing?" during reviews dropped from a monthly average of 40 to 8. Reading the tests gave me the spec, so the implementation took less brain effort to grasp.

Production bugs dropped too. The 11 bugs in the three months before became 3 bugs in the three months after. All three remaining bugs traced back to edge cases that lacked tests. Only bugs that escaped the test net survived — exactly what TDD is supposed to deliver.

The unexpected cost: my Claude Code API spend went up. Splitting work into tests and implementation increases token throughput. Monthly cost rose to about 1.3x. Still a clear net win because my own time savings dwarf the API delta.

When TDD Doesn't Fit, and When It Does

To be honest, not every task fits TDD. Tasks I deliberately keep TDD-free:

UI prototyping. While iterating on Tailwind and React layouts, writing tests upfront becomes a drag. Snapshot or visual-regression tests fit better here.

Data exploration and one-off scripts. Aggregating a CSV once doesn't justify the test-first overhead.

Where TDD pays huge dividends: anywhere the behavior can be precisely defined. Validation, parsing, calculation, state transitions, API response shaping — anything with clear inputs and outputs. On my site, the Stripe webhook handler, plan-detection logic, and article metadata extractor are all under TDD and have barely needed touching since.

The Single Step You Can Try Tomorrow

This piece ran long, so here's the smallest thing you can do tomorrow.

Pick a recently-touched file you don't fully trust. Ask Claude Code:

Infer the current behavior of `src/foo.ts` and write Vitest tests for it in `src/foo.test.ts`.
Do not modify the source. The tests are documentation of current behavior.

This is technically "characterization testing" rather than TDD, but it's the perfect entry point to TDD in the agent era. Reading the generated tests shows you how Claude Code interprets your code. If the interpretation is wrong, your spec is ambiguous. If it's right, you now have a safety net for the next change.

From here, gradually add the habit of writing tests first for new code. Six months in, you'll be where I am — unable to use Claude Code without TDD because doing it any other way feels reckless.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Claude Code2026-04-17
From Spec to Production: Spec-Driven Development with Claude Code
Write a YAML spec and Claude Code auto-generates tests, implementation, and documentation. A practical guide to Spec-Driven Development covering spec formats, TDD automation, and CI/CD pipeline integration with real code examples.
Claude Code2026-05-31
Claude Code vs. Claude in Chrome: Where I Draw the Line in Daily Ops
Running apps solo means constantly hopping between an editor and a browser, and that hopping quietly drains your focus. Here is how I split work between Claude Code and Claude in Chrome over a month, plus the rule I use when a task straddles both.
Claude Code2026-05-24
Five Filters I Use Before Wiring a Claude Code Skill Into My Daily Workflow
Public Claude Code Skills keep multiplying. As an indie developer running four AI tech blogs through Claude Code, I share the filters that decide which Skills stay in my daily workflow — and which ones I quietly remove after a few days.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →