assertive-programming-demo
v1.0.0
Published
A demonstration of Assertive Programming principles from The Pragmatic Programmer
Downloads
45
Maintainers
Readme
Assertive Programming Demo
A demonstration of Assertive Programming principles from The Pragmatic Programmer by Andrew Hunt and David Thomas. This project shows how adding runtime assertions improves reliability and debuggability in critical code paths.
What is Assertive Programming?
Assertive Programming is a defensive programming technique that uses assertions to catch bad states early. Instead of silently producing incorrect results, code with assertions fails fast and fails loud, making bugs obvious the moment they occur.
Key Concepts
- Fail Fast: Detect errors as soon as possible, close to their source
- Fail Loud: Make failures explicit and visible, not silent
- Enforce Invariants: Document and enforce assumptions about program state
- Early Detection: Catch bugs before they propagate and become harder to debug
How This Maps to "Assertive Programming" from The Pragmatic Programmer
In The Pragmatic Programmer, Hunt and Thomas describe assertions as:
"Use assertions to prevent the impossible"
This demo demonstrates several principles from the book:
"If It Can't Happen, Use Assertions to Ensure That It Won't" (Tip 33)
- We assert that payment amounts must be positive
- We assert that balances must be non-negative
- We assert that currencies must match
"Assertions and Side Effects"
- Assertions check conditions but don't modify state
- They're documentation that executes
"Leave Assertions Turned On"
- Unlike some languages where assertions are disabled in production, these always run
- This ensures bugs are caught even in production
Project Structure
.
├── src/
│ ├── assert.ts # Assertion helper functions
│ └── domain/
│ ├── payments.ts # Payments domain WITHOUT assertions
│ └── payments-assertive.ts # Payments domain WITH assertions
├── examples/
│ ├── without-assertions.ts # Demonstrates silent failures
│ ├── with-assertions.ts # Demonstrates explicit failures
│ └── compare.ts # Side-by-side comparison
├── tests/
│ ├── assert.test.ts # Tests for assertion helpers
│ ├── payments.test.ts # Tests for non-assertive version
│ └── payments-assertive.test.ts # Tests for assertive version
└── README.mdThe Domain: Payments
We use a simple payments domain to demonstrate the difference between code with and without assertions:
- Process Payment: Deduct payment from a wallet
- Transfer Money: Move money between two wallets
- Add Funds: Add money to a wallet
Installation
npm installBuilding
npm run buildRunning Examples
See Silent Failures (Without Assertions)
npm run example:withoutThis demonstrates how invalid inputs produce incorrect results silently:
- Negative payment amounts increase balance instead of decreasing
- Insufficient balance creates negative balances
- Currency mismatches are ignored
See Explicit Failures (With Assertions)
npm run example:withThis demonstrates how assertions catch invalid inputs immediately:
- Negative payment amounts throw clear errors
- Insufficient balance prevents negative balances
- Currency mismatches are caught with descriptive messages
Compare Side-by-Side
npm run example:bothShows a direct comparison of behavior with and without assertions.
Running Tests
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverageExample: The Problem Without Assertions
// WITHOUT assertions - silently produces wrong result
const wallet = { balance: 100, currency: 'USD' };
const payment = { amount: -50, currency: 'USD' }; // Negative amount!
const result = processPayment(wallet, payment);
console.log(result.balance); // 150 - WRONG! Balance increased instead of decreasedExample: The Solution With Assertions
// WITH assertions - fails fast with clear error
const wallet = createWallet(100, 'USD');
const payment = { amount: -50, currency: 'USD' }; // Negative amount!
try {
const result = processPayment(wallet, payment);
} catch (error) {
// AssertionError: Payment amount must be positive, got -50
// Bug caught immediately!
}The Assertion Helper
The core assertion helper provides:
export function assert(
condition: unknown,
message: string
): asserts condition {
if (!condition) {
throw new AssertionError(message);
}
}Plus convenient helpers:
assertPositive(value, name)- Ensures value > 0assertNonNegative(value, name)- Ensures value >= 0
Benefits of Assertive Programming
1. Early Bug Detection
- Bugs are caught immediately when they occur
- Failures happen close to the source of the problem
2. Better Debugging
- Clear error messages explain what went wrong
- Stack traces point directly to the violated invariant
3. Self-Documenting Code
- Assertions document assumptions and invariants
- The code explains what conditions must be true
4. Prevents Silent Failures
- No more incorrect results propagating through the system
- Bugs can't hide and cause mysterious behavior later
5. Confidence in Correctness
- When assertions pass, you know the invariants hold
- Valid operations work correctly; invalid ones fail explicitly
When to Use Assertions
Use assertions for:
- ✅ Preconditions: Conditions that must be true before a function runs
- ✅ Postconditions: Conditions that must be true after a function completes
- ✅ Invariants: Conditions that must always be true (class invariants, loop invariants)
- ✅ Impossible conditions: Things that "can't happen" but you want to verify
Don't use assertions for:
- ❌ User input validation (use proper validation with user-friendly errors)
- ❌ Expected error conditions (use proper error handling)
- ❌ Performance-critical paths (consider conditional compilation)
Key Takeaways
- Assertions catch bugs early - Fail fast, fail close to the source
- Assertions document assumptions - They're executable documentation
- Assertions prevent silent failures - Make bugs obvious immediately
- Valid code still works - Assertions only affect invalid inputs
References
- Hunt, Andrew, and David Thomas. The Pragmatic Programmer: Your Journey to Mastery. 20th Anniversary Edition, Addison-Wesley Professional, 2019.
- Tip 33: "If It Can't Happen, Use Assertions to Ensure That It Won't"
License
MIT
