loading...
Cover image for Debugging - you’re doing it wrong. 10 techniques to find a bug in your code

Debugging - you’re doing it wrong. 10 techniques to find a bug in your code

nikpoltoratsky profile image Nikita ・7 min read

Do you remember those long, long hours spent on debugging? When you're staring at a codebase and can't figure out what exactly went wrong? You're not alone! I think all developers struggle with debugging from time to time. That's why in the article I'm going to tell you about my favorite approaches to finding bugs in the code.

Table of contents


Google error message

I'm not going to sort those tips by usefulness but google error message is at the first place by a reason. I think, if you can't even understand an error message from the first sight, the easiest way to solve the problem is to google error message.

In most of the cases, the error message you are facing was googled before by someone else. We have a lot of beautiful places like StackOverflow and GitHub issues, where people aim to help each other. That's why you can not only find the answer but also steps to avoid such error or motivation why this error occurred in the first place. So, why not to google the error? It's the easiest way here.


Console log everything

I think it's one of the most popular ways to find a bug in the codebase - add a tenth of console.log(...) statements across the codebase, rerun the application and try to figure out what went wrong.

I like it, and from my point of view, it's the right solution for simple issues, which are already localized to a few classes. But it's always a bad idea to start seeking with console.log(...)s when you have no idea what's going on here and where that creepy bug resides. Because in that case you may miss something important not logged and you'll have to add console logs and rerun application multiple times until you find the cause of failure.


Use debugger

I don't think I have too much to say here. All developers know what debugger is and why to use it. Instead, I'm going to tell you about a small discussion that flared up the other day in the office with one of my teammates.

