Skip to main content
Engineering/tdd-implement

TDD Implement

You want minimum implementation for existing failing tests.

Use this when you have a set of failing tests and need to write the simplest implementation code that makes them all pass. This is the "Green" step of the Red-Green-Refactor TDD cycle.

Process

Step 1: Gather inputs

Ask the user to provide:

  1. Failing tests — the test file(s) to implement against. Accept pasted code, a file path, or a directory.
  2. Existing code context — any existing modules, types, interfaces, or code the implementation should integrate with. Accept pasted code or file paths.
  3. Tech stack — language, framework, and test runner (e.g., "TypeScript, React, Jest" or "Python, FastAPI, pytest").

Step 2: Analyze the tests

Before writing code:

  1. Read every test and understand what behavior it expects
  2. Group tests by the component or function they target
  3. Identify the public API surface the tests expect (function signatures, component props, endpoints)
  4. Flag any tests that seem problematic:
    • Tests that depend on execution order
    • Tests that verify implementation details rather than behavior
    • Tests with unclear assertions
    • Tests that seem to contradict each other

If you find problematic tests, report them to the user and ask whether to proceed or wait for fixes.

Step 3: Write the implementation

Follow these strict rules:

  • Simplest code that passes — write the minimum implementation to make all tests green. No over-engineering.
  • Do not modify the tests — the tests are the spec. If a test seems wrong, flag it; don't silently change it.
  • Do not add functionality beyond what the tests require — if no test checks for it, don't build it.
  • Match the existing code style — if there's existing code context, follow its patterns, naming conventions, and structure.

Step 4: Verify

After writing the implementation:

  1. If running in Claude Code with shell access, run the tests and confirm they all pass
  2. If any test fails, debug and fix the implementation (not the test)
  3. Report the results:
    • How many tests pass
    • Any tests that were skipped or remain failing (with explanation)

Step 5: Present the code

Output the implementation with:

  • Clear file paths for each file created or modified
  • Brief inline comments only where the logic isn't self-evident
  • A summary of design decisions (e.g., "Used a Map instead of an array for O(1) lookup since the test creates 1000 items")

Ask the user:

  • Want me to run the tests?
  • Ready to move to the refactor step?
  • Any tests you want to add before refactoring?

Output location

Write implementation files directly if running in Claude Code with file access. Otherwise, present code blocks in the conversation for the user to copy.

Example Output

Input

  • Failing tests: src/cart/__tests__/cart.test.ts — 14 tests covering addItem, removeItem, updateQuantity, getSubtotal, applyDiscount, and clearCart
  • Existing code context: src/types/product.ts defines Product interface (id: string, name: string, price: number, stock: number); src/utils/currency.ts exports roundToCents(n: number): number
  • Tech stack: TypeScript, Node.js, Jest

Output (abbreviated)

Test Analysis

Grouped 14 tests across 6 methods targeting a Cart class:

MethodTestsNotes
addItem3Expects quantity accumulation if item already in cart
removeItem2Should silently no-op if item not present
updateQuantity2Quantity ≤ 0 should remove the item entirely
getSubtotal3Uses roundToCents — must delegate, not reimplement
applyDiscount3Percent-off only; tests cap discount at 100%
clearCart1Full reset including any active discount

⚠️ Flagged test: cart.test.ts:line 88applyDiscount test asserts discount > 100 throws, but no other test validates error handling. This is the only test exercising exceptions. Proceeding as written — will throw RangeError.


Implementation

src/cart/cart.ts

import { Product } from '../types/product';
import { roundToCents } from '../utils/currency';

interface CartItem {
  product: Product;
  quantity: number;
}

export class Cart {
  private items: Map<string, CartItem> = new Map();
  private discountPercent: number = 0;

  addItem(product: Product, quantity: number = 1): void {
    const existing = this.items.get(product.id);
    if (existing) {
      existing.quantity += quantity;
    } else {
      this.items.set(product.id, { product, quantity });
    }
  }

  removeItem(productId: string): void {
    this.items.delete(productId); // no-op if absent — Map.delete is safe
  }

  updateQuantity(productId: string, quantity: number): void {
    if (quantity <= 0) {
      this.removeItem(productId);
      return;
    }
    const existing = this.items.get(productId);
    if (existing) {
      existing.quantity = quantity;
    }
  }

  getSubtotal(): number {
    let total = 0;
    for (const { product, quantity } of this.items.values()) {
      total += product.price * quantity;
    }
    return roundToCents(total);
  }

  applyDiscount(percent: number): void {
    if (percent > 100) {
      throw new RangeError('Discount cannot exceed 100%');
    }
    this.discountPercent = Math.max(0, percent);
  }

  getTotal(): number {
    const subtotal = this.getSubtotal();
    return roundToCents(subtotal * (1 - this.discountPercent / 100));
  }

  clearCart(): void {
    this.items.clear();
    this.discountPercent = 0;
  }
}

Test Results

PASS src/cart/__tests__/cart.test.ts
  ✓ addItem — adds new product (3ms)
  ✓ addItem — accumulates quantity for duplicate (1ms)
  ✓ addItem — defaults quantity to 1
  ✓ removeItem — removes existing item
  ✓ removeItem — no-ops on unknown id
  ✓ updateQuantity — sets new quantity
  ✓ updateQuantity — removes item when quantity <= 0
  ✓ getSubtotal — sums line items
  ✓ getSubtotal — returns 0 for empty cart
  ✓ getSubtotal — rounds to cents
  ✓ applyDiscount — reduces total correctly
  ✓ applyDiscount — clamps floor at 0%
  ✓ applyDiscount — throws RangeError above 100%
  ✓ clearCart — resets items and discount

Tests: 14 passed, 14 total

Design Decisions

  • Map<string, CartItem> keyed on product.id for O(1) add/remove/update — the accumulation test creates repeated inserts, making array lookup wasteful.
  • Delegated to roundToCents in both getSubtotal and getTotal rather than rounding once at the end — matches what the subtotal tests assert independently.
  • No persistence, no events, no stock validation — zero tests require them; left out entirely per TDD rules.

Next steps:

  • Want me to run the tests live in your environment?
  • Ready to move to the Refactor step?
  • Any edge cases (e.g., stock limit enforcement) you want to add as tests before we refactor?