---
title: "19 — Browser DevTools — Where JavaScript Gets Diagnosed"
uid: browser-devtools
tags: ["devtools", "roadmap:javascript", "chrome", "debugging", "memory-leaks", "performance", "javascript"]
excerpt: "DevTools isn't one tool — it's a shell holding several (Sources, Memory, Performance, Network), and each failure mode has a specific panel that answers it."
date: 2026-08-13T03:28:03+0000
source: https://www.aveshina.my.id/en/blog/browser-devtools
---

"The F12 panel" was my DevTools model, which made each debugging session a hunt through the same tabs. The idea that everything else hangs off: **DevTools isn't one tool — it's a shell holding several distinct tools, and each failure mode I face (a wrong value, a leak, a slow render) has a specific panel that exists to answer exactly that question.** [1]

The framing that finally landed is matching the panel to the question. When I used to flail — open DevTools, poke around, hope — I was treating it as one monolithic debugger. The shift was treating each tab as a specialized instrument: there's a panel for "what's this value," a panel for "where did the memory go," a panel for "why is this slow," and a panel for "what did the network do." Naming the question first, then opening the matching panel, cut my debugging time dramatically.

```figure
<svg viewBox="0 0 740 280" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="A DevTools shell with five specialized panels, each answering a different question: Console for 'what's this value' (REPL, logging), Sources for 'why is the code wrong' (breakpoints, step-through), Memory for 'where did the memory go' (heap snapshots), Performance for 'why is it slow' (timeline), Network for 'what did the server do'.">
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">
    <text x="370" y="22" font-size="11" font-weight="700" fill="#475569" text-anchor="middle">DevTools — one shell, several specialized panels</text>

    <!-- Console -->
    <rect x="20" y="40" width="135" height="210" rx="8" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.5"/>
    <text x="87" y="62" font-size="12" font-weight="700" fill="#1e1b4b" text-anchor="middle">Console</text>
    <text x="87" y="92" font-size="9" font-weight="700" fill="#1e1b4b" text-anchor="middle">"what's this value?"</text>
    <text x="87" y="118" font-size="9" fill="#475569" text-anchor="middle">REPL</text>
    <text x="87" y="134" font-size="9" fill="#475569" text-anchor="middle">console.log</text>
    <text x="87" y="150" font-size="9" fill="#475569" text-anchor="middle">live inspection</text>

    <!-- Sources -->
    <rect x="170" y="40" width="135" height="210" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="237" y="62" font-size="12" font-weight="700" fill="#052e16" text-anchor="middle">Sources</text>
    <text x="237" y="92" font-size="9" font-weight="700" fill="#052e16" text-anchor="middle">"why is this wrong?"</text>
    <text x="237" y="118" font-size="9" fill="#475569" text-anchor="middle">breakpoints</text>
    <text x="237" y="134" font-size="9" fill="#475569" text-anchor="middle">step through</text>
    <text x="237" y="150" font-size="9" fill="#475569" text-anchor="middle">call stack</text>
    <text x="237" y="166" font-size="9" fill="#475569" text-anchor="middle">watch / scope</text>

    <!-- Memory -->
    <rect x="320" y="40" width="135" height="210" rx="8" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="387" y="62" font-size="12" font-weight="700" fill="#422006" text-anchor="middle">Memory</text>
    <text x="387" y="92" font-size="9" font-weight="700" fill="#422006" text-anchor="middle">"where's the leak?"</text>
    <text x="387" y="118" font-size="9" fill="#475569" text-anchor="middle">heap snapshot</text>
    <text x="387" y="134" font-size="9" fill="#475569" text-anchor="middle">allocation</text>
    <text x="387" y="150" font-size="9" fill="#475569" text-anchor="middle">timeline</text>
    <text x="387" y="166" font-size="9" fill="#475569" text-anchor="middle">retainers</text>

    <!-- Performance -->
    <rect x="470" y="40" width="135" height="210" rx="8" fill="#fce7f3" stroke="#db2777" stroke-width="1.5"/>
    <text x="537" y="62" font-size="12" font-weight="700" fill="#500724" text-anchor="middle">Performance</text>
    <text x="537" y="92" font-size="9" font-weight="700" fill="#500724" text-anchor="middle">"why is it slow?"</text>
    <text x="537" y="118" font-size="9" fill="#475569" text-anchor="middle">timeline record</text>
    <text x="537" y="134" font-size="9" fill="#475569" text-anchor="middle">flame chart</text>
    <text x="537" y="150" font-size="9" fill="#475569" text-anchor="middle">Lighthouse</text>

    <!-- Network -->
    <rect x="620" y="40" width="110" height="210" rx="8" fill="#fee2e2" stroke="#dc2626" stroke-width="1.5"/>
    <text x="675" y="62" font-size="12" font-weight="700" fill="#7f1d1d" text-anchor="middle">Network</text>
    <text x="675" y="92" font-size="9" font-weight="700" fill="#7f1d1d" text-anchor="middle">"server OK?"</text>
    <text x="675" y="118" font-size="9" fill="#475569" text-anchor="middle">requests</text>
    <text x="675" y="134" font-size="9" fill="#475569" text-anchor="middle">status codes</text>
    <text x="675" y="150" font-size="9" fill="#475569" text-anchor="middle">payloads</text>
    <text x="675" y="166" font-size="9" fill="#475569" text-anchor="middle">timing</text>
  </g>
</svg>
```

## Sources: stepping through code

When the bug is "the code is doing the wrong thing," **Sources** is the panel [2]. The workflow: open the file, click a line number to set a **breakpoint**, trigger the action, and execution pauses there. From the paused state I can:

- **Step over / into / out** — walk the code line by line, into function calls or past them.
- **Inspect variables** — hover or use the Scope pane to see live values at the current line.
- **Read the call stack** — the chain of function calls that got me here, clickable to jump to each frame.
- **Watch expressions** — pin specific variables or expressions and watch them change as I step.

The shift from console.log debugging to breakpoint debugging was the single biggest speedup in my workflow. console.log tells me a value *at one line I guessed mattered*; a breakpoint lets me *stop and look around* — at any variable, the stack, the scope — and then step forward one line at a time watching state evolve. For anything more complex than "what's this one value," breakpoints win.

The **Console** sits alongside Sources as a live REPL — I can type any expression and see its value in the current context. It's where quick console.log output lands and where I scratch-evaluate ("what does document.querySelector('.x') return right now?").

## Memory: finding leaks

When the bug is "the tab's memory keeps climbing," **Memory** is the panel [3][4]. The technique that actually works:

1. Take a **heap snapshot** at a clean state.
2. Perform the suspected leaky action (open and close a view, navigate, repeat).
3. Force a garbage collection (the Memory panel has a button).
4. Take a second snapshot.
5. Compare the two — the delta shows what was *allocated and not freed*.

Each retained object shows its **retainers** — the reference path keeping it alive. That's the diagnostic that turns "memory is high" into "this specific listener on this specific element is holding this specific closure." The retainer chain is the answer to *which reference path did I fail to cut* — exactly the question the reachability model from the memory notes predicts.

## Performance: why it's slow

When the bug is "the interaction janks," **Performance** is the panel [5]. I hit Record, perform the slow interaction, stop, and read the timeline — a flame chart showing which functions ran, for how long, and where the main thread was blocked. Long yellow tasks are JavaScript; long purple are layout/recalculation; the goal is finding the one function or layout pass eating the frame budget.

**Lighthouse**, accessible from DevTools, runs an automated audit across performance, accessibility, best practices, and SEO — a checklist of known issues with links to fix each. It's the right starting point for "I don't know what to optimize"; the Performance timeline is the right tool once I have a specific interaction to profile.

## How I use this

The rule I keep is: **name the failure mode, then open the panel.** Wrong value or behavior → Sources with a breakpoint. Memory climbing → Memory panel, two snapshots, read the retainers. Slow or janky → Performance timeline, find the long task. Network or server suspicion → Network panel, check the request and its status. Console is always open as a scratchpad alongside whichever panel I'm in. The discipline of matching the tool to the question — instead of poking around hoping to stumble on the answer — is what made DevTools feel like a real instrument set rather than a wall of tabs.

## References

[1] Chrome for Developers, "Chrome DevTools," 2024. [Online]. Available: [https://developer.chrome.com/docs/devtools/](https://developer.chrome.com/docs/devtools/)

[2] Chrome for Developers, "Debug JavaScript with Chrome DevTools," 2024. [Online]. Available: [https://developer.chrome.com/docs/devtools/javascript/](https://developer.chrome.com/docs/devtools/javascript/)

[3] DebugBear, "Debugging JavaScript memory leaks," 2022. [Online]. Available: [https://www.debugbear.com/blog/debugging-javascript-memory-leaks](https://www.debugbear.com/blog/debugging-javascript-memory-leaks)

[4] Medium / Coding Blocks, "Catching memory leaks with Chrome DevTools," 2021. [Online]. Available: [https://medium.com/coding-blocks/catching-memory-leaks-with-chrome-devtools-57b03acb6bb9](https://medium.com/coding-blocks/catching-memory-leaks-with-chrome-devtools-57b03acb6bb9)

[5] Chrome for Developers, "Analyze runtime performance," 2024. [Online]. Available: [https://developer.chrome.com/docs/devtools/performance](https://developer.chrome.com/docs/devtools/performance)

```quiz
Q: Which DevTools panel answers "why is this code producing the wrong value?"
- Sources (set a breakpoint, step through, inspect variables and the call stack)
- Memory
correct: 0
explain: Sources is the step-through debugger. Set a breakpoint at the suspect line, trigger the action, and pause to inspect live values, the scope, and the call stack — far more powerful than console.log guessing.

Q: The recommended technique for finding a memory leak is…
- take a heap snapshot, perform the leaky action, force GC, take a second snapshot, then compare the delta and read the retainers
- watch the Memory graph and guess
correct: 0
explain: Two snapshots with the action in between, plus a forced GC, isolate what was allocated but not freed. The delta's retainers show the exact reference path keeping each leaked object alive.

Q: When an interaction janks (drops frames), which panel finds the cause?
- Performance — record the interaction and read the timeline/flame chart to find the long task blocking the main thread
- Console
correct: 0
explain: Performance records what ran during the interaction. The flame chart shows which functions or layout passes consumed the frame budget. Long tasks on the main thread are the usual culprit.

Q: What does Lighthouse do?
- Runs an automated audit across performance, accessibility, best practices, and SEO, returning a checklist with fixes
- Steps through code line by line
correct: 0
explain: Lighthouse is an auditor — it scores the page on several axes and links each finding to a fix. It's the starting point when you don't yet know what to optimize.

Q: The Console panel is best used as…
- a live REPL and log output, alongside whichever specialized panel you're actively debugging in
- a full replacement for the Sources debugger
correct: 0
explain: Console evaluates expressions in the current context and shows logged output. It complements the specialized panels — a scratchpad — but doesn't replace step-through debugging in Sources.
```
