When Compression Backfires

Module 13: Prompt Compression & Optimization | Expansion Guide

Back to Module 13

The Problem

You compressed your 20K token codebase down to 5K using aggressive filtering. The AI's response? Generic, wrong, or hallucinates code that doesn't match your architecture. You re-run with full context and suddenly it works perfectly.

Compression saved tokens but destroyed understanding.

The hard truth: compression is optimization. And premature optimization kills more AI sessions than it helps. You need to know when compression makes things worse.

The Core Insight

AI models need context to understand relationships. Over-compression breaks the mental model.

Think of it like reading a novel where every third page is missing. You can follow the plot, but you miss character motivations, foreshadowing, and subtle connections. The AI has the same problem with over-compressed code.

The key: some context types are compression-hostile. Recognize them and back off.

Anti-Pattern Catalog

Anti-Pattern 1: The Signature Massacre

What happens: Compression removes type hints, parameter names, or docstrings from function signatures.

# Original (clear contract)
def process_payment(
    user_id: str,
    amount: Decimal,
    payment_method: PaymentMethod,
    idempotency_key: Optional[str] = None
) -> PaymentResult:
    """
    Process a payment for a user.

    Args:
        user_id: UUID of the user
        amount: Payment amount (must be positive)
        payment_method: Validated payment method
        idempotency_key: Optional key for duplicate prevention

    Returns:
        PaymentResult with transaction ID or error
    """
    pass

# After aggressive compression (broken contract)
def process_payment(user_id, amount, payment_method, idempotency_key=None):
    pass

Why it breaks: AI loses critical information about what types are expected, what the function does, and what constraints exist.

The fix: Never compress function signatures in your public API or the files being modified.

def protect_signatures(code):
    """Extract and preserve function signatures."""
    signatures = extract_function_defs_with_docstrings(code)
    body = extract_function_bodies(code)

    # Compress bodies aggressively, keep signatures intact
    compressed_bodies = compressor.compress_prompt(body, rate=0.3)

    return merge(signatures, compressed_bodies)

Anti-Pattern 2: The Context Collapse

What happens: Compression removes the "glue" that connects pieces of code, leaving isolated fragments.

# Original (shows flow)
def checkout_flow(cart):
    # Validate inventory first
    if not inventory.check_availability(cart.items):
        raise OutOfStockError()

    # Then validate payment
    payment_valid = validate_payment_method(cart.payment)
    if not payment_valid:
        raise PaymentError()

    # Finally process
    return process_order(cart)

# After compression (lost flow)
def checkout_flow(cart):
    inventory.check_availability(cart.items)
    validate_payment_method(cart.payment)
    process_order(cart)

Why it breaks: Comments explaining the "why" are gone. Error handling is gone. The AI doesn't understand the order matters or why.

The fix: Preserve comments that explain control flow, especially for the main file being debugged.

Anti-Pattern 3: The Type System Blindness

What happens: TypeScript interfaces, Python type aliases, or schema definitions get compressed away.

# Original (clear types)
from typing import TypedDict, Literal

class UserProfile(TypedDict):
    user_id: str
    email: str
    role: Literal["admin", "user", "guest"]
    credits: Decimal
    subscription_tier: Literal["free", "pro", "enterprise"]

# After compression (type info lost)
# [Types removed, only runtime code remains]

Why it breaks: AI loses understanding of valid values, field constraints, and relationships between types.

The fix: Extract and preserve type definitions before compression.

def preserve_type_system(code):
    """Keep type definitions intact."""
    types = extract_type_definitions(code)  # TypedDict, interfaces, etc
    schemas = extract_validation_schemas(code)  # Pydantic, Zod, etc
    runtime = extract_runtime_code(code)

    compressed_runtime = compressor.compress_prompt(runtime, rate=0.4)

    return merge(types, schemas, compressed_runtime)

Anti-Pattern 4: The Error Message Purge

What happens: Compression treats error messages as "low value" and removes them.

# Original (helpful errors)
if not user.has_permission("checkout"):
    raise PermissionError(
        f"User {user.id} lacks 'checkout' permission. "
        f"Current role: {user.role}. Required: admin or premium"
    )

# After compression (useless error)
if not user.has_permission("checkout"):
    raise PermissionError()

Why it breaks: When debugging, error messages are often the most valuable context. They explain what went wrong.

The fix: Boost importance of error messages and exception handling.

def protect_error_context(code):
    """Preserve error messages and raise statements."""
    import ast

    tree = ast.parse(code)
    protected_lines = set()

    for node in ast.walk(tree):
        # Protect raise statements
        if isinstance(node, ast.Raise):
            protected_lines.add(node.lineno)

        # Protect exception handlers
        if isinstance(node, ast.ExceptHandler):
            protected_lines.update(range(
                node.lineno,
                node.end_lineno + 1
            ))

    return selective_compress(code, protected_lines)

