Section 04: Anti-Patterns & Common Mistakes
Learn from others’ mistakes! This guide shows you what NOT to do when working with Claude Code, and how to do it right instead.
Prompting Anti-Patterns
Overview: These are the most common mistakes when prompting Claude. The first 5 are critical - avoid these at all costs.
❌ Anti-Pattern 1: Vague Requests
The Mistake:
claude "make it better"
claude "fix this"
claude "improve performance"
claude "refactor the code"
Why It’s Bad:
- Claude has to guess what you want
- Results are inconsistent
- Often misses your actual intent
- Wastes time with back-and-forth
Do This Instead:
# Specific, measurable, actionable
claude "Refactor @src/auth.js for readability:
- Extract functions over 20 lines
- Add descriptive variable names (no single letters)
- Add JSDoc comments for public methods
- Follow error handling pattern from @CLAUDE.md"
claude "Fix null pointer exception on line 45 in @src/payment.js when user.address is undefined. Add null check and return proper error."
claude "Optimize database query in @src/services/user.js:getUserOrders. Currently 1800ms, target <200ms. Use JOIN instead of N+1 queries."
Key Takeaway: Be specific about what, why, and how.
❌ Anti-Pattern 2: No Context Provided
The Mistake:
claude "add authentication"
# Which file? What pattern? What tech?
claude "the tests are failing"
# Which tests? What error? What changed?
claude "create an API endpoint"
# For what? Following what pattern?
Why It’s Bad:
- Claude invents patterns instead of following yours
- Inconsistent with your codebase
- Misses project-specific requirements
- Result doesn’t match your architecture
Do This Instead:
# Include context files
claude "Add JWT authentication to @src/routes/api.js
following pattern from @src/middleware/auth-example.js.
Use httpOnly cookies (not localStorage).
Token expiry: 24 hours.
@CLAUDE.md authentication section"
# Include error logs
npm test 2>&1 | claude "Tests failing:
[paste output]
Recent changes:
- Updated auth middleware (PR #234)
Files involved:
@src/middleware/auth.js
@tests/auth.test.js
Diagnose + fix"
# Include patterns
claude "Create REST API endpoint for products
following our pattern:
@src/routes/users.js (as template)
@CLAUDE.md API conventions
Include:
- Validation
- Error handling
- Tests"
Key Takeaway: Always include @CLAUDE.md and relevant files.
❌ Anti-Pattern 3: Everything At Once
The Mistake:
claude "Build complete e-commerce system with:
- User authentication
- Product catalog
- Shopping cart
- Payment processing
- Order management
- Inventory tracking
- Admin dashboard
- Email notifications
- Analytics
- Mobile responsiveness"
Why It’s Bad:
- Overwhelming scope
- Hard to review
- Difficult to test
- Easy to introduce bugs
- Impossible to revert if something’s wrong
Do This Instead:
# Break into phases
claude "Phase 1: User authentication
Step 1: Database schema for users
Generate migration following @migrations/pattern.sql"
# Wait, review, approve
claude "Step 2: User model
@src/models/User.js
Include: validation, password hashing"
# Continue incrementally
claude "Step 3: Auth endpoints
POST /register
POST /login
POST /logout"
# Then move to Phase 2 (products), etc.
Key Takeaway: Small, incremental steps. Commit often.
❌ Anti-Pattern 4: Ignoring Project Patterns
The Mistake:
claude "add a new feature"
# Claude invents its own pattern
# Claude generates:
function getUser(id, callback) {
db.query('SELECT * FROM users', (err, result) => {
callback(err, result);
});
}
# But your project uses async/await!
Why It’s Bad:
- Inconsistent codebase
- Harder to maintain
- Confuses team members
- Violates project standards
Do This Instead:
claude "Add getUser function following our patterns:
@CLAUDE.md (async/await, not callbacks)
@src/services/example-service.js (service pattern)
Must:
- Use async/await
- Use repository pattern
- Handle errors with AppError
- Include tests"
Key Takeaway: Always reference @CLAUDE.md patterns.
❌ Anti-Pattern 5: No Success Criteria
The Mistake:
claude "write tests"
# How many? What coverage? Which framework?
claude "optimize this"
# How much? What metric?
claude "make it secure"
# Against what threats?
Why It’s Bad:
- Unclear when “done”
- Can’t measure success
- May over/under-deliver
- Hard to verify correctness
Do This Instead:
claude "Generate tests for @src/services/payment.js:
Coverage requirements:
- Unit tests: All public methods
- Edge cases: null, undefined, empty, boundaries
- Error cases: Network timeout, DB failure
- Integration: Real DB (not mocks)
- Target: 90%+ coverage
Test framework: Jest
Pattern: @tests/example.test.js
Success criteria: All tests pass + 90% coverage"
claude "Optimize @src/api/search.js:
Current: 2000ms response time
Target: <200ms (p95)
Constraints: Can't change API response format
Measure: Time from request to response
Verify: Load test with 100 concurrent requests"
claude "Security review of @src/auth/:
Check for:
- SQL injection (parameterized queries?)
- XSS (output encoding?)
- CSRF (tokens?)
- Session fixation
- Secrets in code
Report: Severity + location + fix"
Key Takeaway: Define “done” clearly.
Workflow Anti-Patterns
❌ Anti-Pattern 6: Accepting Code Blindly
The Mistake:
claude "implement user registration"
# Claude generates 200 lines
# You click Accept without reading
# Ship to production
# 🔥 Production breaks 🔥
Why It’s Bad:
- Claude is smart but not perfect
- May misunderstand requirements
- Could have security issues
- Might break existing functionality
- You’re responsible for the code
Do This Instead:
claude "implement user registration"
# Claude generates code
# YOU REVIEW:
# 1. Read the code line by line
# 2. Check for edge cases
# 3. Verify error handling
# 4. Look for security issues
# 5. Run tests
# 6. Manual testing
# Only THEN accept
# Better: Use Plan Mode
claude --plan-mode "implement user registration"
# Review plan BEFORE implementation
# Edit plan if needed
# THEN implement
Key Takeaway: Always review. You’re the engineer, Claude is the assistant.
❌ Anti-Pattern 7: No CLAUDE.md
The Mistake:
# Project has NO CLAUDE.md
# Day 1
You: "Add API endpoint"
Claude: Creates pattern A
# Day 2
You: "Add another endpoint"
Claude: Creates pattern B (inconsistent!)
# Day 3
Teammate: "Add third endpoint"
Claude: Creates pattern C (more chaos!)
# Result: 3 different patterns, messy codebase
Why It’s Bad:
- Claude reinvents patterns every session
- No consistency across team
- Wastes time re-explaining patterns
- Knowledge not preserved
Do This Instead:
# Create CLAUDE.md (5 minutes)
cat > CLAUDE.md << 'EOF'
# MyProject
## Tech Stack
- Node.js + Express
- PostgreSQL
- Jest
## API Endpoint Pattern
```javascript
router.post('/resource',
validate(schema),
authenticate(),
async (req, res, next) => {
try {
const result = await service.create(req.body);
res.json({ success: true, data: result });
} catch (error) {
next(error);
}
}
);
Testing Pattern
Follow @tests/example.test.js Coverage target: 80%+ EOF
Now Claude uses YOUR patterns consistently
**Key Takeaway**: Create CLAUDE.md on day 1. Update it as patterns evolve.
---
### ❌ Anti-Pattern 8: Overloading Context
**The Mistake**:
```bash
claude "Review this:
@src/ (entire directory, 50+ files, 10K+ lines)
@tests/ (50+ test files)
@docs/ (20+ doc files)
@config/ (10+ config files)
Fix any issues"
Why It’s Bad:
- Slow responses (analyzing 100+ files)
- High token cost
- Claude gets overwhelmed
- Misses important details
- Hits context limits
Do This Instead:
# Target specific files
claude "Review authentication:
@src/auth/login.js
@src/middleware/auth.js
@tests/auth.test.js
Check for security issues"
# Or use focused queries
claude "Find all TODO comments in @src/auth/"
claude "Check error handling in @src/services/payment.js"
# Use grep for searching
grep -r "TODO" src/ | claude "Analyze these TODOs. Which are urgent?"
Key Takeaway: Less is more. Be targeted.
❌ Anti-Pattern 9: No Incremental Commits
The Mistake:
# Work for 8 hours
# Make 50 changes across 20 files
# One giant commit:
git commit -m "stuff"
# Problem: Can't revert part of changes
# Problem: Hard to review
# Problem: Loses history
Why It’s Bad:
- Can’t revert individual features
- Impossible to review properly
- Lost detailed history
- Hard to debug (which change broke it?)
Do This Instead:
# Commit after each logical step
# Step 1
claude "Add User model"
git add src/models/User.js
git commit -m "feat(models): add User model"
# Step 2
claude "Add user validation"
git add src/validators/user.js
git commit -m "feat(validation): add user validation"
# Step 3
claude "Add user service"
git add src/services/user-service.js
git commit -m "feat(services): add user service"
# Each step is reversible
# Each step is reviewable
# Clear history
Key Takeaway: Small commits, descriptive messages, often.
❌ Anti-Pattern 10: Ignoring Errors
The Mistake:
npm test
# ❌ 5 tests failing
# Ignore it, keep coding...
npm run lint
# ⚠️ 20 linting errors
# Ignore it...
git commit
# Ship it! 🚢
# Production: 🔥
Why It’s Bad:
- Broken tests = broken functionality
- Lint errors = code quality issues
- Small problems become big problems
- Technical debt accumulates
Do This Instead:
# Fix immediately
npm test
# ❌ 5 tests failing
npm test 2>&1 | claude "Tests failing. Diagnose + fix:
@src/implementation
@tests/failing-tests
What changed that broke them?"
# Fix before moving on
npm run lint
# ⚠️ Warnings
npm run lint 2>&1 | claude "Fix these lint issues"
# Clean code before commit
# Pre-commit checklist
# - [ ] All tests pass
# - [ ] No lint errors
# - [ ] No console.logs
# - [ ] Code reviewed
Key Takeaway: Red = Stop. Fix before proceeding.
Security Anti-Patterns
❌ Anti-Pattern 11: Storing Secrets in Code
The Mistake:
// ❌ BAD
const API_KEY = 'sk_live_abc123xyz';
const DB_PASSWORD = 'superSecret123';
const JWT_SECRET = 'mySecretKey';
// Commit to git
// Now secrets are in git history FOREVER
Why It’s Bad:
- Exposed in git history
- Visible to anyone with repo access
- Can’t rotate without code change
- Security audit nightmare
Do This Instead:
// ✅ GOOD
const API_KEY = process.env.STRIPE_API_KEY;
const DB_PASSWORD = process.env.DATABASE_PASSWORD;
const JWT_SECRET = process.env.JWT_SECRET;
// .env (NOT committed)
STRIPE_API_KEY=sk_live_abc123xyz
DATABASE_PASSWORD=superSecret123
JWT_SECRET=mySecretKey
// .env.example (committed - no real values)
STRIPE_API_KEY=sk_live_your_key_here
DATABASE_PASSWORD=your_password
JWT_SECRET=your_jwt_secret
// .gitignore
.env
Key Takeaway: Never commit secrets. Use environment variables.
❌ Anti-Pattern 12: No Input Validation
The Mistake:
// ❌ BAD
router.post('/users', async (req, res) => {
// No validation!
const user = await db.query(
`INSERT INTO users (name, email) VALUES ('${req.body.name}', '${req.body.email}')`
);
res.json(user);
});
// User sends: { name: "'; DROP TABLE users;--", email: "..." }
// SQL injection! 💀
Why It’s Bad:
- SQL injection
- XSS attacks
- Invalid data in database
- Application crashes
Do This Instead:
// ✅ GOOD
const userSchema = Joi.object({
name: Joi.string().min(2).max(50).required(),
email: Joi.string().email().required()
});
router.post('/users',
validate(userSchema), // Validate FIRST
async (req, res, next) => {
try {
// Parameterized query (prevents SQL injection)
const user = await db.query(
'INSERT INTO users (name, email) VALUES (?, ?)',
[req.body.name, req.body.email]
);
res.json({ success: true, data: user });
} catch (error) {
next(error);
}
}
);
Key Takeaway: Validate all input. Use parameterized queries.
Performance & Testing Anti-Patterns
Quick Reference Table:
| Anti-Pattern | Bad Example | Good Approach | Impact |
|---|---|---|---|
| N+1 Queries | Loop with 1 query per item | Use JOIN or batch query | 40x faster |
| No Caching | Query DB every request | Cache with Redis/Memory | 100x+ faster |
| No Tests | Ship without tests | Write tests first (TDD) | Catch bugs early |
| Test Implementation | Test internal methods | Test public behavior | Refactor-safe tests |
| SELECT * | Query all columns | Select only needed fields | 5-10x faster |
| Missing Indexes | Full table scan | Add indexes on foreign keys | 10-100x faster |
Detailed Examples
❌ Anti-Pattern: N+1 Query Problem
Bad:
// 100 users = 101 queries (1 + 100) 🐌
for (const user of users) {
user.orders = await db.query('SELECT * FROM orders WHERE user_id = ?', [user.id]);
}
Good:
// 1 query total ⚡
const result = await db.query(`
SELECT users.*, orders.* FROM users
LEFT JOIN orders ON users.id = orders.user_id
`);
❌ Anti-Pattern: No Tests
Bad:
function calculateDiscount(price, coupon) {
return price * coupon.percent / 100; // Crashes if coupon is null!
}
Good:
// Write tests FIRST
describe('calculateDiscount', () => {
it('handles null coupon', () => expect(calculateDiscount(100, null)).toBe(0));
it('calculates 10%', () => expect(calculateDiscount(100, {percent: 10})).toBe(10));
});
// THEN implement
function calculateDiscount(price, coupon) {
if (!coupon?.percent) return 0;
return price * coupon.percent / 100;
}
Quick Reference: Anti-Patterns Checklist
Before Prompting
- Is my request specific? (not “make it better”)
- Did I include context? (@CLAUDE.md, relevant files)
- Is the scope reasonable? (not “build entire app”)
- Did I reference project patterns?
- Did I define success criteria?
During Development
- Am I reviewing Claude’s code?
- Am I committing incrementally?
- Are tests passing?
- Are there lint errors?
- Is CLAUDE.md up to date?
Before Committing
- All tests pass?
- No secrets in code?
- Input validation added?
- No N+1 queries?
- Tests written?
- Code reviewed?
Common Mistakes Summary
| Anti-Pattern | Impact | Fix |
|---|---|---|
| Vague prompts | Inconsistent results | Be specific: what, why, how |
| No context | Wrong patterns | Always include @CLAUDE.md |
| Everything at once | Overwhelming, buggy | Small incremental steps |
| No patterns | Inconsistent code | Reference @CLAUDE.md patterns |
| No success criteria | Unclear “done” | Define measurable goals |
| Accept blindly | Bugs in production | Review all code |
| No CLAUDE.md | Reinvent every time | Create CLAUDE.md day 1 |
| Overload context | Slow, expensive | Target specific files |
| Giant commits | Can’t revert | Commit after each step |
| Ignore errors | Technical debt | Fix immediately |
| Secrets in code | Security breach | Use env vars |
| No validation | SQL injection, XSS | Validate all input |
| N+1 queries | Slow performance | Use JOINs |
| No caching | Database overload | Cache frequent data |
| No tests | Bugs in production | TDD: test first |
| Test implementation | Brittle tests | Test behavior |
Next Steps
- Learn Good Patterns: Prompt Engineering
- Set Up Properly: CLAUDE.md Guide
- See Real Workflows: Daily Workflows
- Review AI Code: PR Review Guide
- If setup or tooling still fails → Troubleshooting