Skip to content

Commit 607fe3b

Browse files
docs: expand anti-patterns with private state access guidance
Add 'Accessing Private State (Critical Anti-Pattern)' subsection to DESIGN_PHILOSOPHY.md under Module Development anti-patterns. Documents: - Why getattr(obj, '_private', ...) is dangerous - Code example showing the anti-pattern vs correct approach - Guidance on when internal access seems needed - Litmus test for detecting protocol violations Motivated by microsoft/amplifier-app-cli#70 which shows this anti-pattern spreading with 'Pattern from X' comments. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
1 parent 976fb87 commit 607fe3b

1 file changed

Lines changed: 28 additions & 0 deletions

File tree

docs/DESIGN_PHILOSOPHY.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,34 @@ When faced with implementation decisions, ask:
291291
❌ Failing to emit events for observable actions
292292
❌ Crashing kernel on module failure (non-interference)
293293

294+
### Accessing Private State (Critical Anti-Pattern)
295+
296+
**Never use `getattr(obj, "_private", ...)` to reach into internal state.**
297+
298+
This anti-pattern creates fragile code that breaks when internals change:
299+
300+
```python
301+
# ❌ WRONG: Unwrapping internal layers to find what you need
302+
bundle_resolver = getattr(resolver, "_bundle", resolver)
303+
activator = getattr(bundle_resolver, "_activator", None)
304+
305+
# ✅ RIGHT: Use public APIs or request the API be added
306+
activator = resolver.get_activator() # Public contract
307+
```
308+
309+
**Why this is dangerous**:
310+
1. Private attributes (`_name`) are implementation details - they change without warning
311+
2. Each unwrap layer multiplies the fragility
312+
3. "Pattern from X" comments mean the anti-pattern is spreading
313+
4. The "fix" creates more technical debt than the original problem
314+
315+
**When you need internal access**:
316+
- If the functionality should be public → Add to protocol/interface
317+
- If it shouldn't be public → Redesign your approach
318+
- Never solve "I can't access X" by reaching into `_internal_X`
319+
320+
**Litmus test**: Would this code break if the wrapped class's internal structure changed? If yes, you're depending on internals instead of contracts.
321+
294322
### In Design
295323

296324
❌ Over-general modules trying to do everything

0 commit comments

Comments
 (0)