It's a good idea to begin this series of posts by explaining what we mean when we talk about debugging. That's because it's a good old tradition in software engineering to call things names that don't mean the thing they describe. If we just look at the word and extrapolate based on that we probably imagine something like:

flowchart LR
    Bug --> Debugging --> Bugfix

Which would make Debugging the process between a bug and the bugfix. This is a good start, but if you've been coding for a while, you already know this is not it. Let's consider for a moment...

The bug

Is it always a bug that pushes us into debugging? It's quite clear if the user path is broken, that's definitely a bug. What if it isn't though? Let's enumerate some cases we could debug that are a bit more murky:

  • a cryptic type error
  • a slow SQL query
  • a runtime error in the console that seemingly has no impact
  • a random test failure
  • a random CI/CD failure
  • a bundle size that's larger than it should be
  • a slow linter / build tools
  • a cache that didn't work or didn't bust
  • too many rerenders
  • missing or incorrect analytics

Any developer with some years on the clock can recite a similar list off the top of their head. Can all of those be considered bugs? I don't think so. If your code fails to compile because of a type error that you cannot wrap your head around - most likely there's no bug in the typechecker and your code is actually at fault. Knowing that doesn't help you resolve the issue though... Let's maybe just agree we call it an issue then and move onto the next piece of the puzzle.

flowchart LR
    Issue --> Debugging --> Bugfix

The debugging

So we have an issue and then we start debugging to find a fix, right? Are all issues debugged though? Let's look at an example case.

- What if this sentence was supposed to be cyan, but is showing up as red?

You'd probably check the style for it:

/** Case A */
p {
    color: red;
}

Oh, it's actually just specified as red right there in the CSS file. So you change it to green, push a fix and that's it. Now, can we honestly say we've done any debugging here? No, we cannot. Let's go explore a different option - what if you open the CSS file and what you see is:

/** Case B */
p {
    color: green;
}

Oh well, that's a puzzle. It's actually defined correctly, so either the rule is not in effect or something is overriding it. Now we are really debugging. So what's the difference? In Case A the issue was obvious at first glance, in Case B we have no immediate explanation. We need to investigate to find the root cause and prepare the...

flowchart LR
    Issue --> Investigation --> Bugfix

Bugfix, right?

Well... let's pause for a minute and also consider this one. Will debugging always end with a bugfix? That's a reasonable assumption, but any seasoned developer knows that the answer is - No, it will not, for many different reasons. Let's do an exercise together and add a pair of root causes to the list of murky issues from the first paragraph - one that'd end with a bugfix and one without.

  • a cryptic type error
    • a type error in my code - can be fixed
    • a type error in a closed-source dependency we're stuck with - cannot be fixed
  • a slow SQL query
    • a missing index in the DB - can be fixed
    • it only occurs on Tuesday 15:00 when DevOps save backups - cannot be fixed
  • a runtime error in the console that seemingly has no impact
    • duplicate key on React list items - can be fixed
    • user is using unsupported browser - cannot be fixed
  • a random test failure
    • Unmocked date causes test to fail every 1st of the month - can be fixed
    • Timeout due to runners being blocked - cannot be fixed
  • a random CI/CD failure
    • Too low # of runners causing bottlenecks and timeouts - can be fixed
    • CI/CD provider is unstable - cannot be fixed
  • a slow linter / build tools
    • misconfiguration - can be fixed
    • tool cannot handle the scale of your codebase - cannot be fixed

Now you can argue with some of these "cannot be fixed" cases, but you should spot a pattern there. We can fix what we control. If you control both the client, backend and devops - all the power to you, you can probably fix most issues on that list by yourself. For most of us mere mortals, often you'll find that your investigation will lead you to something out of your control. At this point the best thing we can do is to write down a report and pass it onto the person with control over that piece. Our investigation reached it's conclusion regardless if we've fixed the issue.

flowchart LR
    Issue --> Investigation --> Conclusion

If debugging is leading an investigation, then similar to a real detective, we have many different tools at our disposal to study the crime scene andc analyze the trails and clues left. In this series of blog posts I will teach you how to pick the right ones and how to use them. Put your detective hat on!