A few days ago, I was discussing different ways of debugging with one of my teammates (that's the reason why this article appeared). And I said that one of the most robust ways to find an issue is to use a debugger. Easy as that - you're setting breakpoints, then, performing some actions and going step by step through your codebase, observing how the state of the application changes. What can be more straightforward as that?

While my teammate introduced an exciting idea - when you're using the debugger, you are not training your analytical skills and critical thinking. The reason is that with debugger you're just staring at the window with variables and waiting for incorrect behavior. While when you're digging through the codebase without the debugger, you're more focused and trying to understand what's going on and learn something new each time you're seeking for a bug.

What do you think about debugger vs. manual investigations? Please, give me your ideas in the comments.


Problem localization

The key idea of the Problem localization method is to comment/remove a code step by step until you figure out what went wrong. It works especially useful in cases when you are writing an algorithm or some business logic a pretty long time without compiling and executing the application.

In such cases, I'm always doing something wrong, and the easiest way for me is to comment code partially and try to intercept disappeared errors. Then, repeat it until all bugs will be found and revamped.

In some cases it might be a good idea to use the binary search line approach when you comment half of the code (or remove half of the files) - then check if the error is still here. If yes repeat the same with the half of this half, if now - same with the other half.

Of course, this only works in particular cases when you are able to comment/ remove code parts still keeping it working.


Create a few tests

Ok, that's the crazy one, whereas it could be useful in some particular cases. From my sight, it works well when you have some algorithm that works incorrectly, and is too complex to write console.log's or go through it using the debugger.

In that case, it might be useful to write a few tests for it. Tests could help you localize the issue in the algorithm.

After that, when the bug is localized, and you know where to search, you may use another approach to fix it finally.


Analyze logs

Yep, I know you hate to analyze all those 10mb text files with logs. But quite often it could save you a few hours of debugging. Of course, logging has to be configured adequately first of all. Collected log files have to be kept during the appropriate amount of time. But if all conditions are met - you're lucky. It's like console logging, but you already have all console.logs on their places and can just read through actions performed on your system.

But unfortunately, quite often it's a dream... We're not always paying enough attention to logging.


Ask a friend

It's pretty obvious, but we're not doing it quite often. I think all of us have more experienced teammates at the office. Otherwise, we can find an expert across the internet. Don't be afraid, ok? People are always ready to help you!

But. You need to satisfy one requirement - check all the available sources of the information before asking somebody. If you're asking for help with something easy, which is written on the second page of the documentation, then. Well. Be ready to run away!


Git bisect

Git not only helps us to keep an application revisions history, but it also provides us with a few tools for debugging. One of those tools is a git bisect - tool for performing a binary search across your git history. It's quite useful in case you didn't work with the codebase for a while, and since your last intervention, a few hundred commits were added. And now, you encounter a bug and have no idea how and when it appeared. But you remember you didn’t have it in version 2.0.15, for instance.

In that case git bisect will help you. The idea is pretty simple, you start the debugging process with git bisect start, then, we need to mark the current version as bad version because we have a bug here - git bisect bad. After that, we have to tell git about a good working version: git bisect good 2.0.15. On that stage, setup is done, and we can start searching.

git bisect selects commit in the middle of bad-good range and checks out on it. Then, we have to check if we have that bug in this revision? If yes - run git bisect bad, if no - run git bisect good. Then, git will choose a new commit in the original bad-good range, and we have to repeat the process until a commit with the bug is found.

git bisect is a pretty powerful tool that's why a full description will be out of the format of the article, but here is a link with a good explanaition.


Talk to a rubber duck

It's one of the most potent methods of understanding what's going on in the code. The main idea is that you need to find a rubber duck, put it in front of yourself and then, explain your system to it, starting from general concepts and then going line by line. You can read more about it here. I like this idea and use it regularly, so, let me tell you a short story about how I used it the first time and even didn't notice it.

When I just started my career as the programmer, I was building an Android application which contained quite complex handwritten animations. I made one of those animations carefully, step by step, and everything was fine until I stuck.

I had an animation code for no more than 150 lines. And I couldn't find a problem why it wasn't working. I checked all the algorithms multiple times but still nothing. After staring at the screen for a few hours, I decided to ask a teammate for help.

I've started a detailed explanation of how that animation works, and in a minute, I found the issue! One of the methods implicitly converted float to int🤦‍♂️🤦‍♂️🤦‍♂️🤦‍♂️🤦‍♂️. And It took me one minute to understand it when I tried to explain it in detail to my teammate.

That time I even didn't know that's it's a method of a rubber duck. I realized that, later. But I already understood how powerful it is.


Tambourine Dancing

It's my favorite way to solve problems with the software. All you need is tambourine. You need to dance around your workstation, knocking on a tambourine. It will be a big plus if you have a tambourine with the logo of technology you're working with. For instance, I have a tambourine which helps me to solve issues with my microservices:

Java Tambourine

But it's utterly useless for front-end applications.


Conclusion

Thank you for reading this! Hopefully, you've learned something new from the article. The last thing I want to reveal here for those of you who read the material to the end is that the most powerful way to deal with bugs and issues in the codebase is to write code without bugs. Just, ask your manager. 😅

Anyway, be focused and consistent. Do your investigation step by step and use the techniques mentioned above. And you'll resolve all your bugs efficiently.

Follow me on Twitter to staying tuned and let me know if you have any particular topics you would like to hear about!

Posted on by:

nikpoltoratsky profile

Nikita

@nikpoltoratsky

I’m a Google Developer Expert for Angular, open-source contributor and tech author from Belarus. I passionate about sharing knowledge and digging into the depths of technologies.

Discussion

pic
Editor guide
 

Great post, thanks Nikita!

Debuggers are amazing, my absolute favorite tools in my toolbox. The ability to jump into the code to examine variables, to jump back and forth in the execution and figure out where things go awry is just wonderful.

One thing I would add is making sure you can reproduce it and find the boundaries of the problem: under which circumstances does it happen. If you can't define the problem, it's much harder to start fixing it.

I've been burnt so many times by jumping into trying to fix something without first fully understanding when and how it happens. That way, you can also manually test those use cases after you have attempted to fix it.

And yes, I'm in the rubber duck wagon myself too! I have two at work (a regular one for easier problems and a Sherlock Holmes duck for the times when things get though.

 

Hey man! Thank you for sharing your thoughts about debugger. I agree with you

 

I was taught as an eager Electrical Engineer that there are two rules to fixing any problem.

1) Start from a known good: You need to be certain you are using components you can trust. Start from code (or hardware) that you can prove is working properly. If HW or SW is in an unknown state, you cannot reason about it.

2) Divide the problem in half: What would it take for you to get rid of half of the test cases or possible inputs? Can you isolate a module and remove dependencies so that you don't have to worry about the interaction of components, but only the functionality of a single system?

This combo has never failed me.

 

Cool and makes great sense. I'm mechanical engineer myself and love when paths cross

 

I have never used a debugger. As for me, I have learnt the most by manually tracing and grinding at the code. Yes it may be inefficient but a lot of the times you don't get an expected output due to some wrong logic. I guess manually analyzing code helps catch those.

Great read btw!

 

I set up a debugger to step through some open source code that I had volunteered to help out with. I was wanting to add a small feature so wanted to step through to get familiar. On the first step through the code I saw a bug that had been around a while and saw some dead code. I wasn’t looking for bugs but I was reading ahead and stepping and it suddenly did stuff that I mentally said “oh! why? that cannot be right...”.

This particular project is a bash tool to encrypt secrets in git called git-secret. No-one had run a debugger as its bash and folks don’t really thinks about debugging bash with breakpoints. Fortunately visual studio code on mac can integrate with bashdb and you can step through. So i made a video to show others on that project how to use a bashdb with vs code.

Maybe you will see something for the first time if you try stepping through code. Just a thought.

 

Me too, never use debugger, just console.log

 

Regarding to debugger vs. manual investigations, I think a good approach is to first do some manual investigation to get more context on what's going on and what could possibly be the root cause of the problem. Once you have an idea the real problem then you can totally use the debugger since it will save you some time. This is what I've done and it has worked for me 🤓

 

Nice compilation! I think the most underrated debugging technique is static typing - the more things your compiler checks, the less bugs will appear.

 

Yep, static typing can help you with reducing bugs, but I'm not sure it's a debugging technique 😅

 

Great advice. I like the "talk to a rubber duck!" A long time ago, my project manager was literally my rubber duck. I would talk about a problem and she would just "hmm hmmm" me for 3 minutes until I say "and... oh! wait a sec" to see everything resolve.

Furthermore, I would add this: baby steps. Either when you develop or debug, doing one thing and then make sure it works as intended will save you a lot of trouble. Sometimes it is easier said than done, you don't need to do TTD per se (but it will help you a lot). It is especially true in embedded system development when you do not necessarily have access to a console or have enough storage for log files. (Debugging with gdb on an embedded system that is time critical can be in the range of the impossible).

 

I would just advise caution when doing console.log() on an object or an array objects you are tracking.
As i usually stick to backend languages, it really took me by surprise when I spent three hours basicly stripping several hundred lines of code in JS looking for a bug, just to realise that thr objects I was loging were printed out as they were at the end of execution and not in their current state for that current iteration as one would expect.
This was denoted by a little alert sign in chrome, which I checked out a bit too late. :D

An easy but boring workaround is to use JSON.stringify on the object when loging it. :)

 

Thanks for sharing that tip. I've faced it multiple times and it still confuses me

 

Excellent post! Three other similar articles that are worth reading as well:

I will say that I've never tried the "tambourine" technique before. I'll have to keep that in mind next time I run into an issue :)

 

Thank you for sharing those articles! I'll check them.
And yep, you have to try tambourine dancing. 😅

 

I think the tambourine is very smart, frustration is the most common bug inducing and detouring factor; let's solve our internal issues with a nonsense ritual! As long as it is known as a ba ritual is great, in the minute it's become a hippy thing with spirituality, just move one

 

Well said 🙃

 

