The X-Ray: DevTools and the Method
Faced with a bug, there are two possible people: the one who changes something and reloads, and the one who states a hypothesis and checks it. The gap between them is a career.
Something doesn't work. Watch what happens next, because this is one of the few forks in a developer's life that genuinely compounds.
One person comments out a line, reloads, changes a value, reloads, adds !important, reloads, moves a call, reloads. Sometimes it starts working, and they don't know why. The bug is now hidden rather than gone, and nothing was learned.
The other says: here's what I expected, here's what happened, here's the first place they could have diverged. Then checks. Then narrows. This takes longer for the first ten minutes and less time for the rest of your life.
Debugging isn't a talent. It's a binary search over the distance between what you expected and what happened.
The method
Six steps. They're not ceremony — skipping any of them is where the hours go.
1. Reproduce it reliably. A bug you can't summon on demand can't be fixed, only guessed at. Find the exact sequence. If it happens sometimes, the "sometimes" is a clue: it usually means timing, cached data, or a specific record.
2. State expected and actual precisely. Not "the list is broken" but "I expected twelve items sorted by date, I got twelve items in random order." Half of all bugs are solved by writing that sentence honestly, because it forces you to notice what you actually know.
3. Localise by halving. Somewhere between the data leaving the server and the pixels on screen, correct became incorrect. Check the middle. Was the data right there? Then the fault is downstream. Wasn't it? Upstream. Four halvings take you from a thousand lines to sixty.
4. Form one hypothesis, and make it falsifiable. "The date is arriving as a string, so the comparison is lexicographic." That's testable in ten seconds. "Something's wrong with the sorting" isn't a hypothesis; it's a restatement of the symptom.
5. Change one thing. Change three, and if it starts working, you've learned nothing and can't undo the two that were irrelevant.
6. Confirm the fix explains everything. If the bug went away and you can't say why, it didn't go away — it moved. This is the step people skip when they're tired, and it's the one that produces the same bug again in three weeks.
Read the message
Before any tool: read the error, all of it, out loud if necessary. Beginners' eyes slide off error text as though it were decoration, and it's usually the answer.
"Cannot read properties of undefined (reading 'name')" tells you three things: something was expected to be an object, it was undefined, and the fix is wherever that value should have been produced — not at the line that exploded.
The stack trace beneath is a history of how you got there, most recent first. Skip the framework frames and find the first line in your code; that's where to start. Devtools can hide library frames for you, which turns a wall of forty lines into three that matter.
The panels you'll actually live in
Elements — the real DOM, which is not always your HTML. Select an element and the Styles pane shows every rule that applied, what was overridden, and the final computed value. This is the answer to nearly every CSS question, in two seconds, and the box model diagram at the bottom tells you where spacing came from. The Accessibility tab beside it shows the second interface from that article.
Console — errors, warnings and your own output. console.log is legitimate; it's not the beginner's tool that people imply. But it has better relatives: console.table for arrays of objects, which turns twenty lines of noise into a readable grid; console.time for measuring; console.count for "how many times did this run"; console.trace for "who called this". And $0 refers to the element currently selected in Elements, which lets you poke at it live.
Sources — where debugging becomes serious. Set a breakpoint and execution pauses with every variable in scope visible and the call stack laid out. This is strictly more information than console.log gives you, because you see everything at that moment instead of the one thing you thought to print.
The advanced forms are worth learning early because they turn impossible bugs into ordinary ones: conditional breakpoints (pause only when the id equals 4712 — instead of stepping through 400 iterations), DOM breakpoints (pause when anything modifies this element, which finds the mystery code that's changing your button), and XHR breakpoints (pause when a request to this URL fires).
Network — status codes, timing, headers, the actual payload sent and received. When the front end and back end disagree about whose fault it is, this panel settles it in one screenshot. Throttling and "disable cache" live here too.
Application — local storage, session storage, cookies, service workers. Where "why is it still logged in?" and "why is the old version being served?" get answered.
Performance — the flame chart of what the main thread was doing. The next article uses it properly; for now, know it exists and that it shows you long tasks.
Breakpoints versus logs
Both are correct tools with different shapes.
Breakpoints answer "what is the state at this moment?" — every variable, the whole stack, live. Use them when something is wrong here and you don't know why.
Logs answer "what happened over time and in what order?" — which is exactly what breakpoints are bad at, since pausing changes timing and you can't pause your way through a hundred iterations comfortably.
So: logs for sequences and async ordering, breakpoints for state. Two other pieces of vocabulary: a debugger statement in code is a breakpoint you can commit temporarily and put inside a condition, and "blackboxing" library files stops the debugger stepping into framework internals you don't care about.
When it "works on my machine"
That sentence is not an excuse, it's a list of hypotheses. Something differs between your machine and theirs, and the candidates are known: cache (do a hard reload, or open a private window), extensions (they inject scripts and block requests), another browser, different data (their account has fifty items, or an empty field, or a name with an apostrophe), screen size, network speed, locale and time zone — the classic date-off-by-one bug — and an older build than you think you're testing.
Going down that list beats staring at code you've already read four times.
The parts that aren't technical
Explain it out loud. The rubber duck works, and the How to Learn sphere explains why: in your head, knowledge exists as a draft where a feeling of coherence stands in for actual links. Speaking forces the draft into sentences, and the gap surfaces mid-sentence — usually the sentence where you say "and then it gets the id from... hang on."
Step away. After a long stare, the focused mode is running the same route through the same three files. The diffuse mode — walking, showering, doing something else — is what finds the route you haven't taken. This isn't a metaphor; it's the two-modes article, and the reason so many bugs are solved in the shower is that stepping away is a technique, not a surrender.
Time-box it. Thirty minutes with no progress means the strategy is wrong, not that you need forty more minutes of it. Change something structural: ask someone, explain it to someone, rewrite the section from scratch, or start again from step one.
Keep a bug journal. Three lines per real bug: symptom, actual cause, how you found it. After a month you'll see your own repeating categories — "I keep assuming the data arrived", "I keep forgetting async ordering" — and those are worth more than any list of general advice, because they're about you. This is the debrief the How to Learn sphere recommends after exams, applied to the thing you do every day.
In practice
Ban reload-and-hope for a week. Before every change, say what you expect it to prove. This one rule installs the method.
Learn one panel a week. Elements, then Console, then Sources, then Network. Twenty minutes each, spaced out — that's deliberate practice on the tool you'll use for the next decade.
Replace three console.logs with a breakpoint and look at the full scope. Most people never see how much more information is available.
Use console.table on the next array of objects you inspect. Small thing, immediately obvious improvement.
Write the expected-versus-actual sentence before touching anything. Every time.
Start the bug journal today, even at three lines a week.
Check yourself
Close the article and answer in your own words:
- What are the six steps of the method, and which one do people skip when tired?
- Why is "the fix works but I don't know why" not a fix?
- How do you read a stack trace, and which line do you start from?
- What does the Styles pane tell you that guessing doesn't?
- When is a breakpoint better than a log, and when is it the reverse?
- List six hypotheses behind "works on my machine".
- Why does explaining a bug out loud solve it so often?
In short
- Debugging is a method: reproduce, state expected versus actual, halve the search space, form one falsifiable hypothesis, change one thing, confirm the explanation.
- The error message and stack trace usually contain the answer; read them fully and start from the first line of your own code.
- Elements answers CSS questions in seconds, Network settles front-versus-back arguments, Sources shows every variable at a moment, Application explains stale state.
console.table,console.time,console.count,console.traceand$0are worth more than anotherconsole.log.- Breakpoints reveal state; logs reveal sequence — and conditional, DOM and XHR breakpoints turn impossible bugs into ordinary ones.
- "Works on my machine" is a list of hypotheses: cache, extensions, browser, data, size, network, locale, build.
- Explaining out loud exposes the gap, stepping away engages the diffuse mode, and thirty minutes without progress means change strategy, not try harder.
- A three-line bug journal reveals your own repeating mistake categories, which is feedback nothing else gives you.