Coding with AI Assistants · Workflows That Work
Test-First With an Assistant
Write or agree the tests first, then let the assistant make them pass: you define correct, it does the typing.
The problem
You ask for a discount calculator. The assistant writes code and tests together, everything passes, and in production a 100 % coupon produces a negative total when combined with a store credit. Nobody had decided what should happen in that case; the code just did something.
Real-world analogy
Before a tailor cuts cloth, they take your measurements. The measurements are the agreement; the cutting is the work. Tests are the measurements for code: agree them first, then the assistant can cut as fast as it likes.
The idea
- Describe the behaviour and ask the assistant to write tests only, including edge cases.
- Review the tests: they are the specification. Add the cases you care about.
- Ask the assistant to implement until the tests pass, without changing the tests.
- Run the tests yourself; ask for fixes with the failing output.
This keeps you in charge of what is correct while the assistant handles how.
The code
Tests agreed first (including the combined discount case), then an implementation checked against them:
def total_cents(price, coupon_percent=0, store_credit=0):
discounted = price * (100 - coupon_percent) // 100
return max(0, discounted - store_credit) # agreed rule: never below zero
tests = [ # (price, coupon %, store credit, expected) written and reviewed before the code
(1000, 0, 0, 1000), (1000, 10, 0, 900), (1000, 0, 250, 750),
(1000, 100, 0, 0), (1000, 100, 500, 0), # the case nobody had thought about
(999, 50, 0, 499), # rounding: round down in the customer's favour
]
for price, pct, credit, expected in tests:
got = total_cents(price, pct, credit)
print(f"total_cents({price}, {pct}, {credit}) = {got}", "ok" if got == expected else f"FAIL expected {expected}")
Output
total_cents(1000, 0, 0) = 1000 ok total_cents(1000, 10, 0) = 900 ok total_cents(1000, 0, 250) = 750 ok total_cents(1000, 100, 0) = 0 ok total_cents(1000, 100, 500) = 0 ok total_cents(999, 50, 0) = 499 ok
Common mistakes
- Letting the assistant write code and tests in one step and trusting both.
- Allowing the assistant to "fix" failing tests by changing the expected values.
- Tests that only cover the examples in the request, not the edges.
- Forgetting to decide business rules (rounding, negatives) before testing them.
Key points
- Tests first: they are the specification you control.
- Review and extend the tests before any implementation.
- Tell the assistant not to change the tests, and run them yourself.
Further reading: Test-driven development (Martin Fowler) · Claude Code best practices: write tests, commit; code, iterate · pytest
Practice questions
Why write tests before asking the assistant for an implementation?
Answer
The tests become a specification you have reviewed, so correctness is defined by you, and the generated code can be checked objectively against it.
What instruction should accompany 'make these tests pass'?
Answer
Do not modify the tests; if a test seems wrong, explain why instead of changing it.
What kinds of cases should you add to AI-drafted tests?
Answer
Edge and boundary cases (zero, maximum, empty, combined rules) and the business decisions such as rounding and negatives that must be agreed explicitly.
Open in the app to mark lessons complete, take notes and track your progress.