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.

Create a free account to save this page, take notes and track your progress. Sign in

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

  1. Describe the behaviour and ask the assistant to write tests only, including edge cases.
  2. Review the tests: they are the specification. Add the cases you care about.
  3. Ask the assistant to implement until the tests pass, without changing the tests.
  4. 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:

Tests as the specificationTry it yourself
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.

Was this page helpful?