As a software developer, I can guarantee you one thing for sure: you are going to spend a great deal of time debugging code.\n\nThere are certain constants in life which are unavoidable: death, taxes, and programmers creating bugs.\n\nSince so much of your time will be spent debugging code, it’s probably a good idea for a developer to be good at debugging, don’t ya think?\n\nUnfortunately, many developers—even highly experienced ones—tend to, well… suck.\n\nThere are plenty of developers who can whip through new features and sling code around like nobody’s business, but who cleans up the mess of bugs they leave behind?\n\nIt’s one thing to know how to write good code; it’s another thing to know how to debug the ugliest code you’ve ever seen in your life that was written by the legendary Bob who built the whole first version of the application in 48 hours in his basement, but was kind of an “odd” fellow.\n\nFortunately, debugging, like any other skill, is something that can be learned.\n\nIf you apply the right techniques and practice, you can become great at it.\n\nWho knows? You might even enjoy it.\n\nThe key to debugging is realizing that it’s all about mindset.\n\nIt’s about taking a systematic approach to the problem—not rushing it, and not expecting you can just find the problem, get in, and get out.\n\nIt’s about staying calm and collected: attacking the problem from a logical and analytical perspective instead of an emotional one.\n\nIn this chapter, I’m going to lay out a systematic approach to debugging, which will help you to avoid that dreaded debugger mindset and take your debugging skills to the next level.\n\n
What Is Debugging?
\n\n
First Rule of Debugging: Don’t Use the Debugger
\n\nAh, what’s this you say?\n\nA new bug for me to fix?\n\nOh, this is a hairy one?\n\nHave no fear, sir. I will unleash the full power of my mental arsenal on this unholy terror.\n\nWith that mindset, you the programmer sit down at your desk.\n\nYou fire up the debugger.\n\nCarefully you step through the code.\n\nTime seems to blur, minutes turn into hours, hours into weeks.\n\nYou are an old man sitting at the keyboard, still in the same debugging session, but somehow you are “closer.”\n\nYour children have all grown. Your wife has left you.\n\nThe only thing that remains is… the bug.\n\nThe first thing most programmers do when they want to debug an issue in the code is to fire up the good old debugger and start looking around.\n\nWrong.\n\nDon’t do this.\n\nThe debugger should be your last resort.\n\nWhen you fire up the debugger immediately, you are effectively saying, “I don’t have any idea what is causing the issue, but I’m just going to look around and see.”\n\nIt’s like when your car breaks down and you don’t know jack shit about cars, so you open up the hood and look for something wrong.\n\nWhat are you looking for?\n\nYou don’t even know.\n\nDon’t get me wrong.\n\nThe debugger is a wonderful and powerful tool.\n\nUsed properly, the debugger can help you solve all kinds of problems and can help you see what happens when your code is running.\n\nIt’s not, however, the place to start, and many bugs can be solved without ever touching the debugger.\n\nYou see, just like Facebook or funny YouTube cat videos, the debugger has a way of sucking you in.\n\n
Reproduce the Error
\n\nSo, if you don’t simply fire up the debugger to debug a problem, what do you do?\n\nWell, I’m glad you asked.\n\nThe first thing any sane person should do is to reproduce the bug to make sure that it is actually a bug and that you will be able to debug it.\n\nOne-hundred percent of problems that can’t be reproduced can’t be debugged.\n\nSo, if you can’t reproduce the problem, there ain’t no point in debugging it. Ya hear me?\n\nNot only can you not debug a problem that can’t be reproduced, but also, even if you did fix it, you can’t verify it was fixed.\n\nSo, the very first thing you should do when you are trying to debug a bug is to make sure you can reproduce the bug yourself.\n\nIf you can’t, go and get help.\n\nIf a tester filed the bug, get them to reproduce it for you.\n\nIf the bug is intermittent and can’t be reliably reproduced, this means that you do not know the circumstances required to reproduce the problem.\n\nThere is no such thing as an intermittent problem.\n\nIf it is a problem, it can be reproduced; you just have to know how.\n\nSide note on intermittent problems:\n\nOk, so your boss is demanding that you fix the problem.\n\nThey’ve seen it in production. Customers have seen it. It’s definitely a problem.\n\nThe “I can’t reproduce it” pushback is not working—they aren’t buying it.\n\nWhat do you do?\n\nYou still can’t debug a problem that you can’t reproduce.\n\nBut what you can do is gather more evidence.\n\nInsert logging statements in the code.\n\nGather as many details as possible about when the problem happens and under what conditions it happens.\n\nArtificially recreate the environment and circumstances if you can.\n\nDo not be tempted to throw in “fixes” for the problem that you can’t recreate.\n\nIf you don’t understand the problem enough to recreate it, you have a very, very low chance of accidentally fixing it by a guess, and you will have an extremely difficult time knowing if your fix even worked.\n\nFind a way to reproduce the problem, even if it is only reproducible in production.\n\n
Sit and Think
\n\nAfter you can reproduce your issue, the next step is a step most software developers skip because they are so eager to solve the problem—but this step is crucial.\n\nIt’s a really simple step.\n\nJust sit and think.\n\nYes, that’s right.\n\nThink about the problem and what the possible causes could be.\n\nThink about how the system works and what might bring about the odd behavior you’re seeing.\n\nYou are going to be in a rush to jump into the code and into the debugger and start “looking at things,” but before you start looking at things, it’s important to know what you are looking for and what things to look at.\n\nYou’ll likely come up with a few ideas or hypotheses about what might be causing the issues.\n\nIf you don’t, be patient. Keep sitting and thinking.\n\nStand and walk around if it helps, but before you move on, you should at least have a few ideas that you want to test.\n\nIf you absolutely can’t come up with anything, continue to resist firing up the debugger, and instead take a browse through the source code and see if you can gather a few more clues about how the system is supposed to work.\n\nYou should have at least two or three good hypotheses you can test before you move on from this step.\n\n
Test Your Hypotheses
\n\n
Check Your Assumptions
\n\nMost of the time, your hypotheses are not going to pan out.\n\nThat’s just life.\n\nIf that’s the case, the next best thing you can do is to check your assumptions about how things are working.\n\nWe typically assume that code is working a certain way or that some input or output must be some value.\n\nOften we think, “Well, this can’t possibly be happening. I’m looking at the code right here. There is no way it could be producing this output.”\n\nOften, we are wrong.\n\nIt happens to the best of us.\n\nThe best thing you can do with these assumptions is to check them.\n\nAnd what’s the best way to check them?\n\nYes, that’s right. More unit tests.\n\nWrite some unit tests that check obvious things which “have to be working” along the workflow of the problem you are trying to debug.\n\nMost of these tests should easily pass, and you’ll say, “Duh.”\n\nBut, every once in awhile, you’ll write a unit test to test some obvious assumption and the results will shock you.\n\nRemember, if the answer to your problem was obvious, it wouldn’t be a problem at all.\n\nOnce again, the pragmatist side of me has to tell you that, yes, it’s ok to open up the debugger to check your assumptions as well.\n\nBut only after you’ve tried to check the assumptions using unit tests first.\n\nAgain, it’s like climbing that mountain and putting in anchors along the way.\n\nAvoid the debugger if you can, use it if you must, but, once again, only to validate or invalidate specific assumptions you have already formed.\n\n
Divide and Conquer
\n\nI remember working on a really hairy bug with a printer incorrectly interpreting a print file written in the PostScript printing language.\n\nI tried everything I could think of to debug the problem.\n\nI tested all kinds of hypotheses.\n\nNothing panned out.\n\nIt seemed like the bug was some kind of combination of multiple commands in the print file, and I had no idea which ones it was.\n\nSo, what did I do?\n\nWell, I cut the print file in half.\n\nThe bug was still there.\n\nSo, I cut it in half again.\n\nIt disappeared this time.\n\nI tried the other half. Back again.\n\nI kept hacking away at big chucks of the print file until I got the entire file down from several thousand lines of code to just five.\n\nThe five lines of code that, in that order, produced the bug.\n\nEasy peasy.\n\nSometimes when you get stuck debugging, what you need to do is figure out a way to cut the problem in half—or take as big of a chunk out of it as possible.\n\nDepending on the problem, this could look very different, but try and think of ways you can eliminate a large amount of code or remove a large amount of the system or variables and still reproduce the bug.\n\nSee if you can come up with tests which completely eliminate parts of the system for being responsible for the error.\n\nThen do it again… and again.\n\nIf you keep hacking away, you’ll likely find the critical components required to create the error, and then the problem can become relatively easy to solve.\n\n
If You Fix It, Understand Why
\n\nI’m going to give you one final piece of advice about debugging—although I’m sure I could write a whole book on the subject.\n\nIf you fix a problem, understand why what you did fixed it.\n\nIf you don’t understand why what you did fixed the problem, you are not done yet.\n\nYou may have inadvertently caused a different problem, or—very likely—you haven’t fixed your original problem.\n\nProblems don’t go away on their own.\n\nIf you didn’t fix the problem, I can guarantee you it’s not fixed. It’s just hiding.\n\nBut if you did fix the problem, don’t stop there. Explore a little deeper, and make sure you understand exactly what was going on that caused the problem in the first place and how what you did fixed it.\n\nToo many software developers debug a problem by twiddling bits, the code apparently starts working, and they assume it is fixed without even knowing why.\n\nThis is a dangerous habit for many reasons.\n\nAs I mentioned above, when you randomly tweak things in the system and change bits of code here and there, you could be causing all kinds of other problems which you aren’t aware of.\n\nBut, perhaps more than that, you are training yourself to be a shitty debugger.\n\nYou are developing the habit of messing with things until it works. No technique, no rigor.\n\nYou may get lucky sometimes, but you won’t have a repeatable process or reliable skillset for debugging.\n\nNot only should you understand what broke, why, and how you fixed it, but also you should verify the fix.\n\nI know it seems like common knowledge, but I can’t tell you how much time is wasted by programmers “fixing a problem,” assuming the fix worked, and passing the code to QA only for QA to reproduce the problem and have it go back to the developer who has to start over at square one again.\n\nIt’s a huge waste of time that can be prevented by taking an extra five minutes to verify that what you fixed is actually fixed.\n\nIn fact, don’t just verify the fix; write a regression test for the problem so that it never happens again.\n\nIf you truly understand the problem you fixed, you should be able to write a unit test that exploits the issue, and then your fix should make that unit test pass.\n\nFinally, look for other instances of this same class of bug.\n\nBugs tend to hang out together.\n\nIf you found something wrong with one assumption you made about the system or some incorrectly coded component, it’s very likely that there are other issues which are also caused by that same problem.\n\nAgain, this is why it is so critical that you understand what the real problem was and why your solution fixed it.\n\nIf you know what happened and why, you can quickly figure out if there are likely to be other issues caused by the same underlying problem.\n\n
Art and Science
\n\n