Anti-Pattern 5: The Test Apocalypse

What happens: Tests are "less important than prod code" so they get compressed to nothing.

# Original test (documents behavior)
def test_checkout_with_insufficient_credits():
    """
    When user has $5 credits but cart is $10,
    checkout should fail with InsufficientFundsError
    and preserve the cart state.
    """
    user = User(credits=Decimal("5.00"))
    cart = Cart(items=[Item(price=Decimal("10.00"))])

    with pytest.raises(InsufficientFundsError) as exc:
        checkout(user, cart)

    assert "insufficient funds" in str(exc.value).lower()
    assert cart.is_active  # Cart should not be cleared

# After compression (lost behavior spec)
def test_checkout_with_insufficient_credits():
    user = User(credits=Decimal("5.00"))
    cart = Cart(items=[Item(price=Decimal("10.00"))])
    pytest.raises(InsufficientFundsError, checkout, user, cart)

Why it breaks: Tests are documentation of expected behavior. Compressing them loses the spec.

The fix: For feature changes, include full test context. For debugging, include only failing tests at full fidelity.

Anti-Pattern 6: The Dependency Black Box

What happens: External library usage gets compressed, AI loses understanding of how to use APIs.

# Original (shows library usage)
import stripe
stripe.api_key = settings.STRIPE_SECRET_KEY

# Stripe requires amount in cents, not dollars
amount_cents = int(payment.amount * 100)

charge = stripe.Charge.create(
    amount=amount_cents,
    currency="usd",
    source=payment.token,
    idempotency_key=payment.idempotency_key  # Critical for retries
)

# After compression (lost API details)
import stripe
charge = stripe.Charge.create(
    amount=amount_cents,
    currency="usd",
    source=payment.token
)

Why it breaks: Comments explaining library quirks (cents vs dollars, idempotency) are lost. AI might suggest incorrect API usage.

The fix: Don't compress code that interfaces with external APIs. Include relevant docs instead.

Anti-Pattern 7: The Configuration Vaporization

What happens: Configuration files and constants get filtered out as "not code."

# config.py (excluded from context)
MAX_RETRY_ATTEMPTS = 3
PAYMENT_TIMEOUT_SECONDS = 30
ALLOWED_CURRENCIES = ["USD", "EUR", "GBP"]
FEATURE_FLAGS = {
    "enable_store_credit": True,
    "enable_crypto_payments": False
}

# checkout.py (AI sees this without config)
if currency not in ALLOWED_CURRENCIES:  # AI doesn't know what's allowed!
    raise ValueError(f"Unsupported currency: {currency}")

Why it breaks: AI can't suggest valid changes without knowing configuration constraints.

The fix: Always include configuration files that define constraints for the code being modified.

Detection Strategies

How to Know Compression Broke Your Prompt

Symptom Root Cause Fix
AI suggests generic solutions Lost domain-specific context Reduce compression rate
AI hallucinates function signatures Signature compression Preserve signatures intact
AI violates type constraints Type definitions removed Include full type system
AI suggests already-tried solutions Error messages compressed Protect error handling code
Generated code doesn't compile Syntax-breaking compression Use AST-aware compression

When to Skip Compression Entirely

Do NOT Compress For:

Safe Compression Checklist

Before compressing, verify:

class CompressionSafetyCheck:
    def is_safe_to_compress(self, code, task):
        """Run safety checks before compression."""
        checks = {
            'has_sufficient_tokens': self.token_count(code) > 10000,
            'not_security_audit': 'security' not in task.lower(),
            'not_first_attempt': self.attempt_count > 1,
            'has_fallback_context': self.can_retrieve_full_context(),
            'preserves_signatures': self.will_keep_signatures(),
            'preserves_types': self.will_keep_type_defs(),
            'preserves_errors': self.will_keep_error_handling()
        }

        if not all(checks.values()):
            failed = [k for k, v in checks.items() if not v]
            self.warn(f"Compression safety failed: {failed}")
            return False

        return True

Quick Reference

Never Compress:

Warning Signs:

# If you see these, compression failed:
- AI suggests wrong types
- AI hallucinates non-existent functions
- Generated code doesn't compile
- Solutions ignore constraints
- Responses are too generic

Recovery Pattern:

if ai_response_quality == "poor":
    # Step 1: Try again with less compression
    compression_rate *= 1.5  # 0.4 → 0.6

    if still_poor:
        # Step 2: Include full context for main file
        include_full_file(target_file)

    if still_poor:
        # Step 3: Skip compression entirely
        send_full_context()

Safe Defaults: