Back to blog
Advertisement

AI in Development: The Truth About Productivity That 90% of Developers Don't Know

Hello HaWkers, in 2025 we have a fascinating situation: 90% of developers use AI tools daily, dedicating an average of 2 hours per day working with them. But here is the paradox that nobody is talking about openly: recent studies show that developers take 19% longer when using AI compared to not using it.

How is this possible? If the tools promise to make us super productive, why are many developers getting slower? The answer is not simple, but understanding it can mean the difference between using AI as a booster or as a crutch that hurts you.

The Reality of Numbers: What the Data Reveals

According to Google's 2025 DORA report and Stack Overflow surveys, we have an interesting scenario. More than 80% of developers report that AI increased their productivity. But when we objectively measure task completion time, 19% become slower.

This discrepancy reveals something important: the perception of productivity is different from actual productivity. AI tools make us feel more productive because they reduce initial cognitive friction - that moment of "where do I start?". But they can introduce new problems.

The biggest frustration, cited by 66% of developers, is dealing with "AI solutions that are almost right, but not quite". This "almost" can consume more time in debugging than writing the code from scratch would have taken.

// Common example: AI-generated code that "almost" works
// Prompt: "Create a function to fetch users from an API with retry"

async function fetchUsersWithRetry(url, maxRetries = 3) {
  // ⚠️ AI generated this - looks good at first glance
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url);
      const data = await response.json();
      return data;
    } catch (error) {
      console.log(`Attempt ${i + 1} failed`);
      // 🚨 BUG: No delay between retries!
      // 🚨 BUG: Doesn't check HTTP status (can be 404 and keep retrying)
      // 🚨 BUG: Last attempt also enters catch without re-throw
    }
  }
}

// Fixed version after reviewing the generated code
async function fetchUsersWithRetry(url, maxRetries = 3, delayMs = 1000) {
  let lastError;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url);

      // Check if response is successful
      if (!response.ok) {
        // Don't retry on client errors (4xx)
        if (response.status >= 400 && response.status < 500) {
          throw new Error(`Client error: ${response.status}`);
        }
        throw new Error(`Server error: ${response.status}`);
      }

      const data = await response.json();
      return data;

    } catch (error) {
      lastError = error;
      console.log(`Attempt ${attempt + 1}/${maxRetries} failed:`, error.message);

      // If not the last attempt, wait before trying again
      if (attempt < maxRetries - 1) {
        await new Promise(resolve => setTimeout(resolve, delayMs * (attempt + 1)));
      }
    }
  }

  // If we got here, all attempts failed
  throw new Error(`Failed after ${maxRetries} attempts: ${lastError.message}`);
}

// Usage with proper handling
try {
  const users = await fetchUsersWithRetry('https://api.example.com/users');
  console.log('Users:', users);
} catch (error) {
  console.error('Unrecoverable error:', error);
  // Implement fallback or notify user
}

This example perfectly illustrates the "almost right" problem. The AI version works in basic cases, but fails in edge cases. Identifying and fixing these problems can take more time than writing everything manually.

Advertisement

Where AI Really Shines (And Where It Fails)

Research shows clear patterns of where developers trust AI or not. Surprisingly, there is strong resistance in high-responsibility tasks:

  • 76% don't plan to use AI for deployment and monitoring
  • 69% don't plan to use for project planning
  • 87% have concerns about accuracy
  • 81% have concerns about security and privacy

On the other hand, AI is extremely useful for:

  • Writing boilerplate code and initial structures
  • Generating basic unit tests
  • Explaining legacy or unknown code
  • Suggesting refactorings and improvements
  • Documentation and comments
// Ideal use case: AI generating test structure
// You wrote this complex function:
class ShoppingCart {
  constructor() {
    this.items = [];
    this.discounts = [];
  }

  addItem(product, quantity = 1) {
    const existingItem = this.items.find(item => item.id === product.id);

    if (existingItem) {
      existingItem.quantity += quantity;
    } else {
      this.items.push({ ...product, quantity });
    }

    return this;
  }

  applyDiscount(discountCode, percentage) {
    if (this.discounts.some(d => d.code === discountCode)) {
      throw new Error('Discount already applied');
    }

    this.discounts.push({ code: discountCode, percentage });
    return this;
  }

  calculateTotal() {
    const subtotal = this.items.reduce((total, item) => {
      return total + (item.price * item.quantity);
    }, 0);

    const totalDiscount = this.discounts.reduce((total, discount) => {
      return total + (subtotal * discount.percentage / 100);
    }, 0);

    return Math.max(0, subtotal - totalDiscount);
  }
}

// AI is excellent for generating test structure
// Prompt: "Generate tests for the ShoppingCart class above"

