The 5 Debugging Techniques That Save Me Hours
Chrome DevTools, profiling, strategic logging, and other tools I reach for before touching the code.
Debugging Is a Skill, Not a Talent
Early in my career, I thought good debuggers had some innate ability to see bugs. After five years, I realize they just have better systems. They know where to look first, what tools to use, and how to narrow the search space quickly.
These five techniques aren't clever tricks. They're systematic approaches that I use almost every day. The goal isn't to find bugs faster — it's to avoid wasting time on dead ends.
Technique 1: Binary Search with Git Bisect
When a bug is reported and I don't know which commit introduced it, git bisect is the fastest path to an answer. It performs a binary search across your commit history, asking you at each step whether the bug is present. For a history of 1000 commits, it finds the culprit in about 10 steps.
I use this at least twice a month at Flipkart. Last month, a subtle CSS regression appeared in production that nobody could trace to a specific change. Git bisect identified the offending commit in under five minutes — it was an unrelated dependency update that changed a PostCSS plugin version.
The prerequisite for git bisect is small, atomic commits. If your commits are 500-line monsters that change everything, bisect will identify the commit but you'll still spend an hour finding the bug within it.
Technique 2: Performance Profiling Before Guessing
When something is slow, the temptation is to guess where the bottleneck is and start optimizing. Resist this. Open Chrome DevTools, record a performance profile, and let the data tell you where the time is going.
I've seen engineers spend days optimizing a React component's render logic only to discover that the actual bottleneck was a synchronous layout calculation in CSS. The profiler would have shown this in 30 seconds.
The Performance tab in Chrome DevTools shows you a flame chart of everything that happened during the recording. Look for long tasks (yellow blocks over 50ms), forced reflows (purple 'Layout' events), and unnecessary re-renders. The data doesn't lie.
Technique 3: Strategic Console Logging
I know, I know — console.log debugging feels primitive. But used strategically, it's one of the most efficient tools available. The key word is strategic. Don't scatter console.logs randomly. Place them at the boundaries of your system: where data enters, where it's transformed, and where it exits.
I use a pattern I call 'breadcrumb logging.' At each major decision point in the code, I log the input and the decision. This creates a trail that shows me exactly where the data diverged from my expectations. It's faster than stepping through a debugger for complex async flows.
Technique 4: Reproducing in Isolation
If I can't reproduce a bug in isolation, I don't understand it well enough to fix it. My first step for any non-trivial bug is to create a minimal reproduction — the smallest possible code that exhibits the behavior.
For UI bugs, I use a blank Next.js project and copy in only the relevant components. For API bugs, I use a REST client with the exact request parameters. The act of isolating often reveals the root cause before I even start debugging, because I discover which dependencies and context are actually necessary for the bug to occur.
At CARS24, we made 'minimal reproduction' a required field in our bug ticket template. It added 15 minutes to the filing process but saved hours of debugging time because the reproducer eliminated ambiguity about what 'the bug' actually was.
Technique 5: Reading the Error, Actually Reading It
This sounds patronizing, but I mean it sincerely. I regularly see engineers glance at an error message, make an assumption about what's wrong, and start fixing the wrong thing. Read the entire error message. Read the stack trace. Click through to the source line.
Modern frameworks like Next.js and React have invested heavily in error messages. They tell you what went wrong, why, and often link to documentation with the fix. The answer is frequently in the error message itself, if you take 30 seconds to actually read it.
The corollary: when you write code that throws errors, write good error messages. Include the expected value, the actual value, and what the developer should check. Your future self will thank you.
Related Articles
The Full-Stack Engineer's Guide to Core Web Vitals
Practical tips from production — not theory, not docs rewrites, just what actually moved the needle.
ReadPerformance Budgets That Actually Work
How we enforced performance budgets in CI at CARS24 and Mamaearth — and what happened when we didn't.
ReadThe Cost of JavaScript: A Deep Dive
Bundle analysis, tree-shaking, and how we cut Total Blocking Time by 60% at Mamaearth.
ReadFound this useful? I write about engineering, performance, and career growth.