On this page
  1. The problem: AI invents functions that already exist
  2. Find duplication: grep for patterns
  3. Extract: move shared code to a common module
  4. Prevention: train the AI to check first
    1. Add a standing instruction
    2. Add breadcrumb comments at the top of files
    3. When the AI duplicates anyway (it will)
  5. The extraction checklist
  6. Related topics
how-to

How to Stop AI Assistants from Duplicating Code Across Your Project

AI assistants can't see your entire codebase, so they duplicate validation, formatting, and helper functions everywhere. Here's how to find duplication, extract shared code, and prevent the AI from copying itself again.

Quick answer

  • AI assistants duplicate code because they can’t see your entire project — they rewrite functions that already exist.
  • Find duplicates: grep for similar function names and logic patterns.
  • Extract shared code into a lib/ or utils/ module. Import it. Delete the copies.
  • Add a standing instruction: “Before writing a new function, check if it already exists in utils/ or lib/.”
  • Full guide: How to Work With AI Assistants Without Creating Tech Debt

The problem: AI invents functions that already exist

# AI wrote this in users.py:
def validate_email(email):
    return "@" in email and "." in email.split("@")[1]

# Two days later, AI wrote this in orders.py:
def is_valid_email(email):
    parts = email.split("@")
    return len(parts) == 2 and "." in parts[1]

# Three weeks later, AI wrote this in settings.py:
def check_email(email_str):
    import re
    return bool(re.match(r"[^@]+@[^@]+\.[^@]+", email_str))

Three implementations. Different names, slightly different behavior. The AI doesn’t know the first two exist because it only sees the file it’s editing.

Find duplication: grep for patterns

After every AI-generated feature, search for similar code:

# Search by function name patterns
grep -r "def.*email" src/ --include="*.py"

# Search by implementation pattern (line from the AI's code)
grep -r '"@" in email' src/ --include="*.py"

# Search by similar logic
grep -r "\.split.*@" src/ --include="*.py"

If you find matches in multiple files, you have duplication. Extract it.

Extract: move shared code to a common module

# Create the shared module
mkdir -p src/utils
# src/utils/validation.py
"""Validation functions shared across the application."""
import re

def is_valid_email(email: str) -> bool:
    """Check if an email address is syntactically valid."""
    return bool(re.match(r"[^@]+@[^@]+\.[^@]+", email))

# Add other validators as needed:
# def is_valid_url(...):
# def is_valid_phone(...):

Then replace all copies with imports:

# In users.py, orders.py, settings.py — replace inline function with:
from src.utils.validation import is_valid_email

Run the tests. If they pass, delete the old implementations.

Prevention: train the AI to check first

Add a standing instruction

Create a .aidigest or .cursorrules file:

Before writing any new function, check if equivalent functionality
already exists in:
  - src/utils/
  - src/lib/

If a function already exists (even with a different name), import it
instead of rewriting it. Use grep to search: "grep -r 'def.*<concept>' src/"

Add breadcrumb comments at the top of files

# src/api/routes/users.py
# NOTE: Validation functions are in src/utils/validation.py
#       (is_valid_email, is_valid_url, is_valid_phone)
# NOTE: Formatting functions are in src/utils/formatting.py
#       (format_date, format_currency, format_phone)

The AI reads this and knows where to find shared functions before writing new ones.

When the AI duplicates anyway (it will)

Accept that it’ll happen. The fix isn’t prevention — it’s detection:

  1. After every AI session, grep for new function definitions
  2. Check if any match patterns from other files
  3. Extract duplicates immediately

This is a 60-second check that prevents weeks of divergence.

The extraction checklist

When you find duplication, extract it correctly:

  1. Choose the best implementation — not necessarily the first one
  2. Give it a clear, searchable nameis_valid_email, not check
  3. Put it in the right moduleutils/validation.py, not utils/misc.py
  4. Replace all callers — use grep to find every reference
  5. Delete the originals — don’t leave them “for reference”
  6. Run tests — verify behavior didn’t change

Where this bites vibecoders

The AI generates three identical-but-different get_user() functions across three files. The vibecoder changes the auth logic in one, ships it, and discovers the other two still use the old logic. Users get inconsistent behavior depending on which endpoint they hit. Extracting shared code the moment duplication appears prevents this class of bug entirely.


Share: