Skip to main content

Vibe Coding & AI-Generated Debt

"Vibe coding" is the practice of coding with AI assistants—accepting suggestions from Copilot, ChatGPT, or Claude without fully understanding every line. It's fast, it's productive, and it's creating a new category of technical debt.

The Rise of AI-Assisted Development

AI coding assistants are now mainstream:

  • GitHub Copilot: 1.3M+ paid subscribers
  • ChatGPT/Claude: Used daily by millions of developers
  • Cursor, Windsurf, Replit: AI-native IDEs growing fast

The productivity gains are real. But so are the problems.


The Hidden Costs of Vibe Coding

1. Dead Code Accumulation

AI suggests complete solutions. You use 60% of it:

# You asked: "function to parse JSON config"
# AI gave you:

import json
import yaml # only need JSON
import toml # only needed JSON
from pathlib import Path
from typing import Dict, Any, Optional, List # List unused

def parse_config(path: str) -> Dict[str, Any]:
file_path = Path(path)

if file_path.suffix == '.json':
with open(file_path) as f:
return json.load(f)
elif file_path.suffix in ['.yml', '.yaml']: # never use YAML
with open(file_path) as f:
return yaml.safe_load(f)
elif file_path.suffix == '.toml': # never use TOML
with open(file_path) as f:
return toml.load(f)
else:
raise ValueError(f"Unsupported format: {file_path.suffix}")

def validate_config(config: Dict[str, Any]) -> bool: # never called this
required_keys = ['database', 'api_key', 'debug']
return all(key in config for key in required_keys)

You wanted 10 lines. You got 25. Half is dead.

2. Security Vulnerabilities

AI doesn't understand your security context:

# You asked: "function to run shell command"
# AI gave you:

import subprocess

def run_command(cmd: str) -> str:
result = subprocess.run(
cmd,
shell=True, # CRITICAL: Command injection vulnerability!
capture_output=True,
text=True
)
return result.stdout

# AI didn't ask: "Is cmd from user input?"
# AI didn't know: This is called from a web endpoint

Result: Exploitable vulnerability shipped to production.

3. Complexity Creep

AI writes verbose, "safe" code that handles cases you don't have:

# You asked: "sum a list of numbers"
# You expected: sum(numbers)
# AI gave you:

from typing import List, Union, Optional
from decimal import Decimal
import logging

logger = logging.getLogger(__name__)

def sum_numbers(
numbers: List[Union[int, float, Decimal]],
initial: Optional[Union[int, float, Decimal]] = None,
ignore_none: bool = True,
round_result: Optional[int] = None
) -> Union[int, float, Decimal]:

if not numbers:
logger.warning("Empty list provided to sum_numbers")
return initial or 0

total = initial or 0
for num in numbers:
if num is None and ignore_none:
continue
if num is None:
raise ValueError("None value in list")
total += num

if round_result is not None:
total = round(total, round_result)

return total

30 lines instead of 1. Complexity: 6 instead of 1.

4. Orphaned Functions

AI creates helper functions you never call:

5. Copy-Paste Propagation

You accept AI suggestions across multiple files. Same pattern, same bugs:

# file1.py - AI wrote this
data = request.json() # No validation
db.execute(f"INSERT INTO t VALUES ({data['id']})")

# file2.py - You asked AI for similar code
data = request.json() # Same vulnerability copied
db.execute(f"INSERT INTO other VALUES ({data['id']})")

# file3.py - Pattern continues...

The Vibe Coding Cycle

The problem isn't AI—it's accepting without reviewing.


Why Traditional Tools Miss This

Linters Don't Catch Logic Issues

# Passes all linters!
def process(user_id):
query = f"SELECT * FROM users WHERE id = {user_id}" # SQL injection
return db.execute(query)

Tests Only Cover What You Test

AI-generated code often has:

  • Untested error branches
  • Unused parameters (no test exercises them)
  • Dead functions (no test calls them)

Code Review Doesn't Scale

Average PR size with AI assistance: 2.5x larger
Code review time: Fixed
Result: Less scrutiny per line

How Skylos Fixes Vibe Coding Debt

1. Dead Code Detection

Find the AI bloat:

skylos . --confidence 70
Unused imports: 23
Unused functions: 8
Unused parameters: 12

2. Security Scanning

Catch what AI didn't consider:

skylos . --danger
SKY-D210 CRITICAL: SQL injection in api/users.py:45
SKY-D212 CRITICAL: Command injection in utils/shell.py:12

3. Complexity Alerts

Flag AI's over-engineering:

skylos . --quality
SKY-Q301: Function 'sum_numbers' has complexity 6 (threshold: 10)
SKY-Q304: Function 'parse_config' has 5 parameters (threshold: 5)

4. AI Defect Checks

Catch evidence-backed AI-code failure modes:

skylos . --ai-defects
SKY-L012: Phantom security helper call
SKY-D224: Real package called with invented API
SKY-A101: Test assertion weakened in this diff

5. Quality Gate

Prevent AI debt from shipping:

skylos . --danger --quality --ai-defects --gate

Block PRs that introduce vulnerabilities or dead code.


Best Practices

1. Run Skylos before committing
# Pre-commit hook
skylos . --danger --quality --ai-defects || exit 1

Catches AI-introduced issues immediately.

2. Delete AI's unused suggestions

AI gives you 5 functions. You need 2. Delete the other 3.

skylos . -i  # Interactive mode
3. Question AI's security assumptions

AI doesn't know:

  • Where your data comes from
  • Who can call this function
  • What's sensitive in your context

Ask: "Is this input trusted?"

4. Simplify AI's over-engineering
# AI gave you 30 lines for summing
# You need:
total = sum(numbers)

Use Skylos complexity warnings as a signal.

5. Set up CI gates

Don't let AI debt accumulate:

- run: skylos . --danger --quality --ai-defects --gate

In-Loop Verification

skylos verify is the agent-loop path for vibe coding defects. Instead of running a full project report after a feature is done, agents and editors can ask Skylos for a narrow verdict on the changed file or range:

skylos verify . --file src/app.py --range 40:75 --project-context

The verifier returns versioned JSON with only AI-code trust findings. Each finding includes rule_id, vibe_category, ai_likelihood, range, message, optional suggested_fix, confidence, severity, and category. AI-defect findings use category: "ai_defect" and full scans group them under the ai_defects output bucket.

The same schema is available through MCP as verify_change, which lets MCP-compatible coding agents verify a change after each edit. The VS Code extension also routes idle changed-function analysis through this verifier path.

For unsaved editor buffers, pass a JSON manifest through stdin:

printf '{"file":"src/app.py","code":"def handler(): pass\n","range":"1:1"}' \
| skylos verify . --stdin --no-fail

Local Defect Signals

Skylos can record local structural signals when a developer accepts, dismisses, or trains on an AI-defect finding through the agent service. Signal capture is off by default and controlled by [tool.skylos.contribution].

[tool.skylos.contribution]
collect_local_signals = true
contribute_public_corpus = false
structural_signatures_only = true
include_source = false

Local events are written to .skylos/contribution/events.json. They contain rule/category/severity metadata, file extension, line bucket, and hashes. Raw source is not captured.

AI-Specific Detection Rules

Skylos includes rules specifically designed to catch evidence-backed failure modes common in AI-generated code. AI-defect checks (SKY-A101 to SKY-A105, SKY-L012, SKY-L023, SKY-D222, SKY-D224, and SKY-D225) report under the ai_defects output bucket; broader AI-prone quality and security rules keep their existing buckets. See AI Defect Verification for the exact grouping method and why some AI-defect rules keep historical SKY-L or SKY-D IDs.

RuleWhat It CatchesWhy AI Does ThisVibe Category
SKY-A101Assertion weakening — exact assertions replaced with truthiness/null checks, skips, or removed exception assertionsLLMs sometimes make tests pass by weakening the test instead of preserving behaviorassertion_weakening
SKY-A102High-risk change without tests — auth, billing, validation, tenant, webhook, or similar code changed with no test file changedAI-generated PRs can touch behavior-sensitive code without adding reviewable test evidencetest_impact_gap
SKY-A103CI permission expansion — GitHub Actions diff adds write permissions or privileged workflow triggersAI-generated CI edits can accidentally broaden repository token privilegesci_permission_expansion
SKY-A104Public CLI surface drift — public CLI flag removed from argparse, Click, or Typer codeAI-generated refactors can break documented commands or automation without noticing compatibility impactpublic_api_surface_drift
SKY-A105Contract route guard missing — a route lacks a decorator required by .skylos/ai-contract.ymlLLMs add endpoints without applying repo-specific auth or tenant guardrailsmissing_contract_guardrail
SKY-L012Phantom function callssanitize_input() that doesn't existLLMs hallucinate security functions that were never defined or importedhallucinated_reference
SKY-L023Phantom decorators@require_auth that doesn't existLLMs hallucinate security decorators, leaving functions unprotectedhallucinated_reference
SKY-L026Unfinished generation — function body is only pass or ...LLMs generate stubs and move on, leaving empty functions in productionincomplete_generation
SKY-L016Undefined configos.getenv("ENABLE_CACHING") never setLLMs invent feature flags that reference config that doesn't existghost_config
SKY-L024Stale mocksmock.patch("mod.old_func") targets deleted functionAfter refactoring, AI-generated tests still patch old function namesstale_reference
SKY-L013Insecure randomnessrandom.randint() for tokensLLMs reach for random instead of secrets for security values
SKY-L014Hardcoded credentialspassword="admin123"LLMs generate working examples with real-looking credentials
SKY-L010Security TODO markers# TODO: add auth checkLLMs leave placeholder comments as "future work" that never happens
SKY-L011Disabled security controlsverify=FalseLLMs disable TLS/CSRF to make examples "just work"
SKY-L017Error info disclosurereturn str(e)LLMs return raw exceptions for "debugging" instead of safe error messages
SKY-L020Broad file permissionsos.chmod(f, 0o777)LLMs use maximally permissive modes to avoid permission errors
SKY-D222Hallucinated dependencies — importing packages that don't exist on PyPILLMs invent package names that sound right but aren't realdependency_hallucination
SKY-D224API signature hallucinations — real package, invented method or keywordLLMs confidently call APIs that look plausible but are not in the installed packageapi_signature_hallucination
SKY-D225Dependency version hallucinations — npm or Go versions that were never publishedLLMs invent semver pins that make manifests look complete but fail in buildsdependency_hallucination
SKY-D260Prompt injection — hidden instructions in comments, YAML, MarkdownHostile content targeting AI agents processing your codebase
# Scan for AI-generated code issues
skylos . --ai-defects

The Numbers

MetricWithout SkylosWith Skylos
Dead code from AI~30% of AI-generated code< 5%
Security vulns shipped1 in 10 PRsBlocked at CI
Average function complexity127
Time to onboard new dev3 weeks2 weeks

Next Steps

Getting Started

Install Skylos and scan your codebase

CI/CD Integration

Set up quality gates to prevent AI debt