跪拜 Guibai
← All articles
Frontend · JavaScript · AI Programming

AI Stealth Edits: Five Ways Your Copilot Changes Code You Never Asked It To Touch

By kyriewen ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

These failures are not edge cases — they are systematic behaviors baked into how LLMs optimize for pattern consistency over project-specific intent. A developer who trusts the diff at a glance will ship broken auth, race conditions, or silently altered business logic that unit tests often miss because tests mock the very conventions the AI deleted.

Summary

A developer catalogued five recurring patterns where AI assistants silently modify code beyond the requested change. The most dangerous: deleting files with zero explicit imports that frameworks like Next.js and Nuxt auto-load by convention, silently removing auth middleware or error boundaries. Converting synchronous startup code to async breaks initialization order in ways that produce intermittent failures. Extracting functions during refactoring can turn a loop-breaking `break` into a function-scoped `return`, changing control flow entirely. Over-abstraction turns two-line utilities into thirty-line class hierarchies that bloat the codebase and intimidate future maintainers. And in long conversations, AI reverts manual fixes back to its own earlier output, treating its generated version as canonical.

The root cause is not malice but missing context: AI treats every codebase as a greenfield project and applies generic best-practice patterns without understanding which deviations are deliberate. The universal countermeasure is reviewing the full diff after every AI edit, not just the lines you asked it to change.

Takeaways
AI assistants delete files with no explicit imports, which includes Next.js middleware, error boundaries, Nuxt plugins, and Vite glob-imported modules — all auto-loaded by framework convention.
Converting synchronous config reads or constructor logic to async breaks initialization order, producing intermittent startup failures that are extremely hard to reproduce.
Extracting a loop body into a function can turn a `break` into a `return`, stopping only the extracted function rather than the enclosing loop.
AI over-abstraction wraps simple utilities in interfaces, classes, and configuration objects, inflating two-line functions into thirty-line hierarchies that teams become afraid to touch.
During long conversations, AI will revert manual fixes back to its own earlier output because it treats its generated version as the canonical reference.
The single most effective defense: review the complete diff after every AI edit, not just the lines you asked it to change.
After manually fixing AI-generated code, start a new conversation before continuing — otherwise the old context will overwrite your fixes.
Conclusions

The patterns described are not bugs in a specific tool; they emerge from the fundamental tension between an LLM's drive toward pattern consistency and a real codebase's accumulation of deliberate, context-dependent irregularities.

AI's preference for async and abstraction reflects its training data's bias toward 'modern best practices,' but production systems are full of synchronous code and flat functions that exist for concrete reasons — timing, debuggability, or framework contracts — that the model cannot infer from code alone.

The 'overwriting manual fixes' pattern reveals a deeper problem: AI treats conversation history as ground truth, so the longer a session runs, the more it fights deviation from its own earlier output. This makes long AI pairing sessions actively dangerous without deliberate context resets.

Concepts & terms
Convention-based file loading
Frameworks like Next.js and Nuxt automatically load files from specific directories or with specific names (middleware.ts, error.tsx, plugins/, composables/) without requiring explicit import statements. Static analysis tools and AI see no references and flag them as dead code.
Top-level await
The ability to use the `await` keyword at the top level of a module without wrapping it in an async function. Not supported in CommonJS modules, which means converting a synchronous require-time operation to async can break the module if the runtime hasn't loaded ESM support.
Control-flow keyword semantics under extraction
When extracting a block of code into a separate function, `break` and `continue` inside loops lose their connection to the original loop — they become `return` statements scoped only to the new function. This silently changes program behavior.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