You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Drafted remaining 4 sections (07-10) to complete the Architecture
Principles chapter:
- Section 07: Practical Application - Complete task management API
example applying all 5 principles with Claude Code collaboration
- Section 08: Common Pitfalls - 8 architecture anti-patterns with
examples and solutions
- Section 09: Summary - Synthesis of all 5 principles with quick
reference checklist
- Section 10: Further Reading - Curated resources including book
references, tools, specs, and community links
Chapter now complete with 10 sections, 8 Mermaid diagrams total.
Updated tasks.md to mark P1-004 complete (11/81 tasks done).
Fixed broken links in sections 09 and 10 (pointed to unwritten chapters).
Note: Some markdown lint issues remain in section 06 (written earlier)
that need to be addressed separately in a cleanup pass.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
[Placeholder: 1-2 sentence summary of this section's content
20
-
for search and navigation purposes. To be written during drafting.]
19
+
Explains why testability is critical for agentic development and how to
20
+
architect systems that enable fast, reliable testing of AI-generated code.
21
+
Covers dependency injection, pure functions, and test boundaries.
21
22
---
22
23
23
-
[Placeholder: Explain why testability is critical for agentic development.
24
-
When AI generates code, you need fast, reliable tests to validate correctness.
25
-
Architecture must enable easy testing - dependency injection, pure functions, mocked boundaries.
26
-
Show examples of testable vs. untestable architectures and their impact on AI development velocity.
27
-
~3-5 pages.]
24
+
# Testability: Validating AI-Generated Code
28
25
29
-
**Testability principles**:
26
+
When AI generates code 10x faster than humans, what becomes the bottleneck? Testing. You can ask Claude Code to build an entire REST API in 30 minutes, but how do you know it works correctly? How do you validate that the AI understood your requirements and implemented them faithfully?
30
27
31
-
-[Principle 1: Dependency injection for mocking]
32
-
-[Principle 2: Pure functions and predictable behavior]
33
-
-[Principle 3: Test boundaries aligned with component boundaries]
34
-
-[Principle 4: Fast feedback loops]
28
+
The answer isn't "test less." It's **architect for testability from the start**. When your system is designed to be easily tested, you can iterate with AI agents at high velocity while maintaining confidence that the code does what it's supposed to do.
35
29
36
-
[Additional subsections on testability patterns]
30
+
## Why Testability is Critical for Agentic Development
31
+
32
+
In traditional development, you write code slowly and carefully, testing as you go. You intimately understand every line because you wrote it. With AI-generated code, you're reviewing hundreds or thousands of lines you didn't write. You need a different validation strategy.
33
+
34
+
Consider this scenario: You prompt Claude Code to implement user authentication. Five minutes later, you have a complete implementation with password hashing, session management, JWT tokens, and email verification. It looks sophisticated. But does it work? Are there security vulnerabilities? Does it handle edge cases?
35
+
36
+
Without good tests, you're forced to:
37
+
-**Manually test every scenario** (slow and error-prone)
38
+
-**Read and understand all the generated code** (defeats the velocity gains)
39
+
-**Hope the AI got it right** (dangerous)
40
+
41
+
With a testable architecture, you can:
42
+
-**Run automated tests in seconds** and know if it works
43
+
-**Trust the AI-generated code** if tests pass
44
+
-**Iterate quickly** by regenerating implementations and re-running tests
45
+
46
+
Testability transforms AI code generation from "risky and slow to validate" to "fast and verifiable."
47
+
48
+
## The Four Pillars of Testable Architecture
49
+
50
+
### 1. Dependency Injection: Control Your Collaborators
51
+
52
+
The most powerful testability pattern is **dependency injection**—providing dependencies from outside rather than creating them inside your code. This lets you swap real implementations with test doubles (mocks, stubs, fakes).
53
+
54
+
**Untestable approach:**
55
+
56
+
```python
57
+
classUserService:
58
+
defcreate_user(self, email, password):
59
+
# Hard-coded dependency - can't test without real DB
60
+
db = PostgresDatabase("production-db-url")
61
+
hashed = bcrypt.hash(password)
62
+
user_id = db.insert("users", {
63
+
"email": email,
64
+
"password_hash": hashed
65
+
})
66
+
67
+
# Hard-coded email service - sends real emails in tests!
The test runs in **milliseconds**, requires **no infrastructure**, and **verifies the logic** completely.
119
+
120
+
When you prompt an AI agent to implement a feature, you can specify: "Use dependency injection for all external services." The AI will generate testable code, and you can validate it immediately with tests.
121
+
122
+
### 2. Pure Functions: Predictable and Easy to Test
123
+
124
+
A **pure function** is one that:
125
+
-**Always returns the same output for the same inputs** (deterministic)
126
+
-**Has no side effects** (doesn't modify external state, doesn't do I/O)
127
+
128
+
Pure functions are the easiest code to test because you just call them with inputs and check outputs. No setup, no mocking, no cleanup.
discount_rate: Discount as decimal (e.g., 0.1 for 10%)
167
+
168
+
Returns:
169
+
Total price with tax and discount applied
170
+
"""
171
+
subtotal =sum(item.price for item in items)
172
+
with_tax = subtotal * (1+ tax_rate)
173
+
with_discount = with_tax * (1- discount_rate)
174
+
returnround(with_discount, 2)
175
+
```
176
+
177
+
Now the test is trivial:
178
+
179
+
```python
180
+
deftest_calculate_total_price():
181
+
items = [
182
+
Item(price=10.00),
183
+
Item(price=20.00)
184
+
]
185
+
186
+
total = calculate_total_price(
187
+
items=items,
188
+
tax_rate=0.08,
189
+
discount_rate=0.1
190
+
)
191
+
192
+
# 30.00 + 8% tax = 32.40, - 10% discount = 29.16
193
+
assert total ==29.16
194
+
```
195
+
196
+
No setup, no mocking, just inputs and outputs. You can test dozens of scenarios in seconds.
197
+
198
+
**Architectural principle**: Push impure operations (I/O, state mutation, time) to the edges of your system. Keep the core logic pure. This makes the bulk of your code trivially testable.
199
+
200
+
### 3. Test Boundaries Aligned with Component Boundaries
201
+
202
+
Remember component decomposition from earlier sections? Well-designed component boundaries make natural test boundaries. Each component should be testable independently.
203
+
204
+
```mermaid
205
+
graph TD
206
+
A[Shopping Cart Feature] --> B[Cart Service]
207
+
A --> C[Inventory Service]
208
+
A --> D[Pricing Service]
209
+
210
+
B -.->|test boundary| B1[Cart Service Tests]
211
+
C -.->|test boundary| C1[Inventory Service Tests]
212
+
D -.->|test boundary| D1[Pricing Service Tests]
213
+
214
+
A -.->|integration test| A1[Cart Feature Integration Tests]
215
+
216
+
style B1 fill:#e1f5e1
217
+
style C1 fill:#e1f5e1
218
+
style D1 fill:#e1f5e1
219
+
style A1 fill:#fff4e1
220
+
```
221
+
222
+
*Figure 3.6: Component boundaries define test boundaries. Each component has focused unit tests, while integration tests verify components work together.*
223
+
224
+
When components have:
225
+
-**Clear interfaces**: Easy to mock for testing
226
+
-**Single responsibility**: Tests focus on one thing
227
+
-**Explicit dependencies**: Easy to inject test doubles
228
+
229
+
Then you can:
230
+
-**Test each component in isolation** (fast unit tests)
231
+
-**Test integrations between components** (targeted integration tests)
232
+
-**Know exactly what broke** when tests fail (precise test boundaries)
233
+
234
+
### 4. Fast Feedback Loops
235
+
236
+
The final pillar is **speed**. Tests must be fast enough to run constantly during development. If tests take 10 minutes to run, you won't run them frequently. If they take 10 seconds, you'll run them after every change.
237
+
238
+
**Slow tests kill agentic development velocity.** You iterate with AI, generate code, run tests, iterate again. This cycle needs to be measured in seconds, not minutes.
With fast unit tests, you can run hundreds of tests in seconds. This enables:
277
+
-**Continuous testing** while developing with AI
278
+
-**Immediate feedback** when something breaks
279
+
-**Confidence to iterate rapidly** knowing tests will catch regressions
280
+
281
+
## Testability Checklist for Agentic Development
282
+
283
+
When architecting a new system or feature, verify:
284
+
285
+
-[ ]**Dependencies are injected**, not hard-coded
286
+
-[ ]**Core logic is pure functions** where possible
287
+
-[ ]**I/O and state changes** are pushed to boundaries
288
+
-[ ]**Components are independently testable**
289
+
-[ ]**Interfaces are mockable** for testing
290
+
-[ ]**Tests run in milliseconds**, not seconds or minutes
291
+
-[ ]**Test coverage includes edge cases** and error conditions
292
+
-[ ]**AI agents can generate tests** from specifications
293
+
294
+
When prompting AI to implement features, include testability requirements:
295
+
296
+
> "Implement user authentication with dependency injection for database and email services. Write pure functions for password hashing and validation. Include unit tests for all logic."
297
+
298
+
The AI will generate testable code, and you can validate it immediately.
299
+
300
+
## The Testability-Velocity Feedback Loop
301
+
302
+
Here's the beautiful thing about testable architecture: it creates a positive feedback loop with AI development velocity.
303
+
304
+
1.**Testable code → Fast test execution** (seconds, not minutes)
305
+
2.**Fast tests → Frequent testing** (after every AI iteration)
**Mistake 2: Skipping tests because "AI code is usually correct"**
344
+
345
+
AI code is usually *plausible*, not necessarily correct. It can have subtle bugs, security issues, or misunderstand requirements. Tests catch these.
346
+
347
+
**Mistake 3: Over-mocking to the point of meaningless tests**
348
+
349
+
```python
350
+
# Bad: Mocks so much that test is meaningless
351
+
deftest_create_user():
352
+
mock_service = Mock()
353
+
mock_service.create_user.return_value =123
354
+
355
+
# This always passes - you're testing the mock!
356
+
result = mock_service.create_user("email", "pass")
357
+
assert result ==123
358
+
```
359
+
360
+
Only mock external dependencies (databases, APIs, etc.). Test your own logic for real.
361
+
362
+
## What's Next
363
+
364
+
You now understand how to architect for testability—dependency injection, pure functions, aligned boundaries, and fast feedback. But testability alone isn't enough. You need to apply these principles systematically across your entire system.
365
+
366
+
In the next section, [Practical Application](./07-practical-application.md), we'll walk through architecting a complete system using all the principles from this chapter: digestibility, decomposition, interfaces, separation of concerns, and testability. You'll see how these principles work together in practice.
0 commit comments