Skip to content
All essays
CraftMarch 25, 202511 min

VS Code Debugging: Master the Built-in Debugger

Master VS Code debugging. Set breakpoints, inspect variables, debug any language, and supercharge your debugging workflow.

Ü
Ümit Uz
Mobile & Full Stack Developer

Why VS Code Debugger?

VS Code includes a powerful debugger that works with virtually any language and runtime. No more console.log debugging—set breakpoints, step through code, inspect variables, and find bugs faster.

javascript
// Instead of:
console.log('User:', user);
console.log('Data:', data);
console.log('Result:', result);

// Just set a breakpoint and inspect!
// Click in the gutter, press F9
// Hover over variables to see values
// Use Debug Console to evaluate expressions

Getting Started

Launch Configuration

json
// .vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Launch Program",
      "skipFiles": ["<node_internals>/**"],
      "program": "${workspaceFolder}/src/index.js"
    },
    {
      "type": "node",
      "request": "attach",
      "name": "Attach to Process",
      "port": 9229
    }
  ]
}

Debug Panel

Variables:    Shows local, global variables
Watch:        Monitor expressions
Call Stack:   Current execution path
Breakpoints:  List of all breakpoints
Debug Console: Evaluate expressions

Breakpoint Mastery

Types of Breakpoints

javascript
// 1. Regular breakpoint (F9)
// Stops execution when hit

function processData(data) {
  const result = data.map(item => { // ← Set breakpoint here
    return transform(item);
  });
  return result;
}

// 2. Conditional breakpoint
// Right-click → Edit Breakpoint → Add condition
// Break only when: data.length > 100

// 3. Logpoint
// Right-click → Add Logpoint
// Prints message without stopping
// console.log('Data:', data);

// 4. Hit count breakpoint
// Break after N times
// Right-click → Edit Breakpoint → Set hit count

// 5. Triggered breakpoint
// Only active when another breakpoint is hit
// Right-click → Edit Breakpoint → Set trigger

Conditional Breakpoints

javascript
// Break when condition is true
data.length === 0
user.age < 18
response.status !== 200

// Break on specific value
item.id === 123

// Complex conditions
data.length > 100 && data.every(item => item.valid)

// Logpoint with expressions
`Processing item ${item.id}: ${item.name}`

// Call functions in conditions
isValidUser(user)  // Break if returns true

Debugging Actions

Stepping Through Code

F5   - Start debugging
F9   - Toggle breakpoint
F10  - Step over (next line)
F11  - Step into (function)
Shift+F11 - Step out (return to caller)
Shift+F5 - Stop debugging

// Continue execution
F5 or Continue button

// Restart
Cmd+Shift+F5 (Mac)
Ctrl+Shift+F5 (Windows)

Inspecting Variables

javascript
// Hover over variable
const user = { name: 'John', age: 30 };
// Hover to see value

// Add to Watch
// Type expression in Watch panel
user.name
data.length
calculateTotal(items)

// Scope variables
// Automatically shows:
// - Local variables
// - Closure variables
// - Global variables

// Variable inspection
// Click on object to expand
// See properties and values
// Modify values inline

Advanced Debugging

Debug Console

javascript
// Evaluate expressions in Debug Console
// Variables are in scope

> user.name
'John'

> data.filter(item => item.active)
[...items]

> calculateTotal(cart)
123.45

// Call functions
> processData(rawData)
[...processed]

// Modify variables
> counter = 100
100

> items.push(newItem)
5

// Test code snippets
> items.map(i => i.price * 1.1)
[...prices]

Call Stack Navigation

javascript
// View call stack in panel
// Shows execution path
// Click frames to navigate

// Example stack:
processData (index.js:25)
transform (utils.js:10)
validate (helpers.js:5)

// Jump to any frame
// Inspect variables at that level
// Understand how you got here

Language Support

JavaScript/Node.js

json
{
  "type": "node",
  "request": "launch",
  "name": "Debug Node.js",
  "program": "${workspaceFolder}/src/index.js",
  "cwd": "${workspaceFolder}",
  "runtimeExecutable": "node",
  "runtimeArgs": ["--nolazy"],
  "env": {
    "NODE_ENV": "development",
    "DEBUG": "*"
  },
  "console": "integratedTerminal",
  "stopOnEntry": false,
  "skipFiles": ["<node_internals>/**"]
}

React/Next.js

json
{
  "name": "Next.js: debug server-side",
  "type": "node-terminal",
  "request": "launch",
  "command": "npm run dev"
}

{
  "name": "Next.js: debug client-side",
  "type": "chrome",
  "request": "launch",
  "url": "http://localhost:3000",
  "webRoot": "${workspaceFolder}"
}

Python

