← Back to articles
Technology

Debug "Works on My Machine" With 4 Axes

Debug "Works on My Machine" With 4 Axes

"It works on my machine" is the sentence that kicks off the longest bug investigations. The gap between a bug report and your local setup usually hides in one of four places: environment variables, data, browser/client, or timing. Chasing reproduction blindly just burns time on back-and-forth questions. Here's a mechanical way to narrow it down along those four axes.

Why "works on my machine" happens

Most unreproducible bugs trace back to one of these:

  • Environment variables: feature flags, API URLs, timezone settings
  • Data: empty states, legacy formats, or permissions that only exist on the reporter's account
  • Browser/client: extensions, cache, viewport size, OS fonts
  • Timing: non-deterministic behavior from network latency or race conditions

Work through these four axes in order and you stop guessing.

1. Environment variables

  • Diff the two environments mechanically: diff <(env | sort) remote_env.txt
  • Check whether a feature flag is on for one side and off for the other
  • Check TZ and locale — date-boundary bugs almost always live here

2. Data

  • Test with an empty array, null, or zero-count state
  • Look for legacy-format or mixed-encoding records that only exist in one dataset
  • Compare the role/plan of your account against the reporter's account

3. Browser/client

  • Reproduce in an incognito window with all extensions disabled
  • Clear the service worker and cache, then reload
  • Match viewport width and device pixel ratio to the reported environment

4. Timing

  • Throttle the network to "Slow 3G" in Chrome DevTools and retry
  • Try double-clicking or rapid repeated actions
  • Look for code that assumes a specific order of async completion

Real example: a "the button doesn't work" bug that was actually about network speed

A report read: "clicking submit does nothing." It worked instantly on the developer's machine. Environment, data, and browser all matched, leaving timing as the last axis. Throttling to Slow 3G and clicking twice reproduced it: the second click fired a competing request that silently swallowed the first one's error. The fix was simply disabling the button while the request was in flight. Without walking the four axes in order, this could easily have been chased as a browser-specific rendering bug instead.

A checklist before you start digging

  1. Do environment variables and feature flags match? (yes → next)
  2. Are you testing with the same data — same record, same account? (yes → next)
  3. Does it reproduce in the same browser and viewport? (yes → next)
  4. Does it only happen under rapid actions or a slow connection?

If all four match and it still doesn't reproduce, the report is probably missing first-hand evidence — console logs, network logs, or the exact steps taken.

Putting this into practice with Bugoon

Bugoon attaches a screenshot, annotations, and a recorded sequence of interaction steps to every bug report sent from the widget. With the interaction steps already captured, the "timing" axis — what was clicked, in what order — is answered before you even start investigating. When the report flows into a GitHub Issue, that context goes with it, cutting out at least one round-trip of "what did you actually click."

If reports also carried environment metadata automatically — environment name, build number, browser, and viewport size — the first two axes could largely be checked before an engineer opens the ticket. That's not something the widget captures today beyond the screenshot and URL, but it's a direction worth watching for tightening this workflow further.

Streamline bug reporting for your team.

Bugoon is free to get started. Add one line of code to your site and transform how your team handles bugs.

Get Started