describe('ShoppingCart', () => {
  let cart;

  beforeEach(() => {
    cart = new ShoppingCart();
  });

  describe('addItem', () => {
    it('should add new item to cart', () => {
      const product = { id: 1, name: 'Product A', price: 100 };
      cart.addItem(product);

      expect(cart.items).toHaveLength(1);
      expect(cart.items[0]).toEqual({ ...product, quantity: 1 });
    });

    it('should increment quantity of existing item', () => {
      const product = { id: 1, name: 'Product A', price: 100 };
      cart.addItem(product, 2);
      cart.addItem(product, 3);

      expect(cart.items).toHaveLength(1);
      expect(cart.items[0].quantity).toBe(5);
    });

    it('should allow method chaining', () => {
      const product1 = { id: 1, name: 'Product A', price: 100 };
      const product2 = { id: 2, name: 'Product B', price: 200 };

      const result = cart.addItem(product1).addItem(product2);

      expect(result).toBe(cart);
      expect(cart.items).toHaveLength(2);
    });
  });

  describe('applyDiscount', () => {
    it('should apply valid discount', () => {
      cart.applyDiscount('SAVE10', 10);

      expect(cart.discounts).toHaveLength(1);
      expect(cart.discounts[0]).toEqual({ code: 'SAVE10', percentage: 10 });
    });

    it('should throw error when applying duplicate discount', () => {
      cart.applyDiscount('SAVE10', 10);

      expect(() => cart.applyDiscount('SAVE10', 10)).toThrow('Discount already applied');
    });
  });

  describe('calculateTotal', () => {
    it('should calculate total without discounts', () => {
      cart.addItem({ id: 1, name: 'Product A', price: 100 }, 2);
      cart.addItem({ id: 2, name: 'Product B', price: 50 }, 1);

      expect(cart.calculateTotal()).toBe(250);
    });

    it('should calculate total with discount', () => {
      cart.addItem({ id: 1, name: 'Product A', price: 100 }, 2);
      cart.applyDiscount('SAVE20', 20);

      expect(cart.calculateTotal()).toBe(160); // 200 - 40
    });

    it('should apply multiple discounts', () => {
      cart.addItem({ id: 1, name: 'Product A', price: 100 }, 1);
      cart.applyDiscount('SAVE10', 10);
      cart.applyDiscount('SAVE5', 5);

      expect(cart.calculateTotal()).toBe(85); // 100 - 15
    });

    it('should not return negative value', () => {
      cart.addItem({ id: 1, name: 'Product A', price: 100 }, 1);
      cart.applyDiscount('SAVE200', 200);

      expect(cart.calculateTotal()).toBe(0);
    });
  });
});

In this case, AI easily saved 30-40 minutes generating a comprehensive test suite. You still need to review and adjust, but the structure is 90% ready.

ai coding assistant

Advertisement

How to Use AI Effectively: Strategies from the Top 10% Most Productive

Developers who really increase productivity with AI follow specific patterns:

1. Treat AI as Junior Developer, Not as Expert

AI-generated code should be reviewed with the same rigor as code from a junior developer. Assume there may be bugs, untreated edge cases, and anti-patterns.

// ❌ Dangerous use: accepting AI code without review
// const generatedFunction = await askAI("function to process payments");
// // Paste directly into production code - DON'T DO THIS!

// ✅ Correct use: AI as starting point
async function processPayment(userId, amount, paymentMethod) {
  // 1. Ask AI for basic structure
  // 2. Critically review each line
  // 3. Add validations that AI forgot
  // 4. Test edge cases
  // 5. Add logging and observability

  // Validations that AI often forgets
  if (!userId || typeof userId !== 'string') {
    throw new Error('Invalid userId');
  }

  if (amount <= 0 || !Number.isFinite(amount)) {
    throw new Error('amount must be positive and finite');
  }

  const validMethods = ['credit_card', 'debit_card', 'pix'];
  if (!validMethods.includes(paymentMethod)) {
    throw new Error(`Invalid paymentMethod: ${paymentMethod}`);
  }

  // Logging that AI rarely adds
  console.log(`Processing payment: user=${userId}, amount=${amount}, method=${paymentMethod}`);

  try {
    // Processing logic here
    const result = await paymentGateway.process({ userId, amount, paymentMethod });

    // AI often forgets to log success
    console.log(`Payment processed successfully: transactionId=${result.id}`);

    return result;
  } catch (error) {
    // Robust error handling that AI rarely generates correctly
    console.error(`Error processing payment: ${error.message}`, {
      userId,
      amount,
      paymentMethod,
      stack: error.stack
    });

    // Re-throw with additional context
    throw new Error(`Processing failed: ${error.message}`);
  }
}

2. Use Specific and Contextual Prompts

Effective developers don't ask generic questions. They provide complete context:

