When NOT to Use AI for Coding: Critical Scenarios

# When NOT to Use AI: Pitfalls and Anti-Patterns You've probably heard the hype: AI coding assistants can write entire applications, debug complex issues, and make you 10x more productive. While AI tools like GitHub Copilot, Cursor, and ChatGPT are genuinely powerful, they're not magic wands. In fact, misusing them can slow you down, introduce bugs, and prevent you from growing as a developer. Let's explore the situations where AI assistance becomes a liability rather than an asset, and learn to recognize these anti-patterns before they bite you. ## The "Copy-Paste Without Understanding" Trap This is the most common—and most dangerous—mistake developers make with AI tools. ### The Problem When AI generates code that "just works," it's tempting to paste it directly into your project and move on. But code you don't understand is a ticking time bomb. ```python # AI suggested this for handling user authentication @app.route('/login', methods=['POST']) def login(): user = User.query.filter_by(username=request.form['username']).first() if user and check_password_hash(user.password, request.form['password']): session['user_id'] = user.id return redirect(url_for('dashboard')) return render_template('login.html', error='Invalid credentials') ``` Looks reasonable, right? But can you spot the issues? - No CSRF protection - Vulnerable to timing attacks - No rate limiting (brute force risk) - Session fixation vulnerability - No input validation ### The Fix Always treat AI-generated code as a **starting point**, not a finished product. Here's a better approach: ```python # After understanding and improving the AI suggestion from flask_wtf.csrf import CSRFProtect from flask_limiter import Limiter import secrets @app.route('/login', methods=['POST']) @limiter.limit("5 per minute") # Rate limiting def login(): # CSRF protection handled by Flask-WTF username = request.form.get('username', '').strip() password = request.form.get('password', '') # Input validation if not username or not password: return render_template('login.html', error='All fields required') user = User.query.filter_by(username=username).first() # Constant-time comparison to prevent timing attacks if user and secrets.compare_digest( user.password.encode('utf-8'), check_password_hash(user.password, password).encode('utf-8') ): # Regenerate session to prevent fixation session.regenerate() session['user_id'] = user.id return redirect(url_for('dashboard')) return render_template('login.html', error='Invalid credentials') ``` **Action Step**: Before accepting any AI suggestion, ask yourself: "Can I explain what each line does and why it's necessary?" ## Learning Fundamentals: When AI Actually Hurts AI can become a crutch that prevents you from developing essential skills. ### Situations Where You Should Struggle **1. Learning New Concepts** If you're learning recursion, SQL joins, or async programming for the first time, resist the urge to ask AI for solutions immediately. The struggle is where learning happens. ```javascript // DON'T immediately ask AI: "Write a function to flatten a nested array" // DO try it yourself first, even if it takes an hour // Your first attempt might be messy: function flattenArray(arr) { let result = []; for (let i = 0; i < arr.length; i++) { if (Array.isArray(arr[i])) { // Hmm, how do I handle this? // Let me think... } } } ``` After wrestling with the problem, THEN use AI to: - Check your solution - Learn alternative approaches - Understand trade-offs **2. Debugging Your Own Logic** Before asking AI "why doesn't this work?", practice systematic debugging: ```javascript // Your code that isn't working function calculateDiscount(price, discount) { return price - (price * discount); } console.log(calculateDiscount(100, 20)); // Expected: 80, Got: 1980 ``` **Good debugging practice:** 1. Add console.logs to see actual values 2. Check your assumptions 3. Read error messages carefully 4. Use the debugger ```javascript function calculateDiscount(price, discount) { console.log('Price:', price, 'Discount:', discount); // Ah! discount is 20, not 0.20 const discountPercent = discount / 100; return price - (price * discountPercent); } ``` **When to use AI**: After you've attempted debugging yourself, use AI to explain WHY your logic was wrong, not just to get a fix. ## The Over-Architecture Anti-Pattern AI tools often suggest enterprise-level patterns for simple problems. This is particularly common with ChatGPT and Claude. ### The Problem You ask: "How do I store user preferences?" AI suggests: A complete microservices architecture with message queues, event sourcing, and CQRS patterns. ```typescript // AI's overcomplicated suggestion for a simple todo app interface IUserPreferenceRepository { getPreference(userId: string, key: string): Promise; setPreference(userId: string, key: string, value: any): Promise; } class UserPreferenceService { constructor( private repository: IUserPreferenceRepository, private eventBus: IEventBus, private cache: ICache ) {} async updatePreference(command: UpdatePreferenceCommand): Promise { const event = new PreferenceUpdatedEvent(command); await this.eventBus.publish(event); await this.cache.invalidate(`pref:${command.userId}`); // ... 50 more lines } } ``` ### The Fix For a small todo app, you probably just need: ```typescript // Simple and appropriate solution const userPreferences = { theme: localStorage.getItem('theme') || 'light', sortBy: localStorage.getItem('sortBy') || 'date' }; function updatePreference(key: string, value: string) { localStorage.setItem(key, value); userPreferences[key] = value; } ``` **Rule of thumb**: Start simple. Add complexity only when you have a concrete need, not because AI suggested it. ## Security and Compliance: High-Stakes Situations AI tools are trained on public code, which often contains security vulnerabilities and outdated practices. ### Never Trust AI Blindly For: **1. Authentication and Authorization** ```python # AI might suggest this for API authentication def requires_auth(f): @wraps(f) def decorated(*args, **kwargs): token = request.headers.get('Authorization') if token == 'secret-key-123': # NEVER do this! return f(*args, **kwargs) return jsonify({'error': 'Unauthorized'}), 401 return decorated ``` Problems: - Hardcoded credentials - No token expiration - No encryption - Vulnerable to replay attacks **2. Data Privacy and GDPR Compliance** ```javascript // AI suggestion that violates GDPR function trackUser(userId, action) { analytics.track({ userId: userId, email: user.email, // PII without consent! ipAddress: request.ip, action: action, timestamp: Date.now() }); } ``` **Action step**: For security-critical code, always: 1. Consult official documentation 2. Follow established security frameworks (OWASP, etc.) 3. Have security-focused code review 4. Never assume AI knows your compliance requirements ## The "It Works On My Machine" Syndrome AI doesn't know your production environment, infrastructure constraints, or deployment context. ### Environment-Specific Issues ```python # AI suggests this for file uploads @app.route('/upload', methods=['POST']) def upload_file(): file = request.files['file'] file.save(f'/uploads/{file.filename}') # Multiple problems! return 'File uploaded' ``` Issues AI doesn't consider: - Your production server might not have write access to `/uploads/` - File name conflicts - Directory traversal vulnerabilities - No file size limits (DoS risk) - Not handling cloud storage (S3, etc.) ### Better Approach ```python import os from werkzeug.utils import secure_filename from flask import current_app @app.route('/upload', methods=['POST']) def upload_file(): if 'file' not in request.files: return 'No file provided', 400 file = request.files['file'] # Check file size (use your config) file.seek(0, os.SEEK_END) size = file.tell() if size > current_app.config['MAX_FILE_SIZE']: return 'File too large', 413 file.seek(0) # Secure filename and use configured upload folder filename = secure_filename(file.filename) filepath = os.path.join( current_app.config['UPLOAD_FOLDER'], f"{uuid.uuid4()}_{filename}" ) file.save(filepath) return {'filename': filename}, 201 ``` ## Performance and Scale: AI Doesn't Know Your Traffic AI suggestions often ignore performance implications. ```javascript // AI might suggest this for a product listing page async function getProducts(req, res) { const products = await Product.findAll(); // Fetch reviews for each product for (let product of products) { product.reviews = await Review.findAll({ where: { productId: product.id } }); } res.json(products); } ``` This creates an N+1 query problem. With 100 products, that's 101 database queries! **Better approach:** ```javascript async function getProducts(req, res) { // Single query with JOIN const products = await Product.findAll({ include: [{ model: Review, attributes: ['rating', 'comment'], limit: 5 // Only recent reviews }], limit: 20, // Pagination offset: req.query.page * 20 }); res.json(products); } ``` ## When AI Shines: The Right Use Cases To balance this out, here are situations where AI IS incredibly helpful: 1. **Boilerplate code**: Config files, standard CRUD operations 2. **Code translation**: Converting between languages or frameworks 3. **Test generation**: Creating test cases for existing code 4. **Documentation**: Writing comments and README files 5. **Refactoring**: Suggesting cleaner code structure 6. **Learning**: Explaining complex code or concepts ## Building Good Habits Here's a practical workflow that balances AI assistance with skill development: ### The 30-30-30 Rule 1. **30% of the time**: Solve problems without AI first 2. **30% of the time**: Use AI as a pair programmer, iterating together 3. **30% of the time**: Use AI to check your work and learn alternatives 4. **10% of the time**: Copy-paste AI solutions (only for well-understood boilerplate) ### Code Review Checklist for AI-Generated Code Before merging AI suggestions, ask: - [ ] Do I understand every line? - [ ] Does it handle edge cases? - [ ] Are there security implications? - [ ] Is it testable? - [ ] Does it fit our architecture? - [ ] Is it maintainable by others? - [ ] Does it meet performance requirements? ## Conclusion AI coding assistants are powerful tools, but they're tools, not replacements for developer judgment. The developers who thrive with AI aren't those who use it the most—they're those who know when NOT to use it. Avoid blindly accepting suggestions, especially for security, architecture decisions, or when learning new concepts. Use AI to accelerate your work, not to bypass understanding. Remember: AI can write code, but it can't take responsibility for production outages, security breaches, or technical debt. That's still on you. **Next Steps**: - Review your recent AI-assisted code with the checklist above - Identify one area where you've been over-reliant on AI (check out our lesson on [over-reliance](/lessons/over-reliance)) - Practice the 30-30-30 rule for the next week The goal isn't to avoid AI—it's to use it wisely. Master that, and you'll be a better developer than those who work without AI OR those who can't work without it.