Topic Guide
Debugging Method: Reading the System Instead of Guessing
Every bug in this hub started with a plausible wrong theory. A banner looked broken in Safari and the real cause was a missing child element. A slow page looked like a performance problem and the real cause was that it was answering a question nobody needed answered. A firewall rule looked like it wasn't working, mid-incident, and the real cause was a local clock read against UTC log timestamps. In each case the fast path and the correct path diverged right at the first assumption, and the fix was always the same move: stop theorizing and go read the actual state, the actual logs, the actual DOM, the actual query.
The posts below are grouped by the shape of the trap. Some are naming collisions, two different systems claiming the same global or the same querystring key, with no error and no exception to point at. Some are a framework quietly owning something you assumed was yours, a DOM node a library rebuilds on state change, an onFocus event that fires during initialization instead of on real interaction. Some are pure log discipline, a `tail -15` that hid the one line that would have explained everything, a stale credential that failed two code paths at once and looked like two separate bugs.
None of these needed a smarter engineer. They needed someone willing to read the whole log instead of the tail, the actual DOM instead of the assumed one, the real timezone instead of the convenient one. That's the whole method, and it's repeatable on purpose.
21 posts in this guide, by DX
Start here
UTC logs, a local clock, and the canary request: timezone discipline in an incident
I spent rounds of an active production incident convinced a firewall rule wasn't working. The log timestamps looked like they fell after the rule went in, and the bad traffic was still showing up.
Two globals claiming the same name
-
clipboard.js Named Its Global 'Clipboard'. So Did the Browser. Boom.
A copy button broke with no deploy because clipboard.js and the browser both claimed window.Clipboard. A three-level fallback fixed it without a version bump.
-
Why schedules broke around March 4, 1973: a getTime() digit-count sort bug
A schedule sorted correctly for years until one getTime() value gained a digit. A number-to-string promotion made the sort lexicographic, with a breaking point in 1973.
-
Your JIRA Session Won't Cross Into an Iframe. Here's the Reused-Popup Workaround.
Cross-origin cookie isolation means an embedded JIRA iframe always renders logged-out. The fix is a popup with a fixed window name so every navigation reuses the same window instead of spawning a new one.
The framework owns the DOM node, not you
-
PayPal Pay-Later Messages Won't Re-Render, So Destroy the Node and Recreate It
A checkout total updated after a coupon, but its Pay Later message retained the old amount. Replacing the SDK-owned DOM node and scheduling a fresh render resolved the stale estimate.
-
A Tiptap Editor That's Invisible Until You Add ProseMirror's Base CSS
A headless Tiptap editor mounted successfully but offered no visible empty state. A minimum height, deliberate wrapper styling, and the placeholder extension made the editing surface discoverable.
-
Tiptap v3 Fires onFocus During Init, So Subscribe to the DOM Instead
A formatting toolbar opened when a Tiptap editor initialized instead of when a person interacted with it. Moving the listener to the editor DOM node avoided the unwanted initial state in this integration.
A business rule hiding in the wrong layer
-
A Client Tree-Walk That Was Really a Server Business Rule
A client-side delete gate re-derived team counts and treated plan price as money received. Moving the decision to server-owned counts and transaction totals aligned the gate with its actual policy.
-
GetAccountFilter and the Case for an Explicit Context You Can Override
A helper function in my sports SaaS app had a default argument that pointed at the wrong account. Fixing it meant threading the same eight-character override through 27 call sites by hand.
-
A New Feature Is the Best Fuzz-Tester for Your Existing Data Model
A new guard exposed a missing lifecycle flag. When a feature needs a genuine domain fact, adding it to the shared status model can be safer than re-deriving it in one consumer.
When a small bug becomes a bigger incident
-
When a Small Bug Turns Into a Bigger Incident
A simple defect can take far longer to resolve when the surrounding traffic, safeguards, and checks do not tell the same story.
-
When a Small Experiment Starts Touching Too Much
A useful experiment can become hard to trust when it quietly reaches shared work that was never part of the original question.
CSS, layout, and "obviously a browser bug"
The question was wrong before the fix was
URL contracts and querystring collisions
What the logs don't tell you
-
The IPv6 fix bit back: how localhost resolving to ::1 quietly broke a three-hop proxy chain
A morning IPv6 tweak made localhost resolve to ::1 and every request hang for ten seconds. Timed curl probes found the dead hop in a three-hop proxy chain.
-
The Button Clicked and Nothing Happened: IIS Ate My JSON Error and Handed Back HTML
My backend returned a 400 with a JSON error body. IIS replaced it with HTML, the client swallowed the parse error, and the button did nothing.
-
Three compounding bugs spawned a burst of near-identical tickets, and `tail -15` hid the evidence
Four near-identical JIRA tickets showed up seconds apart, with no human in the loop. My first guess was a flaky board or a double-submit somewhere. It wasn't either.
-
Edit a label, get " back: the HTML-entity round-trip bug
A coach opens an edit dialog and the label field shows entity garbage instead of their text. Here is the decode/encode boundary problem behind it, the fix, and the one ordering detail that will burn you if you get careless.
-
The stale credential that failed two different code paths at once
A sandbox checkout died with "merchant login ID or password is invalid." The exact same credentials had authenticated cleanly in a standalone Python proof minutes earlier, returning I00001.
Hitting one of these walls in your own codebase or your own machine? Talk it through with us, or read the rest of the Build Log.