Phase 1: Test-Driven AI Development · 50 min · Python · pytest · Hypothesis
Behavioral Contracts: Constraining AI Code Generation
Contracts are the guardrails. The AI can drive, but it stays on the road.
Hiring signal: Engineers who can define behavioral contracts with preconditions, postconditions, and invariants demonstrate design-by-contract thinking -- a skill that becomes critical when AI generates the implementation and the contract is the only automated check that the implementation is correct.
What you will learn
- Define behavioral contracts with preconditions, postconditions, and invariants
- Express contracts as pytest fixtures and Hypothesis invariants
- Use contract violations as test failures that constrain AI code generation
- Distinguish contract-based testing from example-based testing
The Problem
An AI assistant generates a transfer_funds(from_account, to_account, amount) function. The code works: it deducts from from_account and adds to to_account. But it doesn't check that from_account != to_account (allowing self-transfers that double the balance), it doesn't check that amount > 0 (allowing negative transfers that reverse the flow), and it doesn't ensure the transfer is atomic (allowing partial transfers if the add fails after the deduct).
Example-based tests catch some of these: test_transfer_100_dollars verifies the happy path. But example-based tests are only as good as the examples the developer thinks of. A behavioral contract catches all of them automatically because it defines properties that must always hold, not specific examples.
What Is a Behavioral Contract?
A behavioral contract has three components:
| Component | What it specifies | Example |
|---|
| Preconditions | What must be true before the function is called | from_account != to_account, amount > 0, from_account.balance >= amount |
| Postconditions | What must be true after the function returns | from_account.balance + to_account.balance == original_total (conservation) |
| Invariants | What must always be true, before and after | all account balances >= 0, total system money is conserved |
The key insight: contracts are properties, not examples. Instead of testing transfer(A, B, 100) and checking the result, you define the invariant "total money is conserved" and let Hypothesis generate thousands of transfer scenarios to verify it always holds.
Contracts catch what examples miss
Example-based testing: "I tested transfer(A, B, 100) and it worked." -- But what about transfer(A, A, 100)? transfer(A, B, -50)? transfer(A, B, 999999999)? Example-based testing checks the cases you thought of. Contract-based testing checks ALL cases by verifying properties that must hold for every possible input. This is why contract-based testing is more powerful for AI code: the AI's blind spots are cases you also didn't think of, but contracts catch them automatically.
Unlock the full lesson
You've read the first 2 sections. The rest of this lesson covers Expressing Contracts in Code, Contract-Based Testing vs Example-Based Testing, Using Contracts to Prevent AI Defect Patterns, Build It — plus a hands-on lab, quiz, and project artifact.
Create a free account to unlock Phase 0 and Phase 1 of every course — no credit card.
Browse all courses · View pricing · DeVenture Academy