// ❌ Generic prompt
// "How to do authentication in React?"

// ✅ Specific and contextual prompt
/*
I need to implement JWT authentication in a React application using:
- Context API for global state management
- Axios for HTTP requests
- React Router v6 for protected routes
- Automatic refresh token when access token expires

Requirements:
- Store tokens in httpOnly cookies (not localStorage)
- Redirect to /login if not authenticated
- Show loading during authentication verification
- Allow logout that clears all authentication data

Generate the AuthContext structure and an example of a protected route.
*/

// With this detailed prompt, AI generates something much more useful:
import { createContext, useContext, useState, useEffect } from 'react';
import axios from 'axios';
import { useNavigate } from 'react-router-dom';

const AuthContext = createContext(null);

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const navigate = useNavigate();

  // Configure interceptor for automatic refresh
  useEffect(() => {
    const interceptor = axios.interceptors.response.use(
      response => response,
      async error => {
        const originalRequest = error.config;

        // If token expired and we haven't tried refresh yet
        if (error.response?.status === 401 && !originalRequest._retry) {
          originalRequest._retry = true;

          try {
            // Refresh token is in httpOnly cookie, sent automatically
            await axios.post('/api/auth/refresh');
            return axios(originalRequest);
          } catch (refreshError) {
            // Refresh failed, logout
            setUser(null);
            navigate('/login');
            return Promise.reject(refreshError);
          }
        }

        return Promise.reject(error);
      }
    );

    return () => axios.interceptors.response.eject(interceptor);
  }, [navigate]);

  // Check authentication on load
  useEffect(() => {
    async function checkAuth() {
      try {
        const { data } = await axios.get('/api/auth/me');
        setUser(data.user);
      } catch (error) {
        setUser(null);
      } finally {
        setLoading(false);
      }
    }

    checkAuth();
  }, []);

  const login = async (email, password) => {
    const { data } = await axios.post('/api/auth/login', { email, password });
    setUser(data.user);
    navigate('/dashboard');
  };

  const logout = async () => {
    await axios.post('/api/auth/logout');
    setUser(null);
    navigate('/login');
  };

  const value = {
    user,
    loading,
    login,
    logout,
    isAuthenticated: !!user
  };

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

export const useAuth = () => {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within AuthProvider');
  }
  return context;
};

// Protected route component
export function ProtectedRoute({ children }) {
  const { isAuthenticated, loading } = useAuth();
  const navigate = useNavigate();

  useEffect(() => {
    if (!loading && !isAuthenticated) {
      navigate('/login');
    }
  }, [isAuthenticated, loading, navigate]);

  if (loading) {
    return <div>Loading...</div>;
  }

  return isAuthenticated ? children : null;
}
Advertisement

3. Incremental Iteration Instead of Massive Generation

Instead of asking AI to generate an entire complex component, effective developers work incrementally:

// Step 1: Basic structure
// Prompt: "Create structure of a registration form with validation"

// Step 2: Add specific feature
// Prompt: "Add real-time validation while user types"

// Step 3: Refine behavior
// Prompt: "Add 500ms debounce to validation to not validate on every keystroke"

// Step 4: Improve UX
// Prompt: "Add visual feedback for each field (green/red) and specific error messages"

// Final result is much more refined than asking for everything at once

Hidden Dangers: When AI Makes You a Worse Developer

There is a real risk of skill degradation. Developers who rely too much on AI can:

  • Lose the ability to solve problems from scratch
  • Not deeply understand the code they are using
  • Develop dependency that paralyzes them when AI is not available
  • Accept sub-optimal solutions without recognizing

The solution is not to avoid AI, but to use it consciously as an acceleration tool, not a replacement for critical thinking.

The Future: Agentic AI and the Next Level

2025 marks the emergence of agentic AI - systems that not only generate code but understand entire project context, make refactorings across multiple files, and even run tests.

25% of companies are already initiating pilot projects with agentic AI for DevOps automation. This will drastically change the developer's role, but will not eliminate the need for human expertise - it will only change where it is applied.

If you are fascinated by how AI is transforming development, I recommend checking out another article: TypeScript in 2025: Why 78% of Developers Are Migrating where you will discover how modern typing tools are becoming even more powerful with AI support.

Let's go! 🦅

💻 Master JavaScript for Real

The knowledge you gained in this article is just the beginning. There are techniques, patterns, and practices that transform beginner developers into sought-after professionals.

Invest in Your Future

I have prepared complete material for you to master JavaScript:

Payment options:

  • 2x of $13.08 no interest
  • or $24.90 at sight

📖 View Complete Content

Advertisement
Previous postNext post

Comments (0)

This article has no comments yet 😢. Be the first! 🚀🦅

Add comments