json
{
  "name": "Python: Current File",
  "type": "debugpy",
  "request": "launch",
  "program": "${file}",
  "console": "integratedTerminal",
  "justMyCode": true
}

Go

json
{
  "name": "Launch Package",
  "type": "go",
  "request": "launch",
  "mode": "auto",
  "program": "${workspaceFolder}",
  "env": {},
  "args": []
}

Debugging Tests

Jest

json
{
  "name": "Debug Jest Tests",
  "type": "node",
  "request": "launch",
  "runtimeExecutable": "npm",
  "runtimeArgs": ["test", "--", "--runInBand"],
  "console": "integratedTerminal",
  "internalConsoleOptions": "neverOpen"
}

Vitest

json
{
  "name": "Debug Vitest",
  "type": "node",
  "request": "launch",
  "program": "${workspaceFolder}/node_modules/.bin/vitest",
  "args": ["run", "--reporter=verbose"],
  "console": "integratedTerminal",
  "internalConsoleOptions": "neverOpen"
}

Remote Debugging

SSH Remote Debugging

bash
# Install Remote-SSH extension
# Configure SSH hosts

# .ssh/config
Host server
  HostName example.com
  User ubuntu
  IdentityFile ~/.ssh/key.pem

# Open remote folder
# Code → Open Folder → Select SSH host
# Debug works just like local

Docker Debugging

json
{
  "name": "Docker Node.js",
  "type": "node",
  "request": "attach",
  "port": 9229,
  "address": "localhost",
  "localRoot": "${workspaceFolder}",
  "remoteRoot": "/app"
}

// docker-compose.yml
services:
  app:
    build: .
    ports:
      - "9229:9229"
    command: node --inspect=0.0.0.0:9229 index.js

Advanced Features

Debugging Chrome

json
{
  "type": "chrome",
  "request": "launch",
  "name": "Launch Chrome",
  "url": "http://localhost:3000",
  "webRoot": "${workspaceFolder}",
  "sourceMaps": true,
  "sourceMapPathOverrides": {
    "webpack:///*": "${webRoot}/*"
  }
}

Multi-Target Debugging

json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Server",
      "type": "node",
      "request": "launch",
      "program": "${workspaceFolder}/server.js"
    },
    {
      "name": "Client",
      "type": "chrome",
      "request": "launch",
      "url": "http://localhost:3000"
    }
  ],
  "compounds": [
    {
      "name": "Server+Client",
      "configurations": ["Server", "Client"]
    }
  ]
}

Productivity Tips

Keyboard Shortcuts

F5            - Start/Continue
F9            - Toggle breakpoint
F10           - Step over
F11           - Step into
Shift+F11     - Step out
Shift+F5      - Stop

Cmd+Shift+P   - Show all commands
Cmd+Shift+D   - Show debug
Cmd+K Cmd+X   - Cut line
Cmd+Enter     - Insert line below

Debug Actions

javascript
// Add debug configuration
// Click "Add Configuration" in launch.json

// Auto-attach to process
// Debug → Attach to Process
// Select Node.js process

// JavaScript Debug Terminal
// Terminal menu → Create JavaScript Debug Terminal
// Runs code with debugger attached

Troubleshooting

Source Maps

json
// Ensure source maps work
{
  "type": "node",
  "request": "launch",
  "sourceMaps": true,
  "outFiles": ["${workspaceFolder}/dist/**/*.js"],
  "webRoot": "${workspaceFolder}",
  "resolveSourceMapLocations": [
    "${workspaceFolder}/**",
    "!**/node_modules/**"
  ]
}

Skipping Files

json
{
  "skipFiles": [
    "<node_internals>/**",
    "node_modules/**",
    "${workspaceFolder}/lib/**"
  ]
}

Best Practices

1. Use Descriptive Names

json
{
  "name": "Debug User Registration Flow",
  "type": "node",
  "request": "launch"
}

2. Set Up Environments

json
{
  "env": {
    "NODE_ENV": "test",
    "LOG_LEVEL": "debug"
  },
  "envFile": "${workspaceFolder}/.env"
}

3. Use Pre-Launch Tasks

json
{
  "preLaunchTask": "npm: build",
  "outFiles": ["${workspaceFolder}/dist/**/*.js"]
}

Conclusion

Master VS Code debugging to write better code faster. The built-in debugger works with any language and provides all the features you need to squash bugs efficiently.

Key takeaways:

  • Use breakpoints instead of console.log
  • Leverage conditional breakpoints for targeted debugging
  • Master the Debug Console for live evaluation
  • Set up proper launch configurations
  • Use keyboard shortcuts for speed

Debug like a pro with VS Code!

Enjoyed this essay? Share it.

Related essays

Next essay
Building a Developer Portfolio That Stands Out in 2025