Code Katas
Short, focused drills. Each kata targets a specific skill from the module. 15-25 minutes each. Repeat until fluent.
All katas operate on the same provided sample (below) or on your own recent code. Keep a mistake log after each kata.
The Sample
# src/billing/invoice.py
import datetime
class Invoice:
def __init__(self, rows, client, currency):
self.rows = rows
self.client = client
self.currency = currency
def total(self):
t = 0
for r in self.rows:
t = t + r["amount"] * r["qty"]
if self.client["tier"] == "gold":
t = t - (t * 10 / 100)
if self.currency != "USD":
t = t * self.get_rate()
return t
def get_rate(self):
# always returns 1.0 for now
return 1.0
def send(self):
# builds and sends an email
subject = "Invoice " + str(datetime.datetime.now())
body = self.client["name"] + ": total is " + str(self.total())
# smtp call here...
print("Sent:", subject, body)
Kata 1: Three Review Comments, One PR (15 min)
Write three review comments on the sample, one per bucket (clarity, design, tests). Each must be specific, constructive, and evidence-based. At least one must explicitly say not blocking.
Skill targeted: writing useful comments quickly.
Kata 2: Spot the Broken Window (15 min)
Identify three broken windows in the sample (e.g., magic numbers, inline SMTP, no tests, the "always returns 1.0" placeholder). Write a one-line commit subject for each cleanup commit you would land, in the shape chore: ... or refactor: ....
Skill targeted: noticing hygiene debt without being told.
Kata 3: Pressure Before Pattern (20 min)
List every pattern you are tempted to introduce into Invoice. For each, answer: is there a current pressure for it, and which rule of simple design does it serve? Kill every one that is pure speculation. The goal is usually to eliminate most of them.
Skill targeted: justifying patterns (or not) against current pressure.
Kata 4: Extract a Testable Core (25 min)
Refactor Invoice so that total() is a pure function over data (no time, no network, no mutability). send() may still be side-effectful, but all domain logic is testable without mocks. Do not add test-only methods on Invoice. Write at least three unit tests against the pure core.
Skill targeted: seams and pure cores without damaging the domain.
Kata 5: Reshape the History (15 min)
Pretend the sample was built in one commit named "invoice stuff." Break that into the commit history it should have had: at least 3 commits, each one-intent, each passing tests on its own. Write each commit subject and a one-line body.
Skill targeted: narrative Git history.
Kata 6: One-Page ADR in 15 Minutes (15 min)
Pick a one-way door inside this code (e.g., currency conversion strategy, rounding policy, whether send() is synchronous). Write an ADR in the full Context / Decision / Alternatives / Consequences template.
Skill targeted: tightening ADRs until they are actually one page.
Kata 7: Receive Harsh Feedback (15 min)
A reviewer writes:
"Why did you write this as a class at all? A pure function with three args would be fine. The class here is doing nothing."
Write a response that:
- acknowledges what is correct
- names the real constraint (is there one?)
- offers two options with trade-offs
- does not reflexively agree or reflexively defend
Skill targeted: separating stinging comments from factual disagreements.
Completion Checklist
- Completed at least five katas
- Logged the top three mistakes you made
- Repeated at least one kata on a different sample (e.g., code from a teammate or open-source)
- Timed each kata; none ran more than 2x its budget