console.log Debugging is for Beginners. Here's How Professionals Do It.
If you're still debugging with console.log in 2025, your coworkers are judging you. I get it - we've all been there. You add a quick console.log(user)
, forget to remove it, and boom - it's in production. But VS Code's debugger is sitting right there, waiting for you to actually use it.
I've been debugging production issues at 3am, and trust me - the difference between someone who knows the debugger and someone who doesn't is massive. When your API is returning 500 errors and you have 10 minutes to fix it before the CEO starts asking questions, you want real debugging tools.
Stop Spam-Clicking Through Breakpoints Like an Amateur
Junior developers spam breakpoints everywhere like they're debugging with a shotgun. Here's how to actually debug without losing your mind.
Conditional breakpoints save your sanity. Instead of hitting F5 two hundred times through a loop, right-click any line number and add a condition. Want to debug only when user.id === "12345"
? Done. Need to catch when your array grows too big? Use items.length > 1000
.
I learned this the hard way debugging a payment processor that was failing for one specific user out of thousands. Without conditional breakpoints, I'd still be clicking "Continue" like an idiot.
Setting a conditional breakpoint: Right-click any line number in the gutter, select "Add Conditional Breakpoint", then enter your condition like user.id === "12345"
or items.length > 100
. The breakpoint turns into a diamond shape with a small dot to indicate it's conditional.
Logpoints are what you should be using instead of littering your code with console.log. Right-click a line number, select "Add Logpoint", and type what you want to log. It shows up in the debug console without touching your code. No more committing debug statements to master - we've all done it, and it's embarrassing.
Hit count breakpoints are for when you need to catch that one weird iteration that breaks everything. Function runs 1000 times but only crashes on iteration #847? Set a hit count breakpoint for "= 847" and save yourself from clicking Continue 846 times. Trust me, your wrist will thank you.
Function Breakpoints Are Magic When You Don't Know Where Shit's Called From
Function breakpoints are perfect when some function is getting called and you have no idea where. Just type the function name and VS Code will break every time it's called, no matter where. Great for callback hell debugging.
Exception handling in the debugger is way more useful than you think:
- Caught exceptions: Stop when your try-catch blocks trigger - helps you see what's actually breaking
- Uncaught exceptions: Pause on crashes before they happen - this is gold for finding bugs
- User uncaught exceptions: Only stop on YOUR code's exceptions, not React's internal garbage
I once spent 3 fucking hours chasing a "Cannot read property 'map' of undefined" error that was getting swallowed by an overly broad try-catch. Turned out Node 18.12.0 changed how the Crypto.randomUUID() response gets parsed, and our payment processor was expecting the old format. Found this buried in a random GitHub issue comment - not even in the official changelog. Enable exception breakpoints and save yourself the pain.
The Debug Console is Your Best Friend at 3am
The Variables panel shows you everything in scope, but the real power is in the Watch panel. Instead of constantly expanding user.profile.settings.theme
in the variables view, just add it to the watch list. Boom - it updates live as you step through code.
Debug console is where the magic happens. You can:
- Execute any JavaScript expression:
user.email
shows the value - Modify variables on the fly:
user.role = 'admin'
and test the fix - Call functions with different parameters:
validatePayment({amount: -100})
to test edge cases
This saved my ass debugging a race condition in production that only happened under heavy load. Could modify the timing variables in the debugger to reproduce the bug without restarting the entire application. Turns out setTimeout(0) isn't actually 0ms in Chrome 97+ - it's 1ms minimum. That 1ms difference broke our WebSocket reconnection logic. 10/10, would debug again.
When Your App is Spread Across 47 Different Services
Modern apps don't run as a single process - they're spread across containers, APIs, databases, and whatever the hell else your architect decided was "scalable."
Multi-session debugging lets you debug frontend and backend simultaneously. React throws an error, you set a breakpoint in both the frontend AND the Node.js API that's failing. Follow the request from client to server without losing your mind.
Remote debugging is essential when the bug only happens on staging because of course it does. Your app works fine locally but crashes in the container? Remote attach to the containerized process and debug it directly. No more "works on my machine" bullshit.
Remote work debugging hell: SSH connections that drop every 30 minutes because your ISP thinks you're torrenting Linux ISOs. VS Code Remote extensions that work great until they don't, then you get the dreaded "Could not establish connection to server" error that requires restarting VS Code, clearing SSH config cache, and sacrificing a rubber duck to the connection gods. Port forwarding randomly stops working right before your demo to the CEO because Windows Defender decided your debug server is "suspicious activity."
Container debugging with Dev Containers solved my Docker networking nightmares. Debug configs live with the container setup, so everyone on the team gets the same debugging environment. No more "your Docker setup is wrong" conversations.
Don't Be the Developer Who Commits Their AWS Keys
Debug configurations can expose secrets faster than you can say "data breach." I've seen developers hardcode database passwords in launch.json
and commit them to public repos. Don't be that person.
Workspace Trust stops malicious debug configs from running arbitrary code on your machine. When you open sketchy code from GitHub, VS Code asks if you trust it. Say "No" unless you actually do - those debug configurations could be running cryptocurrency miners.
Here's what you need to know:
- Never hardcode secrets in
.vscode/launch.json
- use environment variables:"DATABASE_URL": "${env:DATABASE_URL}"
- Review debug configs in code reviews - they can contain production credentials
- Understand that launch.json gets committed to git - anything in there is potentially public
- Use separate debug configs for prod/staging/local - don't mix environments
I once saw a startup lose $10k because someone committed Stripe production API keys in their debug configuration. The repo was public on GitHub. Took exactly 47 minutes from commit to first fraudulent charge. The attacker was running automated scripts looking for exactly this kind of fuckup. The company only noticed when their Slack bot posted "💰 $500 charge processed" 20 times in a row.
When Enterprise Security Makes Debugging Feel Like Prison
Enterprise debugging is where simple tasks go to die. Security policies treat you like a potential terrorist, and every debugging session requires three approvals and a sacrifice to the compliance gods.
Your company might require:
- Logging every variable you inspect during debugging (yes, really)
- Approval workflows before you can debug production-adjacent code
- Disabled debugging entirely on anything that touches customer data
- VPN requirements that break SSH connections randomly
I've worked at places where debugging required filling out a form explaining why I needed to inspect a variable. The form took longer than fixing the actual bug. But you adapt - use logpoints instead of breakpoints when possible, debug locally with production data copies, and keep a stash of debugging configs that actually work with the corporate firewall.
AI Code is Debugging Hell in Disguise
GitHub Copilot writes code faster than you can debug it. Sure, it's impressive when it generates 50 lines of perfect-looking code, but have fun debugging when that code fails in production at 2am.
AI-generated code creates new categories of bugs:
- Code that looks correct but has subtle logic errors (async/await race conditions that only happen under load)
- Over-engineered solutions using patterns from 2019 that nobody uses anymore
- Security vulnerabilities the AI learned from Stack Overflow answers marked "deprecated"
- Patterns you didn't write and don't understand (good luck explaining this to your team lead)
- Functions with 47 parameters because Copilot saw one example and decided that's how ALL functions should work
- Promise chains that look elegant but deadlock in Node 16.14+ due to garbage collector changes
The worst part? When AI code breaks, you're debugging someone else's mental model - except that "someone" is a language model trained on billions of lines of code, including all the shitty code on GitHub.
My advice: Use conditional breakpoints heavily when debugging AI code. Set conditions that check the assumptions the AI made. And always, ALWAYS step through AI-generated code manually before trusting it in production. The AI doesn't have to get paged when your app crashes.
Additional debugging resources: Master advanced breakpoint techniques, learn logpoint best practices, understand exception handling, explore data inspection methods, and implement multi-target debugging for professional debugging workflows.