# How to Fix Any Bug: A Systematic Framework

> Learn the step-by-step reduction process Dan Abramov uses to solve frustrating software bugs when AI and guessing fail.
- Title: How to Fix Any Bug: A Systematic Framework · Overreacted
- Summary: Learn the step-by-step reduction process Dan Abramov uses to solve frustrating software bugs when AI and guessing fail. When theories fail, systematically…
- Keywords: debugging, react, programming, software engineering, technology, design, Fix, Any, Bug, Systematic, Framework, Overreacted
- Source: Overreacted — https://overreacted.io/how-to-fix-any-bug
- Read time: 3 min
- Topics: debugging, react, programming, software engineering, technology, design
## A mysterious scroll bug
While building an app, adding a server call to a button click suddenly broke page scrolling, turning smooth movement into erratic jitter.

> So, adding a remote call somehow broke scrolling.
## Challenging safe assumptions
Re-rendering in React should always be safe, and refetching data shouldn't disrupt an ongoing scroll animation, pointing to a hidden flaw.

> In React, a re-render should always be safe to do.
## Delegating blindly to AI
Asking an AI assistant to fix the bug directly led to a series of rewrites, each accompanied by false declarations that the issue was solved.

> Each time, Claude would proudly declare that the problem was solved.
## A common engineering mistake
AI models repeatedly fail in the exact same way human engineers do when they attempt fixes without a clear, repeatable verification path.

> Claude was repeatedly wrong because it didn’t have a repro.
## Defining a true repro
A reproduction case is a reliable set of instructions stating what to do, what to expect, and what actually happens during execution.

> A repro... is a sequence of instructions then, when followed, gives you a reliable way to tell whether the bug is still happening.
## The cost of flaky repros
Unreliable bugs force endless re-testing and guesswork, making high-fidelity, reproducible test conditions essential for rapid progress.

> If my repro was unreliable... I’d either have to gradually remove different sources of uncertainty... or live with the producitivity hit.
## Sensory limits in debugging
The initial repro relied on visual observation of scrolling jitter, something an AI without eyes or direct perception could not evaluate.

> The problem is that 'scrolling jitters' from my repro didn’t mean anything to Claude.
## Trading repros for progress
When a bug cannot be observed directly, you can swap your original reproduction case for a simpler proxy test that moves you forward.

> You can trade a repro for another repro as long as you’re able to convince yourself that it’ll help you make progress on the original problem.
## Narrowing to a proxy metric
A new automated test measured the exact scroll coordinates before and after clicking the button using Playwright instead of relying on sight.

> Measure the document scroll position. Click the button. Measure the document scroll position again.
## The trap of false proxies
Proxy repros carry the risk of testing an unrelated timing artifact or reading state too early, leading developers to reject working solutions.

> One common pitfall with narrowing a repro is that you think you found a good one, but actually your new repro captures some unrelated problem.
## Validating with positive controls
To confirm a proxy repro is accurate, verify that applying a known workaround, like commenting out the server call, produces a passing result.

> This is why, whenever you narrow a repro, you should also confirm that a positive result... is still possible to obtain with the new repro.
## Systematic reduction protocol
Systematically pare down code step-by-step, committing only when the bug persists, and reverting immediately whenever a change hides it.

> Remove something from the relevant code... If the bug is still there, commit the changes.
## The trap of early theories
AI and humans frequently abandon the broken codebase to build side repros based on premature hypotheses, losing sight of the active bug.

> Claude got too carried away testing its own theories and ended up with a bunch of test cases that don’t actually exhibit the bug.
## Applying well-founded recursion
Guaranteed progress requires monotonic reduction, ensuring every commit shrinks the code surface while continuously preserving the reproducible bug.

> You want to know that you’re always, always making incremental progress and the repro keeps getting smaller.
## Isolating environmental scope
Stripping down components until the code was placed in a single file revealed that the bug only occurred when nested inside a root layout.

> Something was breaking when it was nested inside the root layout.
## Spotting the unexpected culprit
The root cause was React Router's ScrollRestoration component, which buggily triggered on data revalidations instead of only full route changes.

> Since my network call (via an action) revalidated the route, it triggered ScrollRestoration during scrollIntoView, causing the jitter.
## Hidden dependency traps
AI scaffolding tools can quietly pin outdated versions of core libraries, introducing fixed upstream bugs into fresh projects.

> The project was set up by Claude which for some inexplicable reason decided I should use an old version of a core dependency.
## The power of elimination
When theories run dry, systematically deleting code while preserving a broken repro will always isolate the true cause of any bug.

> This exact workflow—removing things one by one while ensuring the bug is still present—saved my ass many times.
## Key takeaway

When theories fail, systematically delete code piece by piece while maintaining a broken repro until the root cause is isolated.