PostHole
Compose Login
You are browsing eu.zone1 in read-only mode. Log in to participate.
rss-bridge 2025-10-21T00:00:00+00:00

How to Fix Any Bug

The joys of vibecoding.


How to Fix Any Bug

October 21, 2025

Pay what you like

I’ve been vibecoding a little app, and a few days ago I ran into a bug.

The bug went something like this. Imagine a route in a webapp. That route shows a sequence of steps—essentially, cards. Each card has a button that scrolls down to the next card. Everything works great. However, as soon as I tried to also call the server from that button, scrolling would no longer work. It would jitter and break.

So, adding a remote call somehow broke scrolling.

I wasn’t sure what’s causing the bug. Clearly, the newly added remote server call (which I was doing via React Router actions) was somehow interfering with my scrollIntoView call. But I couldn’t tell how. I initially thought the problem was that React Router re-renders my page (an action causes a data refetch), but in principle there’s no reason why a refetch would interfere with an ongoing scroll. The server was returning the same items, so it shouldn’t have changed anything.

In React, a re-render should always be safe to do. Something else was wrong—either in my code, or in React Router, or in React, or even in the browser itself.

How do I fix this bug?

Can I get Claude to fix it?


Step 0: Just Fix It

I told Claude to fix the problem.

Claude tried a few things. It rewrote conditions in the useEffect that contains the scrollIntoView call and said that the bug was fixed. But that didn’t help. It then tried changing smooth scrolling to instant, and a few other things.

Each time, Claude would proudly declare that the problem was solved.

But it was not!

The bug was still there.

This might sound like I’m complaining about Claude, but really the impetus for writing this article is that I see human engineers (including myself) make the same mistakes. So I wanted to document the process I usually follow to fix bugs.

Why was Claude repeatedly wrong?

Claude was repeatedly wrong because it didn’t have a repro.


Step 1: Find a Repro

A repro, or a reproducing case, is a sequence of instructions then, when followed, gives you a reliable way to tell whether the bug is still happening. It’s “the test”. A repro says what to do, what’s expected to happen, and what is actually happening.

From my perspective, I already had a good repro:

  • Click the button.
  • The expected behavior was scrolling down, but the actual behavior was scroll jitter.

Even better, the bug happened every time.

If my repro was unreliable (e.g. if it happened just 30% of attempts), I’d either have to gradually remove different sources of uncertainty (e.g. recording the network and mocking it in future attempts), or live with the producitivity hit of having to test every potential fix many more times. But luckily, my repro was reliable.

And yet, to Claude, my repro essentially didn’t exist.

The problem is that “scrolling jitters” from my repro didn’t mean anything to Claude. Claude doesn’t have eyes or other ways to perceive the jitter directly. So Claude was essentially operating without a repro—it tried to fix the bug, but didn’t do anything specific to verify it. That is too common, even with the best of us.

In this case, Claude couldn’t have followed my repro exactly since it couldn’t “look” at the screen (taking a few screenshots wouldn’t capture it). So my first repro was unsuitable if I wanted Claude to fix it. This might seem like a problem with Claude, but it’s actually not uncommon when working with other people—sometimes a bug only happens on one machine, or for a specific user, or with specific settings.

Luckily, there is a trick. 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.

Here’s how you can change your repro, and some things to watch out for.


Step 2: Narrow the Repro

Changing the repro you’re working with is always a risk. The risk is that the new repro has nothing to do with your original bug, and solving it is a waste of time.

However, sometimes changing a repro is unavoidable (Claude can’t look at my screen, so I have to come up with something else). And sometimes it is hugely beneficial for iteration (say, a repro that takes ten seconds is vastly more valuable than a repro that takes ten minutes). So learning to change repros is important.

Ideally, you’d trade a repro for a simpler, narrower, more direct repro.

Here’s the idea I suggested to Claude:

  • Measure the document scroll position.
  • Click the button.
  • Measure the document scroll position again.
  • The expected behavior is that there is a delta, the actual behavior is there’s none.

My thinking was that this seems roughly equivalent to the problem I saw with my own eyes. Although this repro doesn’t capture the jitter, failing to scroll down is likely related. Even if it’s not the only problem, it’s worth fixing this on its own.

Claude added some console.logs, opened the page via Playwright MCP, and clicked around. Indeed, the scroll position was not changing despite button click.

Okay, so now Claude is able to verify the bug exists!

Are we done with finding the repro?

Actually, we’re not!

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 that presents in a similar way. This is a very expensive mistake to make because you can waste hours chasing solutions to a different problem than the one you wanted to solve.

For example, it’s possible that Claude simply was reading the scroll position too early, and even if the bug was fixed, it would still “see” the position unchanging. That would be very misleading—even for the right fix, the test would say it’s still buggy, and Claude would miss the right fix! That happens to human engineers too.

This is why, whenever you narrow a repro, you should also confirm that a positive result (“everything works”) is still possible to obtain with the new repro.

This is easier to explain by an example.

I told Claude to comment out the network call (which originally surfaced the bug). If the new repro (“measure scroll, hit the button, measure scroll again”) truly captures the bug I wanted to fix (“scroll jitters on click”), we should expect a change I’ve already verified to fix that bug (commenting out the action call) to also fix the behavior in the new repro (scroll positions should now be different).

And that’s what happened! Indeed, temporarily commenting out the network call also fixed the test Claude was performing—the scroll positions were now different.

At this point, it’s worth trying to change the code a few times in either direction (comment it in, comment it out) to verify that each edit predicts the new repro result. (It’s also worth doing other edits to rule out that every second edit works.)

The scrolling measurement still seemed correlated to that network call.

This still doesn’t mean that the new repro represents the same problem. It could be something unrelated, or just a part of it. It’s always a risk to change the repro. However, a network call having something to do with the scroll position is at least just as suspicious as the original problem (scroll jitter) and would have been worth solving on its own. This is a good enough reason to continue with the new repro.


Step 3: Remove Everything Else

I created a new branch and asked Claude to follow the following workflow:

  • Run the repro to verify the bug is present.

[...]


Original source

Reply