Great Tips, Thanks Nikita!

Doesn't know it is called Talk to a rubber duck. I always follow this trick.

 

The argument that not using a debugging tool is better somehow reminds me of when I was startling out a friend in the office met someone who prided themselves at writing code that compiled and ran correct first time. This was C back in the 90s where your compiler would puke for an hour and your test cases would crash with memory errors without any output. It would take me 20 attempts to get anything none trivial to run before I could even start to check its inputs and outputs to begin to get the logic right. My friend had asked the craftsman how long it took him to write the code they were looking at. The answer was that he took about twice as long as our trial and error approach that involved having the tool (the compiler) give us fast feedback on where our intent had been mistranscribed into code. Yet the person was extremely proud that they wrote code the way they did.

I think the argument that not using a tool like a debugger is in some way more... I am struggling for an adjective... “effective” or “beneficial” seems a little strange to me. If you are having to fix bugs on a large codebase of a team you just joined the best technique is make the bug repeatable, fire up a debugger, and step through. Running a debugger accelerates finding and fixing and also learning the code. You read the code as you step through but are looking at what it does not what the code as written claims it does. I can think of no more effect technique. I could go without the tool and use only other techniques, or I can use the tool and other techniques, and be squashing bugs faster. The clue is in the name of the tool!

What about if its not a large strange code base? Write a unit test, bang out some code, set a breakpoint at the top, and step through the code rewriting and moving the breakpoint. I find this an effective way to get from “looks finished” to ready to ship. Maybe it doesn't work for everyone all the time but a debugger is is certainly a core tool that every dev should be able to run on their codebases.

 

How does the tambourine work? It makes the other developers fix your bugs for you to make you stop?

 

It's one of several possible applications 😃

 

Thanks man!

You allowed me discover git bissect, rubber duck and tambourine

To be honest, i didn't understand anything about tambourine!

 

Just try it. The more people around the better it works.

 

Only for back-end systems, though. There have been relatively few Tambourine debugging kits released for Java, though.

 

Add breakpoints / console.log statements in every try/catch statement that you can find. Chances are, you may be swallowing the real problem, without even realizing it. I wish debuggers had an option to toggle this on when needed.

 

A great post with useful tips, thank you!

 

Writing tech articles - you are doing it wrong. Putting around the 10 BASIC principles of debugging and slapping such title .. nah

 

Thank you for sharing your criticism! I appreciate it

 

Thanks man that was excellent article about debugging!

 

Thank you man, it's a very good post :)
The last one is very funny XD

 

The collection of all sorts of debugging was really helpful. Now I know where to look at. Thanks :-)

 

I normally make a copy of the affected Code and undo my code step by step until I reach the point where the error might have occurred. Done

 

Real programmers don't use silly operating systems and, if they're lucky, they can twiddle an LED for debugging.

 

oh my gosh 🙃

 

On the comment your colleage did about debugger I agree, but the truth is almost no one has all the time in the world to search bugs manually, which usually takes longer.

 

If you really want to remove Bugs from your code:

  • Learn & use Design By Contract (Hoare Logic), TDD is poors man DbC.
  • Make variables Immutable, as much as possible, if your lang allow.
 
 

Jacob Herrington's article on git bisect is great and a lot less dry than the documentation. dev.to/jacobherrington/git-bisect-...

 

Not satisfied with this tricks

 

What techniques can you share to help improve things?

 
Sloan, the sloth mascot Comment marked as low quality/non-constructive by the community View code of conduct

Title is VERY CLICKBAITY. And tips are very basic, except the last one.

 

Hey Dani,

If you feel that way, perhaps this is something to discuss privately with the DEV.TO staff or directly with the author? Or perhaps you would be willing to share some ways to improve the article? Non-constructive criticism doesn't help out with anything: the author and community members witnessing this might be discouraged from posting for fear of being called out and rejected. We have readers of all skill levels on this site, and perhaps your experience level removes you from the target audience of this article.