mirror of
https://github.com/multica-ai/andrej-karpathy-skills.git
synced 2026-06-23 12:05:14 +02:00
Add real-world code examples demonstrating principles of effective coding, highlighting common mistakes and their corrections.
523 lines
14 KiB
Markdown
523 lines
14 KiB
Markdown
# Examples
|
|
|
|
Real-world code examples demonstrating the four principles. Each example shows what LLMs commonly do wrong and how to fix it.
|
|
|
|
---
|
|
|
|
## 1. Think Before Coding
|
|
|
|
### Example 1: Hidden Assumptions
|
|
|
|
**User Request:** "Add a feature to export user data"
|
|
|
|
**❌ What LLMs Do (Wrong Assumptions)**
|
|
|
|
```python
|
|
def export_users(format='json'):
|
|
"""Export all users to a file."""
|
|
users = User.query.all()
|
|
|
|
if format == 'json':
|
|
with open('users.json', 'w') as f:
|
|
json.dump([u.to_dict() for u in users], f)
|
|
elif format == 'csv':
|
|
with open('users.csv', 'w') as f:
|
|
writer = csv.DictWriter(f, fieldnames=['id', 'email', 'name'])
|
|
writer.writeheader()
|
|
writer.writerows([u.to_dict() for u in users])
|
|
|
|
return f"Exported {len(users)} users"
|
|
```
|
|
|
|
**Problems:**
|
|
- Assumed it should export ALL users (what about pagination? privacy?)
|
|
- Assumed file location without asking
|
|
- Assumed which fields to include
|
|
- Assumed CSV fieldnames without checking actual data structure
|
|
|
|
**✅ What Should Happen (Surface Assumptions)**
|
|
|
|
```
|
|
Before implementing, I need to clarify:
|
|
|
|
1. **Scope**: Export all users or filtered subset? (privacy implications)
|
|
2. **Format**: You mentioned "export" - did you mean:
|
|
- Download file in browser?
|
|
- Background job with email notification?
|
|
- API endpoint returning data?
|
|
3. **Fields**: Which user fields? (some might be sensitive)
|
|
4. **Volume**: How many users typically? (affects approach)
|
|
|
|
Simplest approach: Add an API endpoint that returns paginated JSON.
|
|
Would need more info for file-based exports.
|
|
|
|
What's your preference?
|
|
```
|
|
|
|
### Example 2: Multiple Interpretations
|
|
|
|
**User Request:** "Make the search faster"
|
|
|
|
**❌ What LLMs Do (Pick Silently)**
|
|
|
|
```python
|
|
# Adds caching, database indexes, and async processing
|
|
# without asking which "faster" matters
|
|
@lru_cache(maxsize=1000)
|
|
async def search(query: str) -> List[Result]:
|
|
# ... 200 lines of optimization code
|
|
```
|
|
|
|
**✅ What Should Happen**
|
|
|
|
```
|
|
"Make the search faster" could mean:
|
|
|
|
1. **Faster response time** (< 100ms instead of 500ms)
|
|
- Add database indexes
|
|
- Cache frequent queries
|
|
- Estimated effort: 2 hours
|
|
|
|
2. **Handle more concurrent searches** (throughput)
|
|
- Add async/await
|
|
- Connection pooling
|
|
- Estimated effort: 4 hours
|
|
|
|
3. **Faster perceived speed** (UX)
|
|
- Show partial results immediately
|
|
- Progressive loading
|
|
- Estimated effort: 3 hours
|
|
|
|
Current search takes ~500ms for typical queries.
|
|
Which aspect matters most for your use case?
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Simplicity First
|
|
|
|
### Example 1: Over-abstraction
|
|
|
|
**User Request:** "Add a function to calculate discount"
|
|
|
|
**❌ What LLMs Do (Overengineered)**
|
|
|
|
```python
|
|
from abc import ABC, abstractmethod
|
|
from enum import Enum
|
|
from typing import Protocol, Union
|
|
from dataclasses import dataclass
|
|
|
|
class DiscountStrategy(ABC):
|
|
@abstractmethod
|
|
def calculate(self, amount: float) -> float:
|
|
pass
|
|
|
|
class PercentageDiscount(DiscountStrategy):
|
|
def __init__(self, percentage: float):
|
|
self.percentage = percentage
|
|
|
|
def calculate(self, amount: float) -> float:
|
|
return amount * (self.percentage / 100)
|
|
|
|
class FixedDiscount(DiscountStrategy):
|
|
def __init__(self, fixed_amount: float):
|
|
self.fixed_amount = fixed_amount
|
|
|
|
def calculate(self, amount: float) -> float:
|
|
return min(self.fixed_amount, amount)
|
|
|
|
@dataclass
|
|
class DiscountConfig:
|
|
strategy: DiscountStrategy
|
|
min_purchase: float = 0.0
|
|
max_discount: float = float('inf')
|
|
|
|
class DiscountCalculator:
|
|
def __init__(self, config: DiscountConfig):
|
|
self.config = config
|
|
|
|
def apply_discount(self, amount: float) -> float:
|
|
if amount < self.config.min_purchase:
|
|
return 0
|
|
discount = self.config.strategy.calculate(amount)
|
|
return min(discount, self.config.max_discount)
|
|
|
|
# Usage requires 30+ lines of setup for a simple calculation
|
|
```
|
|
|
|
**✅ What Should Happen (Simple)**
|
|
|
|
```python
|
|
def calculate_discount(amount: float, percent: float) -> float:
|
|
"""Calculate discount amount. percent should be 0-100."""
|
|
return amount * (percent / 100)
|
|
|
|
# Usage
|
|
discount = calculate_discount(100.0, 10.0) # $10 off
|
|
```
|
|
|
|
**When to add complexity:** Only when you actually need multiple discount types. If that requirement comes later, refactor then.
|
|
|
|
### Example 2: Speculative Features
|
|
|
|
**User Request:** "Save user preferences to database"
|
|
|
|
**❌ What LLMs Do (Unnecessary Features)**
|
|
|
|
```python
|
|
class PreferenceManager:
|
|
def __init__(self, db, cache=None, validator=None):
|
|
self.db = db
|
|
self.cache = cache or InMemoryCache()
|
|
self.validator = validator or DefaultValidator()
|
|
|
|
def save(self, user_id: int, prefs: dict,
|
|
merge: bool = True,
|
|
validate: bool = True,
|
|
notify: bool = False) -> bool:
|
|
"""Save preferences with optional merging, validation, and notifications."""
|
|
|
|
if validate:
|
|
errors = self.validator.validate(prefs)
|
|
if errors:
|
|
raise ValidationError(errors)
|
|
|
|
existing = self.get(user_id) if merge else {}
|
|
|
|
if merge:
|
|
existing.update(prefs)
|
|
prefs = existing
|
|
|
|
self.db.execute(
|
|
"UPDATE users SET preferences = ? WHERE id = ?",
|
|
(json.dumps(prefs), user_id)
|
|
)
|
|
|
|
if self.cache:
|
|
self.cache.set(f"prefs:{user_id}", prefs)
|
|
|
|
if notify:
|
|
self.notify_preference_change(user_id, prefs)
|
|
|
|
return True
|
|
|
|
def notify_preference_change(self, user_id: int, prefs: dict):
|
|
# Another 30 lines for a feature nobody asked for
|
|
pass
|
|
```
|
|
|
|
**✅ What Should Happen (Just What Was Asked)**
|
|
|
|
```python
|
|
def save_preferences(db, user_id: int, preferences: dict):
|
|
"""Save user preferences to database."""
|
|
db.execute(
|
|
"UPDATE users SET preferences = ? WHERE id = ?",
|
|
(json.dumps(preferences), user_id)
|
|
)
|
|
```
|
|
|
|
**Add later if needed:** Caching (when performance matters), validation (when bad data appears), merging (when requirement emerges).
|
|
|
|
---
|
|
|
|
## 3. Surgical Changes
|
|
|
|
### Example 1: Drive-by Refactoring
|
|
|
|
**User Request:** "Fix the bug where empty emails crash the validator"
|
|
|
|
**❌ What LLMs Do (Too Much)**
|
|
|
|
```diff
|
|
def validate_user(user_data):
|
|
- # Check email format
|
|
- if not user_data.get('email'):
|
|
+ """Validate user data."""
|
|
+ email = user_data.get('email', '').strip()
|
|
+
|
|
+ # Validate email
|
|
+ if not email:
|
|
raise ValueError("Email required")
|
|
-
|
|
- # Basic email validation
|
|
- if '@' not in user_data['email']:
|
|
+ if '@' not in email or '.' not in email.split('@')[1]:
|
|
raise ValueError("Invalid email")
|
|
|
|
- # Check username
|
|
- if not user_data.get('username'):
|
|
+ # Validate username
|
|
+ username = user_data.get('username', '').strip()
|
|
+ if not username:
|
|
raise ValueError("Username required")
|
|
+ if len(username) < 3:
|
|
+ raise ValueError("Username too short")
|
|
+ if not username.isalnum():
|
|
+ raise ValueError("Username must be alphanumeric")
|
|
|
|
return True
|
|
```
|
|
|
|
**Problems:**
|
|
- "Improved" email validation beyond the bug fix
|
|
- Added username validation nobody asked for
|
|
- Changed comments
|
|
- Added docstring
|
|
|
|
**✅ What Should Happen (Surgical)**
|
|
|
|
```diff
|
|
def validate_user(user_data):
|
|
# Check email format
|
|
- if not user_data.get('email'):
|
|
+ email = user_data.get('email', '')
|
|
+ if not email or not email.strip():
|
|
raise ValueError("Email required")
|
|
|
|
# Basic email validation
|
|
- if '@' not in user_data['email']:
|
|
+ if '@' not in email:
|
|
raise ValueError("Invalid email")
|
|
|
|
# Check username
|
|
if not user_data.get('username'):
|
|
raise ValueError("Username required")
|
|
|
|
return True
|
|
```
|
|
|
|
**Only changed:** The specific lines that fix empty email handling.
|
|
|
|
### Example 2: Style Drift
|
|
|
|
**User Request:** "Add logging to the upload function"
|
|
|
|
**❌ What LLMs Do (Reformat Everything)**
|
|
|
|
```diff
|
|
- def upload_file(file_path, destination):
|
|
+ def upload_file(file_path: str, destination: str) -> bool:
|
|
+ """Upload file to destination with logging."""
|
|
+ logger.info(f"Uploading {file_path} to {destination}")
|
|
+
|
|
try:
|
|
- with open(file_path, 'rb') as f:
|
|
- data = f.read()
|
|
-
|
|
- response = requests.post(destination, files={'file': data})
|
|
-
|
|
- if response.status_code == 200:
|
|
- return True
|
|
- else:
|
|
- return False
|
|
+ with open(file_path, "rb") as f:
|
|
+ data = f.read()
|
|
+ response = requests.post(destination, files={"file": data})
|
|
+ success = response.status_code == 200
|
|
+
|
|
+ if success:
|
|
+ logger.info(f"Upload successful: {file_path}")
|
|
+ else:
|
|
+ logger.error(f"Upload failed: {file_path}, status={response.status_code}")
|
|
+
|
|
+ return success
|
|
except Exception as e:
|
|
- print(f"Error: {e}")
|
|
+ logger.exception(f"Upload error for {file_path}: {e}")
|
|
return False
|
|
```
|
|
|
|
**Problems:**
|
|
- Changed quote style ('' to "")
|
|
- Added type hints nobody asked for
|
|
- Added docstring
|
|
- Reformatted whitespace
|
|
- Changed boolean return logic
|
|
|
|
**✅ What Should Happen (Match Existing Style)**
|
|
|
|
```diff
|
|
+ import logging
|
|
+
|
|
+ logger = logging.getLogger(__name__)
|
|
+
|
|
def upload_file(file_path, destination):
|
|
+ logger.info(f'Starting upload: {file_path}')
|
|
try:
|
|
with open(file_path, 'rb') as f:
|
|
data = f.read()
|
|
|
|
response = requests.post(destination, files={'file': data})
|
|
|
|
if response.status_code == 200:
|
|
+ logger.info(f'Upload successful: {file_path}')
|
|
return True
|
|
else:
|
|
+ logger.error(f'Upload failed: {file_path}, status={response.status_code}')
|
|
return False
|
|
except Exception as e:
|
|
- print(f"Error: {e}")
|
|
+ logger.exception(f'Upload error: {file_path}')
|
|
return False
|
|
```
|
|
|
|
**Matched:** Single quotes, no type hints, existing boolean pattern, spacing style.
|
|
|
|
---
|
|
|
|
## 4. Goal-Driven Execution
|
|
|
|
### Example 1: Vague vs. Verifiable
|
|
|
|
**User Request:** "Fix the authentication system"
|
|
|
|
**❌ What LLMs Do (Vague Approach)**
|
|
|
|
```
|
|
I'll fix the authentication system by:
|
|
1. Reviewing the code
|
|
2. Identifying issues
|
|
3. Making improvements
|
|
4. Testing the changes
|
|
|
|
[Proceeds to make changes without clear success criteria]
|
|
```
|
|
|
|
**✅ What Should Happen (Verifiable Goals)**
|
|
|
|
```
|
|
To "fix authentication," I need to define success criteria.
|
|
What specific issue are we solving?
|
|
|
|
If the issue is "users stay logged in after password change":
|
|
|
|
Plan:
|
|
1. Write test: Change password → verify old session invalidated
|
|
Verify: Test fails (reproduces bug)
|
|
|
|
2. Implement: Invalidate sessions on password change
|
|
Verify: Test passes
|
|
|
|
3. Check edge cases: Multiple active sessions, concurrent changes
|
|
Verify: Additional tests pass
|
|
|
|
4. Verify no regression: Existing auth tests still pass
|
|
Verify: Full test suite green
|
|
|
|
Current test coverage for auth: [shows coverage]
|
|
What's the specific auth issue you're seeing?
|
|
```
|
|
|
|
### Example 2: Multi-Step with Verification
|
|
|
|
**User Request:** "Add rate limiting to the API"
|
|
|
|
**❌ What LLMs Do (All at Once)**
|
|
|
|
```python
|
|
# Implements full rate limiting with Redis, multiple strategies,
|
|
# configuration system, and monitoring in one 300-line commit
|
|
# No clear verification steps
|
|
```
|
|
|
|
**✅ What Should Happen (Incremental with Verification)**
|
|
|
|
```
|
|
Plan for rate limiting:
|
|
|
|
1. Add basic in-memory rate limiting (single endpoint)
|
|
Verify:
|
|
- Test: 100 requests → first 10 succeed, rest get 429
|
|
- Manual: curl endpoint 11 times, see rate limit error
|
|
|
|
2. Extract to middleware (apply to all endpoints)
|
|
Verify:
|
|
- Test: Rate limits apply to /users and /posts
|
|
- Existing endpoint tests still pass
|
|
|
|
3. Add Redis backend (for multi-server)
|
|
Verify:
|
|
- Test: Rate limit persists across app restarts
|
|
- Test: Two app instances share rate limit counter
|
|
|
|
4. Add configuration (rates per endpoint)
|
|
Verify:
|
|
- Test: /search allows 10/min, /users allows 100/min
|
|
- Config file parsed correctly
|
|
|
|
Each step is independently verifiable and deployable.
|
|
Start with step 1?
|
|
```
|
|
|
|
### Example 3: Test-First Verification
|
|
|
|
**User Request:** "The sorting breaks when there are duplicate scores"
|
|
|
|
**❌ What LLMs Do (Fix Without Reproducing)**
|
|
|
|
```python
|
|
# Immediately changes sort logic without confirming the bug
|
|
def sort_scores(scores):
|
|
return sorted(scores, key=lambda x: (-x['score'], x['name']))
|
|
```
|
|
|
|
**✅ What Should Happen (Reproduce First)**
|
|
|
|
```python
|
|
# 1. First, write a test that reproduces the issue
|
|
def test_sort_with_duplicate_scores():
|
|
"""Test sorting when multiple items have same score."""
|
|
scores = [
|
|
{'name': 'Alice', 'score': 100},
|
|
{'name': 'Bob', 'score': 100},
|
|
{'name': 'Charlie', 'score': 90},
|
|
]
|
|
|
|
result = sort_scores(scores)
|
|
|
|
# The bug: order is non-deterministic for duplicates
|
|
# Run this test multiple times, it should be consistent
|
|
assert result[0]['score'] == 100
|
|
assert result[1]['score'] == 100
|
|
assert result[2]['score'] == 90
|
|
|
|
# Verify: Run test 10 times → fails with inconsistent ordering
|
|
|
|
# 2. Now fix with stable sort
|
|
def sort_scores(scores):
|
|
"""Sort by score descending, then name ascending for ties."""
|
|
return sorted(scores, key=lambda x: (-x['score'], x['name']))
|
|
|
|
# Verify: Test passes consistently
|
|
```
|
|
|
|
---
|
|
|
|
## Anti-Patterns Summary
|
|
|
|
| Principle | Anti-Pattern | Fix |
|
|
|-----------|-------------|-----|
|
|
| Think Before Coding | Silently assumes file format, fields, scope | List assumptions explicitly, ask for clarification |
|
|
| Simplicity First | Strategy pattern for single discount calculation | One function until complexity is actually needed |
|
|
| Surgical Changes | Reformats quotes, adds type hints while fixing bug | Only change lines that fix the reported issue |
|
|
| Goal-Driven | "I'll review and improve the code" | "Write test for bug X → make it pass → verify no regressions" |
|
|
|
|
## Key Insight
|
|
|
|
The "overcomplicated" examples aren't obviously wrong—they follow design patterns and best practices. The problem is **timing**: they add complexity before it's needed, which:
|
|
|
|
- Makes code harder to understand
|
|
- Introduces more bugs
|
|
- Takes longer to implement
|
|
- Harder to test
|
|
|
|
The "simple" versions are:
|
|
- Easier to understand
|
|
- Faster to implement
|
|
- Easier to test
|
|
- Can be refactored later when complexity is actually needed
|
|
|
|
**Good code is code that solves today's problem simply, not tomorrow's problem prematurely.**
|