Claude Code makes it way too easy to accidentally create the
The real risk isn't just a few messy lines of code; it's the systemic degradation of the architecture. When an agent hits a wall, it doesn't always refactor; sometimes it just finds a workaround. This creates a "layer of indirection" problem. You end up with a chain of calls that looks like ServiceA -> WrapperB -> HelperC -> LegacyD, all because the AI couldn't figure out how to modify LegacyD without breaking a dependency.
To keep an AI workflow from turning your project into a disaster, I've started implementing a few strict guardrails during deployment.
How to stop LLM agents from bloating your codebase
1. Enforce a "Delete-First" Policy: Before letting an agent add a new utility class, explicitly tell it to look for existing functions that can be deleted or merged. If the line count increases by more than 10% in a single PR without a corresponding new feature, I flag it for a manual audit.
2. Strict Complexity Caps: I've started using a custom linting rule or a simple script to track cyclomatic complexity. If the agent submits a function with a complexity score over 10, I reject the commit and force it to break the logic into smaller, pure functions.
3. The "One Layer" Rule: Whenever an agent suggests a wrapper or a proxy to "fix" a type mismatch or a compatibility issue, I ask it to justify why a direct refactor of the source isn't possible. This prevents the "indirection spiral" where the code just keeps getting deeper and harder to trace.
For those of you building a real-world LLM agent integration, you have to remember that the model doesn't feel the pain of maintaining the code six months from now. It only cares about the current prompt's success. If you're running a loop where the AI iterates on a bug, it might "fix" the issue by adding a conditional check that masks the root cause, which is exactly how performance degrades silently.
I recently saw a case where an agent tried to solve a race condition by adding a retry loop with an exponential backoff—which worked for the test case—but it actually masked a deadlocking issue in the database layer. The code "worked," but the underlying architecture became more fragile. That is the definition of technical debt in the age of AI.
If you want to avoid this, don't just treat the agent as a black box. Treat it as a junior developer who is incredibly fast but has zero long-term memory of the system's architectural goals. Use prompt engineering to force it to prioritize simplicity over speed. For example, try adding a constraint to your system prompt: "Prioritize the removal of redundant code over the addition of new abstractions."