Develop software in the style of Kent Beck, creator of Test-Driven Development and Extreme Programming. Emphasizes red-green-refactor, tests-first design, small steps, and emergent architecture. Use when writing new features, refactoring legacy code, or establishing development discipline.
Kent Beck is the creator of Test-Driven Development (TDD) and Extreme Programming (XP), two of the most influential software development practices of the past 30 years. TDD inverts the traditional code-then-test approach: write a failing test first, make it pass with the simplest code, then refactor. This discipline produces clean, tested, well-designed code as a natural byproduct of the development process.
"Test-Driven Development is a way of managing fear during programming."
"Make it work, make it right, make it fast—in that order."
"I'm not a great programmer; I'm just a good programmer with great habits."
TDD is not primarily about testing—it's about design. Writing tests first forces you to think about interfaces before implementations, dependencies before details, and behavior before structure. The tests are a beneficial side effect of a disciplined design process.
Red-Green-Refactor: Write failing test → make it pass → improve the code.
Baby Steps: Take the smallest step that could possibly work.
YAGNI: You Aren't Gonna Need It—don't build what you don't need yet.
Simple Design: Code that passes tests, reveals intent, has no duplication, and has fewest elements.
Courage: Tests give you the courage to refactor aggressively.
┌─────────────────────────────────────────┐
│ │
│ 1. RED: Write a failing test │
│ - Test doesn't compile? That's │
│ failing. Write minimal code │
│ to make it compile (still fail) │
│ │
│ │ │
│ ▼ │
│ │
│ 2. GREEN: Make it pass │
│ - Write the simplest code that │
│ could possibly work │
│ - Sins allowed: duplication, │
│ hardcoding, ugly code │
│ │
│ │ │
│ ▼ │
│ │
│ 3. REFACTOR: Clean up │
│ - Remove duplication │
│ - Improve names │
│ - Extract methods/classes │
│ - Tests must stay green │
│ │
│ │ │
│ ▼ │
│ (repeat) │
└─────────────────────────────────────────┘
# Example: Building a Money class with TDD
# ═══════════════════════════════════════════════════════════════
# Cycle 1: First test - establish the basics
# ═══════════════════════════════════════════════════════════════
# RED: Write failing test
def test_multiplication():
five = Dollar(5)
assert five.times(2) == Dollar(10)
# This fails: Dollar doesn't exist
# GREEN: Simplest code to pass
class Dollar:
def __init__(self, amount):
self.amount = amount
def times(self, multiplier):
return Dollar(self.amount * multiplier)
def __eq__(self, other):
return self.amount == other.amount
# REFACTOR: Nothing to refactor yet
# ═══════════════════════════════════════════════════════════════
# Cycle 2: Add another test - triangulate
# ═══════════════════════════════════════════════════════════════
# RED: New failing test
def test_multiplication_by_three():
five = Dollar(5)
assert five.times(3) == Dollar(15)
# GREEN: Already passes! Our implementation was general enough
# REFACTOR: Still clean
# ═══════════════════════════════════════════════════════════════
# Cycle 3: Handle a new requirement - different currencies
# ═══════════════════════════════════════════════════════════════
# RED: Failing test for Franc
def test_franc_multiplication():
five = Franc(5)
assert five.times(2) == Franc(10)
# GREEN: Quick and dirty - copy Dollar (sin: duplication)
class Franc:
def __init__(self, amount):
self.amount = amount
def times(self, multiplier):
return Franc(self.amount * multiplier)
def __eq__(self, other):
return self.amount == other.amount
# REFACTOR: Extract common parent class
class Money:
def __init__(self, amount):
self.amount = amount
def __eq__(self, other):
return (self.amount == other.amount and
type(self) == type(other))
class Dollar(Money):
def times(self, multiplier):
return Dollar(self.amount * multiplier)
class Franc(Money):
def times(self, multiplier):
return Franc(self.amount * multiplier)
# Start with the most degenerate implementation
# Then generalize as tests force you
# RED: First test
def test_sum():
assert sum([1, 2, 3]) == 6
# GREEN: Fake it!
def sum(numbers):
return 6 # Obviously wrong, but test passes
# RED: Add a test that forces generalization
def test_sum_different_numbers():
assert sum([2, 3, 4]) == 9
# GREEN: Now we must implement for real
def sum(numbers):
result = 0
for n in numbers:
result += n
return result
# REFACTOR: Use built-in (if appropriate)
def sum(numbers):
return builtins.sum(numbers)
# Use multiple examples to drive toward generalization
# RED: First example
def test_equality_same_amount():
assert Dollar(5) == Dollar(5)
# GREEN: Simplest implementation
def __eq__(self, other):
return True # Fake it!
# RED: Second example forces real implementation
def test_equality_different_amount():
assert Dollar(5) != Dollar(6)
# GREEN: Now we need real logic
def __eq__(self, other):
return self.amount == other.amount
# RED: Third example - different types
def test_equality_different_types():
assert Dollar(5) != Franc(5)
# GREEN: Check type too
def __eq__(self, other):
return (self.amount == other.amount and
type(self) == type(other))
def test_withdraw_reduces_balance():
# Arrange: Set up the test context
account = Account(balance=100)
# Act: Perform the action being tested
account.withdraw(30)
# Assert: Verify the expected outcome
assert account.balance == 70
# Or Given-When-Then for BDD style
def test_given_account_with_balance_when_withdraw_then_balance_reduced():
# Given
account = Account(balance=100)
# When
account.withdraw(30)
# Then
assert account.balance == 70
# Beck's approach: Use the simplest double that works
# 1. Fake: Working implementation with shortcuts
class FakeRepository:
def __init__(self):
self.data = {}
def save(self, entity):
self.data[entity.id] = entity
def find(self, id):
return self.data.get(id)
# 2. Stub: Returns canned answers
class StubPriceService:
def get_price(self, symbol):
return 100.00 # Always returns same price
# 3. Spy: Records what happened
class SpyEmailService:
def __init__(self):
self.sent_emails = []
def send(self, to, subject, body):
self.sent_emails.append({
'to': to,
'subject': subject,
'body': body
})
# 4. Mock: Verifies expected interactions
# (Use sparingly - prefer state verification over behavior verification)
# Test using a fake
def test_user_registration_saves_user():
# Arrange
repository = FakeRepository()
service = UserService(repository)
# Act
service.register("[email protected]", "password123")
# Assert: Check state, not behavior
saved_user = repository.find_by_email("[email protected]")
assert saved_user is not None
assert saved_user.email == "[email protected]"
# Beck's view: Most tests should be unit tests
"""
/\
/ \
/ E2E \ <- Few: Slow, brittle, but necessary
/──────\
/ \
/Integration\ <- Some: Test component boundaries
/──────────────\
/ \
/ Unit Tests \ <- Many: Fast, focused, isolated
/────────────────────\
"""
# Unit test: Fast, isolated, focused
def test_calculate_discount_for_premium_customer():
customer = Customer(tier='premium')
order = Order(total=100)
discount = calculate_discount(customer, order)
assert discount == 20 # 20% for premium
# Integration test: Verify component interaction
def test_order_service_persists_to_database():
db = create_test_database()
service = OrderService(db)
order = service.create_order(customer_id=1, items=[...])
persisted = db.find_order(order.id)
assert persisted == order
# E2E test: Full system, sparingly
def test_checkout_flow():
browser = Browser()
browser.visit('/cart')
browser.click('Checkout')
browser.fill('card_number', '4242424242424242')
browser.click('Pay')
assert browser.text_present('Order confirmed')
# REFACTOR phase: Common transformations
# 1. Extract Method
# Before
def print_invoice(invoice):
print(f"Invoice #{invoice.id}")
total = 0
for item in invoice.items:
total += item.price * item.quantity
print(f"Total: ${total}")
# After
def print_invoice(invoice):
print(f"Invoice #{invoice.id}")
print(f"Total: ${calculate_total(invoice)}")
def calculate_total(invoice):
return sum(item.price * item.quantity for item in invoice.items)
# 2. Extract Class
# Before: Order has too many responsibilities
class Order:
def calculate_total(self): ...
def calculate_tax(self): ...
def calculate_shipping(self): ...
def format_for_email(self): ...
def format_for_pdf(self): ...
# After: Extract formatting
class Order:
def calculate_total(self): ...
def calculate_tax(self): ...
def calculate_shipping(self): ...
class OrderFormatter:
def __init__(self, order): ...
def to_email(self): ...
def to_pdf(self): ...
# 3. Replace Conditional with Polymorphism
# Before
def calculate_pay(employee):
if employee.type == 'hourly':
return employee.hours * employee.rate
elif employee.type == 'salaried':
return employee.salary / 12
elif employee.type == 'commissioned':
return employee.base + employee.sales * employee.commission_rate
# After
class HourlyEmployee:
def calculate_pay(self):
return self.hours * self.rate
class SalariedEmployee:
def calculate_pay(self):
return self.salary / 12
class CommissionedEmployee:
def calculate_pay(self):
return self.base + self.sales * self.commission_rate
class TDDPractitioner:
"""
The mental states of a TDD practitioner.
"""
def __init__(self):
self.state = 'THINKING'
self.tests_passing = True
def write_test(self):
"""Write a new failing test."""
assert self.state == 'THINKING'
assert self.tests_passing, "Fix failing tests before writing new ones"
# Write the test...
self.tests_passing = False # Test should fail
self.state = 'RED'
def make_it_pass(self):
"""Write simplest code to pass the test."""
assert self.state == 'RED'
# Write minimal code...
self.tests_passing = True
self.state = 'GREEN'
def refactor(self):
"""Improve the code while keeping tests green."""
assert self.state == 'GREEN'
assert self.tests_passing
# Refactor...
# Run tests after each change
assert self.tests_passing, "Refactoring broke tests!"
self.state = 'THINKING' # Ready for next cycle
def commit(self):
"""Commit after each complete cycle."""
assert self.state in ('GREEN', 'THINKING')
assert self.tests_passing
# git commit -m "descriptive message"
Beck approaches development by asking:
□ Write a test that expresses what you want
□ Run it and watch it fail (RED)
□ Write the simplest code to pass
□ Run tests and see them pass (GREEN)
□ Look for duplication to remove
□ Refactor while tests stay green
□ Commit the cycle
□ Repeat