This week's book giveaway is in the OCAJP forum. We're giving away four copies of OCA Java SE 8 Programmer I Study Guide 1Z0-808 and have Jeanne Boyarsky & Scott Selikoff on-line! See this thread for details.
Does anyone here have any tips concerning how I could improve my troubleshooting skills when I fix bugs? I have taken college courses in Java programming and I have passed the SCJP certification. When I attempt to fix bugs, it sometimes takes me a long time to find the root cause of the problem?
That's an open-ended question, and the real answer is that it will come with experience. The one piece of advice I can offer is get a debugger and figure out how to use it in your environment. I've worked with a lot of smart people that don't want to take the time to get a debugging environment configured, but then take ten times as much time (it seems to me) to write logger statements.
Oh, one more. Learn how to write unit tests really well, and write lots of them.
1. Learn to read and understand error messages, especially stack traces.
It happens often that people have a problem, and then they post on the forum "Here's my program, I got an error. Can someone tell me what's wrong?". They don't even read the error message - they just think "Oh, there's an error, now I don't know what to do". Error messages contain a lot of information. A stack trace tells you exactly where in the program an exception occurred and what the sequence of method calls was up to the point where the exception occurred. That's a lot of valuable information that helps you the find out why the exception happened.
2. Use logging, or System.out.println()
One simple way to find out exactly what is happening in your program is to put System.out.println() at crucial points in the program, for example at the beginning of methods (so that you know when the method is called), or to print the values of variables. Even better is using a logging framework (I prefer SLF4J and Logback).
3. Learn to use a debugger.
IDEs such as Eclipse and NetBeans have a built-in debugger. You can set breakpoints, so that when you run the program, it stops at the line where you put the breakpoint; using the debugger you can then look at the values of variables at that point, and step through the program statement by statement to find out exactly what is happening. This is a very powerful tool to understand exactly what happens in your program.
4. You need evidence.
When you have a theory about why a bug is occurring, you need to find a way to prove that your theory is correct. A good way to do this is by writing a unit test (preferrably using JUnit, the de-facto standard unit test framework). Don't try to fix things based on your theory without having clear evidence that your theory indeed correctly explains the cause of a bug - shotgun debugging is not an effective way to solve bugs.
Write tests first. Add code incrementally. Make sure all your tests pass all the time. Refactor ruthlessly.
Basically, do Test-Driven Development. In the five years that I've been doing TDD, I don't think I've had to use a debugger to step through code more than a handful of times, literally. When there's a problem with the code, I write a test to isolate the problem. This still takes some experience but the cleaner the code and the design and the more unit tests you have, the easier it is to find problems. At least that's my experience.
Junilu Lacar wrote:Basically, do Test-Driven Development. In the five years that I've been doing TDD, I don't think I've had to use a debugger to step through code more than a handful of times, literally.
Is there any good tutorial on how to write tests well in the Internet?
It is difficult to wade through code to determine the cause of a bug. You can try to break the process into smaller pieces, identifying states that cause or characterize the bug, determining how these states are modified, isolating the code that modifies the states, determine what the correct states should be to mitigate the bug.
I will mention that I've been programming for more than 30 yrs, and recently had a bug I never saw before, and had a HARD time isolating the bug (which was an unexpected recursive call through toString members). It took a few days to identify what part of the code was even associated with the bug.
When you have a theory about why a bug is occurring, you need to find a way to prove that your theory is correct.
An important corollary to this is: Always question your assumptions. Not just to gain evidence to support your theory about what is wrong. You also have question your assumptions about what couldn't possibly be wrong. I've wasted a lot of time over the last 30 years looking in the wrong place because I assumed that some piece of code was behaving a certain way, so I ruled it out of my search, only to find out later that my assumption was wrong, and that was actually where the bug was.
Obviously we have to start our search somewhere, and experience and intuition will often lead us in the right direction, and that's fine. Take the path that's most likely to bear fruit. After a while though, if we're not finding what we expected to find, we have to consider looking in other places that we didn't initially consider viable sources of the problem.
And finally, besides being willing to question our assumptions, we also have to learn to recognize what assumptions we're making. Sometimes we just take certain things so deeply for granted we don't even realize that it's just an assumption and needs to be proved again in this current context if we're going to rule it out.
John Vorwald wrote:I will mention that I've been programming for more than 30 yrs, and recently had a bug I never saw before, and had a HARD time isolating the bug (which was an unexpected recursive call through toString members). It took a few days to identify what part of the code was even associated with the bug.
I'm curious, was there anything about the code that made it hard to find the bug? Were there names of things (variables, constants, methods, objects) that could have been better chosen, for example? Or code that could have been made simpler to understand such that it would have been apparent that an inappropriate recursive call was being made? Could there have been a test written such that it would have failed if an inappropriate recursive call was made?
Was there anything that could have been done in the first place to avoid the bug?