The bug that looked like a React problem
I was chasing a hydration mismatch in a React Native app. The UI was rendering a fallback state when it shouldn’t, and the checks around it looked fine at first glance.
I pasted the relevant component code into Claude and asked what might be going on. The response came back fast, structured, and confident.
The very convincing wrong answer
Claude decided the problem was in my useEffect cleanup. It pointed to a common pattern: stale closures in async effects and state updates firing after unmount. It walked through how this could cause inconsistent UI and hydration-like symptoms.
Then it suggested a refactor. It rewrote the effect using safer patterns, proper cleanup, and a more defensive approach to async work. The code looked like the kind of thing a senior React dev would send in a code review.
I trusted it and spent the next 45 minutes refactoring every effect in the component tree to match that advice.
The hydration mismatch was still there. Exactly the same.
At that point, I finally did what I should have done at the start: I stopped treating it as a React problem and looked at the data coming in.
The actual bug: a string pretending to be null
The real issue was nowhere near useEffect.
I wrote about this earlier in Why I Still Hand‑Code Every Hover State.
In the API layer, the endpoint was returning the string "null" instead of an actual null value. React Native’s JSON handling treated that as a normal, truthy string.
My UI logic was doing a simple falsy check to decide whether to show a fallback:
if (!data.value) {
// show fallback
}
With a real null, that would have worked. With the string "null", the condition never triggered. The component rendered the wrong state, and it looked like a hydration or state sync issue from the outside.
Claude never had a chance to see that. I had only pasted the component code. From its perspective, all symptoms pointed to React. It diagnosed the visible symptoms, not the disease.
The confidence trap
The problem was not that the AI guessed. Guessing is fine. The problem was how it sounded while guessing.
The explanation came wrapped in the same tone it uses for things that are actually true. No hedging, no “this is one possibility,” no suggestion to check the network layer first. Just a clean narrative about effect cleanup issues and a polished refactor.
I trusted it because it sounded like the kind of architectural insight you pay for. It pressed all the right “this is senior advice” buttons: mentions of stale closures, async patterns, subtle lifecycle edge cases. That trust cost me an hour in the wrong layer of the stack.
More on this in my article The frontend code I deliberately keep boring.
The rule I use now
That session gave me a new rule:
If the AI gives me an architectural answer, I verify it with a 10-minute manual test before I touch the code.
In this case, that would have meant logging the API response before refactoring anything in the React layer. Ten minutes of looking at raw data would have exposed the "null" string immediately.
Instead, I skipped the boring verification step because the answer felt right. The bug was subtle, the explanation was elegant, and the fix was “clean up your effects,” which always sounds like a good idea. I let the aesthetics of the answer override basic debugging discipline.
How I pair with AI now
This changed how I use AI for debugging. I stopped treating it as a diagnostician and started treating it as a brainstorming partner.
My default pattern now looks like this:
- Ask for multiple possible causes of the bug, not a single “best guess.”
- Rank those causes myself based on what I know about the system: recent changes, weak spots, layers that often break.
- Use AI for concrete artifacts (queries, small refactors, log statements), not for deciding where to spend the next hour.
The key shift is that I keep ownership of the diagnosis. The AI can widen my search space, but it does not get to choose the search order.
Read also Where Cursor Stops and My Judgment Begins.
The real cost of plausible answers
The hour I lost was not wasted; it was tuition. It taught me that the most expensive AI answers are the plausible ones, because they are the ones you act on without questioning.
The fix for that is not to stop using AI. It is to put a small verification tax in front of every confident answer. A quick log, a minimal reproduction, a sanity check in a different layer. Ten minutes up front is cheaper than an hour refactoring the wrong thing.
AI did not misplace that hour. I did, by outsourcing my skepticism to a confident paragraph and a nice-looking code block.
Subscribe to my newsletter to get the latest updates and news
Member discussion