In Chapter 1, we conducted a thorough background check on your new partner — the AI "super intern." We learned that it is deeply knowledgeable and blisteringly productive, yet also inexperienced and utterly lacking in a sense of responsibility. Now it is time to move to the next stage of its "onboarding training": learning to recognize the three most common, and most destructive, types of mistakes this intern makes in real work.
I call these three mistakes the "three traps" of AI programming. They are: confirmation bias, the entropy spiral, and local optimum.
These three traps are like quicksand lurking in the code jungle. On the surface they look flat and harmless, but once you step in, you sink deeper and deeper, until you drag the entire project into the mire. They are extremely insidious, because in many cases the "wrong" answers AI gives even look "right" in the short term. They run, they solve the problem at hand, and they even pass your initial tests.
That is precisely their danger. They do not crash the system immediately; instead, like the proverbial frog in slowly warming water, they quietly erode your project's health, maintainability, and extensibility. Until one day, when you realize that even a small requirement change requires touching a dozen files, or that fixing one bug unleashes five more, it is already too late.
The purpose of this chapter is to equip you with a "trap detector." We will dissect the causes and manifestations of each trap in depth, and through vivid real-world cases, teach you how to recognize and avoid them with sharpened instincts before they cause irreversible damage.
2.1 Confirmation Bias: AI Only Digs Itself Deeper
"Confirmation bias" is a classic concept in psychology. It refers to people's tendency to seek out, interpret, and remember information that supports their existing beliefs or hypotheses. When this phenomenon occurs in AI, the consequences can be more severe, because AI, within a session, lacks the human mechanisms of "reflection" and "self-correction."
The confirmation bias trap in AI, put simply, is this: once AI arrives at an initial, flawed design or fix based on incomplete or incorrect information, all of its subsequent behavior stubbornly revolves around "patching up" and "defending" that wrong solution, rather than overturning it from the root.
It is like a stubborn driver who took a wrong turn at the very start of the journey. When you point out, "I think we've taken a wrong turn," he does not turn back onto the correct road. Instead he insists, "No, this is the right way — if we turn right at the next intersection, we're sure to loop back around." And so you drive further and further down the wrong road, until the fuel runs out and you are stranded in the wilderness.
How the Trap Is Formed: AI's "Context Lock-In" Tendency
To understand why AI behaves this way, we need to look inside its "brain" once more. A large language model is essentially a probabilistic prediction engine. Its core task is to "predict the most likely next token based on the existing text (the context)."
When you give it a task, it produces a solution. Once that solution is written into the conversation history, it becomes part of the context. When you then point out a problem with that solution, AI draws on "my previous solution" plus "the problem you raised" as its new context for the next response.
In its probabilistic model, "making small fixes" to the existing solution has a far higher probability than "overturning everything and starting over." Because it assumes that your question is a request to "polish" the solution, not to "reject" it. This context-based, linear, one-way "thought chain" makes it extremely difficult for AI to undertake a "disruptive" self-revolution.
[Real-World Scenario] An Architectural Disaster Brought On by "Confirmation Bias"
Let us look at a synthetic but true-to-life teaching case. Every step below comes from common patterns of real incidents; the characters and data are fictional.
Background: You are building an internal management dashboard that needs a user permission system. You give AI its first instruction: "Please design a frontend permission control scheme with three roles: Admin, Editor, and Visitor."
Step One: AI Plants the "Wrong" Seed
AI quickly produces a solution: in the frontend route configuration, add a meta field to each route containing a roles array, like this:
{
path: '/dashboard',
component: Dashboard,
meta: { roles: ['admin', 'editor'] } // Only admin and editor can access
}
It then provides route guard logic that checks, on every route transition, whether the user's role is in the meta.roles array.
Analysis: Does this solution work? Yes. For a simple internal system with fixed roles, it is even a common quick implementation. But it carries a fatal architectural flaw: it hard-codes the permission logic into the frontend. This means that every time you need to add a role or adjust permissions, you must modify the frontend code and redeploy. It is a poorly designed solution that lacks extensibility.
The wrong seed has been planted.
Step Two: You Try to Correct It, and AI Begins Its "Confirmation Bias"
After the project goes live, the product manager raises a new requirement: "We need to add an 'Auditor' role. It can see all pages but cannot perform any editing operations."
You relay the requirement to AI: "On the existing basis, add an 'auditor' role that has access to all pages."
A rational, experienced developer might reflect at this point: "Hard-coding roles is already causing trouble. Shouldn't I refactor and move the permission logic to the backend?"
But AI will not. Its confirmation bias is triggered. Its "reasoning" goes: "My original design (frontend hard-coding) is correct. I just need to 'elegantly' accommodate the new role within this design."
So it offers a solution that leaves you speechless: it iterates through every route configuration and manually adds 'auditor' to the meta.roles array of each one.
{ path: '/dashboard', meta: { roles: ['admin', 'editor', 'auditor'] } }
{ path: '/settings', meta: { roles: ['admin', 'auditor'] } }
// ... repeats the same operation for dozens of routes
It has "executed" your instruction perfectly — while cementing that bad design even deeper.
Step Three: The Disaster Escalates, and AI "Digs Itself Deeper"
Another month passes, and the product manager delivers the ultimate challenge: "We want permissions to be dynamically configurable! That is, from a backend interface we should be able to freely check which pages each role can access, without needing a frontend release."
This is a requirement that should overturn the original design entirely. With a glimmer of hope, you give AI this instruction.
By now, AI's confirmation bias is terminally ill. Inside its head, alarms are ringing everywhere — but every alarm is saying the same thing: "At all costs, defend my original architecture! Prove it is right!"
So it begins to "show off," proposing a solution of staggering, almost "demonic" complexity:
- On application startup, the frontend first requests a JSON "permission configuration table" from the backend.
- Once it receives this JSON, the frontend dynamically and recursively modifies the route configuration objects in memory. It writes a complex function that traverses the route tree, finds each route, and rewrites its
meta.rolesarray according to the permission table. - To handle newly added pages, it even suggests keeping a "full route table" in the frontend code, then dynamically "activating" or "hiding" parts of it based on the permission configuration.
Do you see what happened?
Rather than admit that "the original hard-coded solution was wrong," AI would rather implement, on the client side, an extremely complex dynamic permission-computation logic that rightfully belongs on the server. It covers strategic laziness with tactical diligence.
If you unfortunately adopted this solution, your project would have become a complete "shit mountain" — a fragile, unmaintainable monstrosity whose complex logic only AI (if even it) could understand.
This is the most terrifying aspect of the confirmation bias trap: it never directly tells you "I can't do it." Instead, it "solves" the problem you pointed out with an even more complex, even more wrong solution, dragging you and the project together into the abyss. (The book's "confirmation bias" is a carry-over label for this context-conditioning phenomenon -- AI's difficulty in overturning an established assumption within a session; it is not identical to the confirmation-bias mechanism in human psychology, a distinction Chapter 6 revisits.)
How to Avoid the "Confirmation Bias" Trap?
Now that you have identified this trap, the way to avoid it follows naturally. The core principle is: never "debate" an AI that has sunk into obsession — learn to "reset" it.
- Intervene early; recognize the "bad smell": The moment you find that AI's very first solution already carries potential architectural problems (such as hard-coding or high coupling), be on high alert. Do not ask it to "patch" the solution, because that will simply trigger confirmation bias.
- Resolutely clear the session (
/clear): This is the most effective and cheapest weapon against confirmation bias. Once you judge that AI has gone down the wrong path, stop wrestling with it. Immediately clear the current conversation history and open a brand-new session. - Implant stronger "constraints" in the new session: In the new session, your opening sentence should no longer be "help me implement a permission feature," but must carry the "constraints" you distilled from the previous failure. For example: "Please design a frontend-backend separated permission scheme. Requirements: the frontend is responsible only for dynamically generating menus and routes based on the permission list returned by the backend; all permission judgment logic must be encapsulated in backend APIs; no concrete role name strings should appear anywhere in the frontend code."
Through "clearing + enhanced constraints," you effectively drag that driver who was careering down the wrong road out of the car, hand him a brand-new map marked with the correct route and the restricted zones, and let him set off anew.
Remember, in your collaboration with AI, you are not its "colleague" — you are its "navigator." When the course drifts, your job is not to help it steer, but to reset the navigation system directly.
2.2 The Entropy Spiral: Patch upon Patch, and the System Decays Faster
In physics, "entropy" is a measure of a system's disorder. The second law of thermodynamics states that the entropy of an isolated system never decreases; "entropy increase" in software is a borrowing of that term -- a software project that is not actively maintained will only passively respond to requirement changes and bug fixes, and its complexity, disorder, and fragility will keep growing. This process is the "entropy increase" of software.
The arrival of AI has vastly accelerated this process.
The "entropy spiral" trap refers to this: because AI is exceptionally good at "local, quick" fixes, developers come to rely on it to slap "patches" onto the system rather than carrying out fundamental refactoring. Each patch solves the immediate problem, but it also adds a small increment of complexity. Accumulated over time, these patches interact and become interdependent, eventually dragging the system into an irreversible, accelerating vicious cycle of decay.
It is like an old plumbing system.
- The first time it leaks, you wrap a strip of tape around it. — Problem solved.
- A second crack appears nearby, and you wrap another strip of tape. — Problem solved too.
- Gradually, the whole pipe is layered in tape, and you can no longer tell where the pipe ends and the tape begins. By now the pressure distribution inside the pipe has become bizarre. Fixing a leak in one place may well cause an even more fragile spot to burst elsewhere. The system turns extremely unstable, and maintenance costs climb exponentially.
AI is that supplier who can hand you an endless roll of "tape" at the speed of light.
How the Trap Is Formed: AI's "Minimal Effort" Tendency
Why does AI prefer patching? Because it is trained to complete the "verb" in your instruction in the most efficient way possible.
- When you ask it to "fix this bug," it searches for the code change that alters the least and is the most direct. Patching obviously requires less "effort" than refactoring the entire function.
- When you ask it to "add a feature," it searches for the option that intrudes least on the existing code. Adding an
if-elseclearly takes less "effort" than redesigning a strategy pattern.
AI has no "code hygiene," no pursuit of "engineering aesthetics," and certainly no sense of responsibility for "long-term maintainability." It is a born "opportunist" and "pragmatist," and it will always take the shortest path to "getting the current task done," even if that path leads into a swamp.
[Real-World Scenario] How a Component "Decays" in AI's Hands
Let us observe how a common frontend data-fetching component, with AI's "help," descends step by step into the abyss of entropy.
Phase One: The Initial Version (Low-Entropy State)
You ask AI to write a React component that fetches and displays user information. It produces clean, "textbook" code:
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]);
if (!user) {
return <div>Loading...</div>;
}
return <h1>{user.name}</h1>;
}
At this point the component is in a perfect low-entropy state. Single responsibility, clear logic.
Phase Two: The First Patch (Entropy Begins to Rise)
Feedback from production says that when the network is slow, the page stays on "Loading..." for too long, hurting the user experience. You ask AI: "Add a timeout handler — if it hasn't loaded within 5 seconds, show an error message."
AI immediately applies the first patch:
// ...
useEffect(() => {
const timer = setTimeout(() => {
// Assume we handle the timeout error state here
}, 5000);
fetch(...)
.then(...)
.finally(() => clearTimeout(timer));
}, [userId]);
// ...
It introduces a setTimeout and a clearTimeout. The code is beginning to develop a bit of a "smell," but the problem is indeed solved. Entropy has increased slightly.
Phase Three: The Second Patch (Decay Accelerates)
The product manager says: "We don't just want to show the user's name — we also want to show their article list. Add it to this same component."
You instruct AI: "After fetching the user info, make another request to fetch their article list."
AI faithfully applies the second patch, nesting a second fetch inside the first .then callback:
// ...
const [user, setUser] = useState(null);
const [posts, setPosts] = useState([]); // New state
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
setUser(data);
// Nested request!
fetch(`/api/posts?userId=${userId}`)
.then(res => res.json())
.then(data => setPosts(data));
});
}, [userId]);
// ...
Now we have nested API requests and two independent states. The component's responsibility is no longer single, and the code is becoming hard to read. This is the classic "waterfall request" performance problem. Entropy has increased significantly.
Phase Four: The Third and Fourth Patches... (the Entropy Spiral Forms)
- Bug fix: When a request fails, the whole component gets stuck. You ask AI to "fix it." AI appends a
.catchblock to everyfetch, handling each different error state separately. - New feature: The product manager says, "We need a pull-to-refresh feature." You ask AI to "add pull-to-refresh." AI brings in more state (like
isRefreshing) and adds increasingly convoluted logic insideuseEffect.
Final Phase: System Decay (High-Entropy State)
Months later, this UserProfile component has become a bloated "monster" of over 200 lines. It maintains seven or eight useState hooks, the dependency array in useEffect is terrifyingly long, and it is crammed with all manner of if-else and try-catch.
At this point the product manager raises a seemingly simple requirement: "We want to show a guidance prompt when a user has never posted any articles." You toss this requirement to AI. AI analyzes the sprawling code and... breaks the timeout logic, or triggers a memory leak during pull-to-refresh.
You have reached the endgame of the entropy spiral: the system's complexity has exceeded the cognitive limits of AI (and even of you yourself). Any tiny change can set off an avalanche of chain reactions. Maintaining the project has become a hellish game of "whack-a-mole."
How to Break Free from the "Entropy Spiral"?
Battling entropy is the eternal mission of the software engineer. In the AI era, that mission has become more important than ever.
- Change your "verbs": Stop using verbs like "fix," "add," and "implement" with AI, which lure it into patching. Learn to use "refactor."
- Wrong instruction: "Help me fix a bug: the page crashes when the data is empty."
- Correct instruction: "Refactor this component. I need it to gracefully handle three states: loading, load success, and load failure. Please extract the data-fetching logic into a custom Hook."
- Conduct regular "code health reviews": Institutionalize refactoring. Mandate that every iteration cycle sets aside a fixed amount of time specifically to repay "technical debt." You can let AI help with the review: "Please audit the
UserProfilecomponent and list the 'code smells' it contains, such as overly long functions, excessive responsibilities, deep nesting, and so on." - Embrace the Single Responsibility Principle (SRP): While AI writes code, you must supervise at all times as its "architect." The moment you notice a component or function's responsibility becoming impure — say, it now handles data fetching, UI rendering, and user interaction all at once — immediately command AI to split it apart.
Remember, AI is the finest "tactical executor," but it can never replace your position as the "strategist." Your strategy is to fight entropy at all costs, preserving the system's order and simplicity.
2.3 Local Optimum: It Seems to Solve the Problem, but It Ruins the Architecture
This is the most insidious — and, over the long run, the most destructive — of the three traps.
The "local optimum" trap refers to this: when solving an isolated problem, AI tends to choose a solution that "looks" simplest and most efficient within the current module or function. Yet when that solution is viewed from the global perspective of the entire system, it may violate established architectural principles, forge unnecessary coupling with other modules, or plant hidden hazards for future extension.
It is like a chess player who sees only the gains and losses in one corner of the board. To capture an opponent's pawn, he lays bare a fatal weakness in his own king. From the local view, he has won; from the global view, he has lost the whole game.
AI is exactly such a "tactical genius, strategic fool." Its "vision" is usually confined to the code snippets you hand it and the context of the current session. It cannot, as a human architect can, hold in its mind a complete "system architecture blueprint" spanning every module.
How the Trap Is Formed: AI's "Limited Contextual Vision"
The context window of a large model is finite. Even with ever-lengthening context windows, it cannot form a structured, hierarchical "mental model" of the entire codebase the way a human can. In its eyes, all the code in the project is just one long, flattened sequence of tokens.
When you ask it to solve a specific problem, it focuses first on the code most directly related to that problem. It finds a solution that makes the current function run and the current test pass — and with that, its task is done. Whether that solution conflicts with a module three directories away, or violates a design principle spelled out in the project's README.md, lies entirely beyond its "scope of concern."
[Real-World Scenario] An "Architectural Erosion" Caused by "Local Optimum"
Background: You are developing a modular frontend application. Following the classic "layered architecture" principle, you divide the application into three layers:
- UI Layer: responsible for page rendering — these are "dumb" components.
- Business Logic Layer: responsible for handling user interactions, data fetching, and state management.
- API Layer: responsible for communicating with backend interfaces and encapsulating HTTP requests.
This is a clean, decoupled, ideal architecture. Data flows in one direction: the UI layer calls the business logic layer, which calls the API layer.
The Problem Appears: In a UserProfile component (UI layer) that displays user information, you need to add a "Refresh" button. Clicking it should re-fetch the user information.
You give AI a seemingly harmless instruction: "In the UserProfile component, add a refresh button. When clicked, call the API again to fetch the data."
AI's "Local Optimum" Solution
AI begins to analyze the task. Its "vision" is trained on the UserProfile.jsx file. What, it wonders, is the simplest, most direct way to implement this? — Call the fetch API directly inside the component!
It might generate code like this:
// In UserProfile.jsx (UI layer)
import React from 'react';
function UserProfile({ userId }) {
// ... other code
const handleRefresh = () => {
// To solve the problem quickly, just call the API right here!
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
// ... update state
});
};
return (
<div>
{/* ... */}
<button onClick={handleRefresh}>Refresh</button>
</div>
);
}
Is this a good solution? Locally, it seems "perfect":
- Efficient: Only one file was touched, with minimal code.
- Independent: It introduces no new dependencies.
- Functional: Click the button and the feature actually works.
Yet from a global architectural perspective, it is a disaster.
This "local optimum" solution is like a sharp knife slicing straight through the layered architecture you painstakingly designed. It lets the UI layer — which should be "dumb" — jump over the business logic layer entirely and couple tightly to the underlying API communication.
The Beginning of Architectural Erosion
That one "exception" is like opening Pandora's box:
- Logic leakage: The API endpoint (
/api/users/:userId), a detail that should be maintained by the API layer, has leaked down into the UI layer. If the backend interface address ever changes, you must go modify every UI component that calls it directly. - Code duplication: Another
UserAvatarcomponent in the project also needs a refresh feature. What does AI do? It very likely copies and pastes the samefetchsnippet all over again. Repeated, scattered API calls become a maintenance nightmare. - Principle collapse: Other team members (or your future self) see this precedent and think, "Oh, so we can just
fetchdata directly in a component." Gradually, more and more people take this "shortcut." Your layered architecture becomes so much dead letter, and the whole project degenerates into a tangle of "spaghetti code" in which every module depends on every other.
What Is the Correct "Global Optimum" Solution?
A proper process, supervised by a human architect, should look like this:
- You (the decision maker): "I need to add a refresh feature to the
UserProfilecomponent." - You (the architect): "According to our layered architecture, the UI layer cannot call the API directly. This logic belongs in the business logic layer."
- You (the commander), issuing a constrained instruction to AI: "In the
useUserProfilecustom Hook (business logic layer), expose arefreshfunction. In theUserProfilecomponent (UI layer), call thatrefreshfunction."
AI will happily execute this instruction, because it is just as simple and direct. But this time, it is running along the correct "architectural track" you have laid out.
How to Avoid the "Local Optimum" Trap?
Fighting the "local optimum" trap is, at its core, defending your authority as the "architect."
- "Document" and "instructionalize" your architectural principles: Do not keep the architecture blueprint only in your head. Write it down — make it part of the project's
README.mdor a dedicatedARCHITECTURE.mdfile. More importantly, when you make requests to AI, hammer these principles home as "preconditions."- For example: "According to our layered architecture (UI layer – business logic layer – API layer), please add a refresh feature to the
UserProfilecomponent." Merely adding that opening clause dramatically raises the probability that AI chooses the correct solution.
- For example: "According to our layered architecture (UI layer – business logic layer – API layer), please add a refresh feature to the
- Review AI's "dependency changes": When reviewing AI-generated code, pay special attention to whether it has introduced new
importstatements. A UI component suddenly importing an API client, or a low-level utility function suddenly importing an upper-level business module — these are strong signals of "architectural erosion." - Ask "global impact" questions: When you are doubtful about AI's solution, proactively steer it toward more macro-level thinking.
- "Does your solution increase the coupling between the
UserProfilecomponent and other modules?" - "If multiple components will need this refresh feature in the future, would your current design lead to code duplication? Is there an approach that better fits the DRY (Don't Repeat Yourself) principle?"
- "Does your solution increase the coupling between the
This is essentially forcing AI to step out of its narrow "local vision" and simulate an "architecture review." In this way, you can harness AI's logical reasoning to help you uncover latent flaws in its own proposals.
[Self-Checklist] Is Your AI Collaboration on the Eve of Losing Control?
Having read about the three traps, you may feel a lingering chill. Do not worry — recognizing the problem is the first step toward solving it. Now pick up a pen, or recite silently in your heart, and answer the following questions honestly. This checklist will help you quickly diagnose whether your collaboration with AI has already shown dangerous signals.
Part One: Signals of "Confirmation Bias"
- Have you found that, to get AI to fix a bug it introduced itself, you've gone through more than five rounds of dialogue with it, and it feels like it's just "going in circles"?
- When AI offers a clearly flawed solution, is your first instinct "how do I convince it to correct it," rather than "I should weigh whether to keep correcting or clear the session now"?
- Does your project harbor "legacy" pieces of "black magic" code whose complex logic only you and AI understand?
- Do you often catch yourself saying to AI: "No, no, that's not what I meant — I meant, building on what you did before..."?
Part Two: Signals of the "Entropy Spiral"
- Looking back at your commit history, is it crammed with "patch-type" commit messages like "Fix: ...", "Hotfix: ...", and "Add: ...", with very few "Refactor: ..."?
- Have you noticed that the line count of some core file in the project (a component, a service class) has more than doubled over the past month?
- When you ask AI to add a small piece of logic to an existing feature, does it tend to tack on an
if-elserather than refactor it into a more elegant structure (such as a strategy pattern or polymorphism)? - Do you feel a knot of dread before touching certain "ancient code" in the project, knowing that even a tiny change could trigger unforeseen chain reactions?
Part Three: Signals of the "Local Optimum"
- During code review, have you found that AI-generated code looks perfect within a single file, yet shatters the module boundaries of the whole project (for example, the UI layer calling a database model directly)?
- Does your project have a clear set of architecture documents, yet you rarely cite their principles when making requests to AI?
- Have you noticed that code implementing the same function (such as API requests or date formatting) is scattered across a dozen different places, with no unified abstraction?
- When you ask AI to solve a problem, do its solutions often make you sigh and think, "It works, but something just feels off"?
Diagnosis Results
- 0–2 "yes" answers: Congratulations — your collaboration with AI is quite healthy. You have already grasped, by instinct, the art of managing AI. The later chapters of this book will provide you with more systematic theory and tools to take your abilities to the next level.
- 3–6 "yes" answers: Yellow alert. You are beginning to feel the side effects of AI collaboration. Your project is being slowly eroded, but there is still time to recover. You need to immediately begin practicing the "constraint" techniques covered in the following chapters, and proactively reclaim command of the project.
- 7 or more "yes" answers: Red alert! Your AI collaboration is on the eve of losing control — it may well have already slipped out of control. You have very likely become AI's "code babysitter," spending most of your time cleaning up after it. You need a thorough "revolution in thought and action." Set aside your coding work, read the rest of this book seriously, and resolve to fundamentally change the way you collaborate with AI, starting with the very next requirement.
Remember, tools are neither good nor bad in themselves. A hammer can build a house, or it can smash your fingers. AI, this unprecedented "divine hammer," is no exception. Learning to recognize these three traps is the first and most important protective charm that lets you grasp this divine hammer — without having it turn against you.