Section 1.3.2
The Digestibility Principle: Fitting in Context Windows
Architecture Principles for Agentic Development
The Digestibility Principle: Fitting in Context Windows
You sit down to fix a bug in your company's payment processing system. You open the main payment handler file and immediately see imports from twelve different modules. To understand what happens when a payment fails, you need to trace through error handling code scattered across five files, check configuration in three more, and understand how the retry logic interacts with the database transaction manager in yet another file.
By the time you've mentally pieced together the full picture—maybe 45 minutes later—you've forgotten why you started. This is the experience of working with non-digestible code.
Now imagine handing this same task to an AI agent. The AI faces an even harder problem: it has a finite context window (typically 50,000-200,000 tokens), and once that's full, it must make decisions based on incomplete information. Unlike you, it can't spend days building mental models or rely on institutional knowledge. It either fits the relevant code in its context window, or it works blind.
This is the essence of the digestibility principle: code must be organized so that both humans and AI agents can understand and work with it within the constraints of working memory and context windows.
The Parallel Between Human and AI Constraints
Humans have working memory limitations—we can hold about 4-7 "chunks" of information in our minds at once. Experienced developers overcome this by creating mental abstractions: we think in terms of "the payment handler" or "the authentication system" rather than individual functions and variables.
AI agents have context window limitations—they can process a fixed number of tokens (roughly 4 characters per token) before they must either forget earlier information or stop processing. Modern AI agents like Claude have context windows of 200,000 tokens, which sounds enormous until you realize that's:
- Only about 50,000 lines of code
- A medium-sized codebase
- Easily exceeded by following import chains in poorly structured systems
The key insight: What helps humans manage complexity also helps AI agents work effectively. The principles are the same—clear boundaries, minimal coupling, explicit dependencies—but AI agents require us to be more disciplined about following them.
What Makes Code Digestible?
Let's define digestibility precisely:
Digestibility: The property of code being understandable and modifiable within the constraints of working memory (human) or context windows (AI), without requiring external knowledge or cross-file exploration.
A digestible component has four key characteristics:
1. Bounded Size
The component fits entirely within reasonable limits:
- Human limit: Can be understood in one sitting (~30-60 minutes)
- AI limit: Fits in context window with room for related code (~10,000-20,000 tokens)
Example - Digestible:
# payment_processor.py (~500 lines, ~2,000 tokens)
class PaymentProcessor:
"""Self-contained payment processing with clear boundaries."""
def process_payment(self, amount, method, customer_id):
"""Process payment with complete error handling."""
# Validation (50 lines)
# Payment API call (30 lines)
# Error handling (40 lines)
# Persistence (30 lines)
# Notification (20 lines)
pass
Example - Non-Digestible:
# payment_controller.py (imports from 12 files)
from payment.validators import *
from payment.processors import *
from payment.errors import *
from payment.notifications import *
from payment.retry import *
from payment.logging import *
# ... 6 more imports
# Logic scattered across all imported files
# Total: 5,000+ lines across 12 files (~20,000 tokens)
2. Clear Boundaries
The component has explicit inputs, outputs, and dependencies:
- Inputs: Clear parameters or configuration
- Outputs: Explicit return values or side effects
- Dependencies: Minimal and explicit (not hidden in nested imports)
When an AI agent (or human) reads a digestible component, they can immediately answer:
- What does this do?
- What does it need to work?
- What does it produce?
- What can break?
3. Self-Contained Logic
The component contains all the logic needed to understand and modify it:
- Business rules are co-located with the code that uses them
- Error handling is in the same file as the operations that can fail
- Comments and documentation are inline
- No hidden dependencies on global state or external files
Digestible Example:
class OrderValidator:
"""Validates orders with all rules co-located."""
# Business rules are here, not in a separate file
MIN_ORDER_AMOUNT = 10.00
MAX_ORDER_ITEMS = 50
ALLOWED_REGIONS = ['US', 'CA', 'MX']
def validate(self, order):
"""Validate order - all logic in one place."""
if order.total < self.MIN_ORDER_AMOUNT:
raise ValidationError("Order below minimum")
if len(order.items) > self.MAX_ORDER_ITEMS:
raise ValidationError("Too many items")
if order.region not in self.ALLOWED_REGIONS:
raise ValidationError("Region not supported")
return True
4. Minimal Coupling
The component depends on few other components, and those dependencies are:
- Explicit: Clearly imported or injected
- Stable: Well-defined interfaces that rarely change
- Few: Ideally 3-5 direct dependencies, not 15-20
The fewer dependencies, the less code an AI agent must load into context to understand this component.
The Impact on AI Agent Effectiveness
Here's what happens when an AI agent encounters these two architectures:
flowchart TD
Start([AI Agent Receives Task])
Start --> Analyze[Analyze Codebase]
Analyze --> Decision{Architecture<br/>Digestible?}
%% Digestible Path (Left/Green)
Decision -->|Yes: Clear boundaries,<br/>self-contained| LoadFull[Load Entire Component<br/>in Context Window]
LoadFull --> UnderstandQuick[Understand Structure<br/>& Dependencies Quickly]
UnderstandQuick --> ContextOK1{Within Context<br/>Limits?}
ContextOK1 -->|Yes: ~10-20K tokens| Confident[Make Changes<br/>Confidently]
Confident --> Test1[Run Tests]
Test1 --> Success1([✓ High-Quality Output<br/>Minimal Bugs])
%% Non-Digestible Path (Right/Red)
Decision -->|No: Scattered logic,<br/>tight coupling| ReadMulti[Must Read<br/>Multiple Files]
ReadMulti --> ContextFull{Hit Context<br/>Window Limit?}
ContextFull -->|Yes: >50K tokens| PartialView[Only Partial View<br/>of System]
ContextFull -->|Try to continue| Fragment[Fragment Understanding<br/>Missing Dependencies]
PartialView --> Hesitant[Make Changes<br/>Hesitantly]
Fragment --> Hesitant
Hesitant --> Guess[Guess at Side Effects]
Guess --> Test2[Run Tests]
Test2 --> Bugs([✗ Bugs & Rework<br/>Low Confidence])
%% Styling
classDef goodPath fill:#d4edda,stroke:#28a745,stroke-width:2px
classDef badPath fill:#f8d7da,stroke:#dc3545,stroke-width:2px
classDef decision fill:#fff3cd,stroke:#ffc107,stroke-width:2px
classDef success fill:#28a745,color:#fff,stroke:#1e7e34,stroke-width:3px
classDef failure fill:#dc3545,color:#fff,stroke:#bd2130,stroke-width:3px
class LoadFull,UnderstandQuick,ContextOK1,Confident,Test1 goodPath
class ReadMulti,ContextFull,PartialView,Fragment,Hesitant,Guess,Test2 badPath
class Decision,ContextOK1,ContextFull decision
class Success1 success
class Bugs failure
Figure 3.1: How architecture digestibility affects AI agent effectiveness. Digestible architecture (green path) enables confident changes, while non-digestible architecture (red path) forces guesswork and increases bugs.
The difference is stark:
- Digestible architecture: AI loads component, understands it fully, makes changes confidently
- Non-digestible architecture: AI hits context limits, makes educated guesses, introduces bugs
Real-World Example: The 12-File Payment Handler
Let's make this concrete with a real scenario I encountered at a previous company.
Before: Non-Digestible Architecture
The payment processing system was spread across 12 files:
payment/
├── handlers.py (routing, 300 lines)
├── validators.py (validation, 250 lines)
├── processors.py (API calls, 400 lines)
├── errors.py (exceptions, 150 lines)
├── retry_logic.py (retry, 200 lines)
├── notifications.py (email/webhook, 350 lines)
├── logging.py (audit logs, 180 lines)
├── config.py (settings, 120 lines)
├── models.py (data models, 280 lines)
├── database.py (persistence, 300 lines)
├── webhooks.py (webhook handling, 220 lines)
└── utils.py (helpers, 150 lines)
Total: ~2,900 lines across 12 files ≈ 11,000 tokens
To understand a simple payment flow, an AI agent (or human) had to:
- Start in
handlers.py(routing) - Follow to
validators.py(validation rules) - Check
config.py(payment limits) - Read
processors.py(API calls) - Understand
retry_logic.py(failure handling) - Check
errors.py(exception types) - Look at
notifications.py(what happens after)
By file #5, the AI agent's context window is half full with payment code, leaving little room for understanding the broader system or making complex changes.
After: Digestible Architecture
We refactored to a single, self-contained module:
payment/
└── payment_processor.py (single file, 600 lines)
├── PaymentConfig (configuration, 50 lines)
├── PaymentValidator (validation, 80 lines)
├── PaymentProcessor (core logic, 200 lines)
├── PaymentRetry (retry logic, 100 lines)
├── PaymentNotifier (notifications, 100 lines)
└── PaymentErrors (exceptions, 70 lines)
Total: ~600 lines in 1 file ≈ 2,400 tokens
Now, an AI agent (or human) could:
- Open one file
- See the complete payment flow
- Understand all error cases
- Modify confidently
Result: Bug reports dropped 60% over the next quarter, and new features that used to take 3 days now took 1 day.
The Context Window Math
Let's be specific about the numbers:
| Component Type | Lines of Code | Tokens | Fits in AI Context? |
|---|---|---|---|
| Single digestible module | 500-1,000 | 2,000-4,000 | ✓ Yes, with room to spare |
| Medium component with deps | 2,000-3,000 | 8,000-12,000 | ✓ Yes, but tight |
| Large scattered system | 5,000+ | 20,000+ | ✗ No, requires fragmentation |
| Entire codebase | 50,000+ | 200,000+ | ✗ Never |
Claude Code's context window: 200,000 tokens Practical usable space: ~50,000 tokens (leaving room for conversation, documentation, tests)
Rule of thumb: If a component and its immediate dependencies exceed 10,000 tokens (~2,500 lines), it's not digestible. Break it down.
Digestibility is Not Just About Size
It's tempting to think digestibility is just "keep files small." But that's not quite right. A digestible component is:
- Self-contained, not just small
- Cohesive, not just brief
- Clear, not just short
You can have a 1,000-line file that's perfectly digestible (all logic for one concern) and a 200-line file that's incomprehensible (scattered concerns, hidden dependencies).
Bad small file:
# utils.py (150 lines but incomprehensible)
def process(data):
# What does this do? Need to read 5 other files to understand
return transform(validate(enrich(data)))
Good large file:
# order_processor.py (800 lines but digestible)
class OrderProcessor:
"""Complete order processing with all logic co-located.
This class handles:
- Validation (100 lines)
- Inventory check (80 lines)
- Payment processing (120 lines)
- Fulfillment (150 lines)
- Notification (100 lines)
- Error handling (150 lines)
Everything you need to understand orders is here.
"""
pass
Why This Matters More in the AI Era
Digestibility has always been good practice, but with AI agents it becomes critical for three reasons:
1. AI Can't Build Institutional Knowledge
You can spend weeks learning a codebase. An AI agent gets one shot per task. If the code isn't digestible, the AI is lost.
2. Velocity Amplifies Architecture Problems
When AI agents accelerate development 5-10x, bad architecture becomes a problem 5-10x faster. A poorly structured system that was "manageable" at human speed becomes unmaintainable at AI speed.
3. Context Windows Are Hard Limits
When a human hits cognitive load limits, they take a break and come back. When an AI hits its context window limit, it cannot continue without losing information. It's a hard wall.
Making Your Code Digestible: Quick Audit
Ask yourself these questions about any component:
- Size: Can I read and understand this file in one sitting?
- Boundaries: Are inputs, outputs, and dependencies explicit?
- Self-containment: Can I understand this without reading other files?
- Coupling: Does this depend on fewer than 5 other components?
- AI test: Could an AI agent load this entire component and all its dependencies in context?
If the answer to any is "no," you have a digestibility problem.
The Digestibility-Performance Trade-off
You might ask: "Doesn't making code digestible mean duplicating logic or missing optimization opportunities?"
Sometimes, yes. But here's the reality:
Premature abstraction kills velocity. It's better to have slight duplication in digestible components than to create intricate abstractions that require loading ten files into context to understand.
Optimize for change speed, not runtime performance. In most systems, developer velocity matters more than milliseconds of runtime. When AI agents can understand and modify code quickly, you can iterate faster.
Refactor when patterns emerge. Once you see the same logic in three places, then abstract it. Not before.
Summary: The Digestibility Principle
The digestibility principle states that architecture must enable both humans and AI agents to understand and modify code within the constraints of working memory and context windows.
Key characteristics of digestible code:
- Bounded size: Fits in context (~10,000-20,000 tokens)
- Clear boundaries: Explicit inputs, outputs, dependencies
- Self-contained logic: All related code co-located
- Minimal coupling: Few, stable dependencies
Why it matters:
- AI agents can't build institutional knowledge
- Velocity amplifies architecture problems
- Context windows are hard limits
The payoff: When code is digestible, both humans and AI agents work faster, make fewer mistakes, and ship higher quality products.
In the next section, we'll explore how to achieve digestibility through component decomposition—the art of breaking systems into independently understandable pieces.