Right now, i find bugs but i have a hard time fixing them. Please tell me if there are any tactics or strategies that can help to fix bugs faster. More importantly, are there any tips
or techniques to reduce the chances of having bugs in my final code ?
Adhere strictly to the usual conventions of formatting, identifier spelling, object orientation (eg encapsulation, data hiding).
Design your algorithms and classes on paper before you try to commit them to code.
Use the features (eg generics, @Override annotation) which will produce errors messages from the compiler; remember the compiler error is your friend.
Andy Jack wrote:I create my own test cases which test for boundary conditions and such. I don't know how to develop professional style test cases, but we'll leave that for another day...
Well, for starters, I'd get a proper testing app - of which one of the most popular is JUnit - and try and get into the habit of writing tests while you're programming.
Please tell me if there are any tactics or strategies that can help to fix bugs faster.
1. Document your programs well. If you know what a method is supposed to do, it's usually much easier to work out what went wrong if it doesn't. Learn how the javadoc comments work, because they can be invaluable.
2. Avoid situations where references can be null. NullPointerException's are some of the most pernicious and hard to find of all, because they are thrown automatically by the JVM, and sometimes miles from where the problem actually originates. So:
3. Never ignore exceptions. Specifically, never do anything like this:at the very least, print out the stacktrace; but better still (especially while you're testing) may be to simply let the program fail, in which case you will get the stacktrace automatically.
And when you get the stacktrace, read it carefully. It contains tons of valuable information.
More importantly, are there any tips or techniques to reduce the chances of having bugs in my final code ?
Yes. Program defensively.
Whenever you write a method, think not only about what it should do when things go right, but also when they go wrong; and make sure you have a strategy in place for when it does.
Test all arguments to your methods, and throw IllegalArgumentException (again, with a good message) if they're wrong.
Remember: the faster you find an error, the easier it is to correct it, so a good maxim to follow is: fail early, and fail LOUD.
- X 2
C.A.R. Hoare, winner of the 1980 Turing Award, wrote:There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.
My strategy for avoiding and finding bugs is to write code of the first kind. To do that, you need to choose good names that clearly state the intent, have good separation of concerns, and maintain the code in a state such that it is well-factored into single-minded classes and methods. My methods are generally no longer than 10 lines of code. Others average even fewer; I write/refactor to many single-line methods. Compose method is one of my goto refactoring techniques.
Without clarity and conciseness, hunting down bugs in your logic is much harder than it needs to be in most cases. The discipline of Test-Driven Development also helps me tremendously.
I don’t like multiple return or return in the middle of a method. I know people will disagree with me about that, but I can think of two ways to enhance their suggested solution, both making the programming more defensive:-Both my versions have the advantage that the calling code can “find out” whether the add method was called successfully. The second attempt is much more assertive, and the boolean return is now redundant:-
Campbell Ritchie wrote: I know people will disagree with me about that...
True . I think the original version is much clearer than the first "improvement" (and it could return false inside the first condition without affecting the readability if that was desired).
I would prefer the second approach in general, though it does actually change the behaviour, and that may not always be desirable or (if the code is already released) possible. But if something is considered an error, I would always prefer to throw an exception, following Winston's earlier advice.
Junilu Lacar wrote:My strategy for avoiding and finding bugs is to write code of the first kind.
Me too; but in 35 years, I've never quite mastered the art completely.
@Andy: Junilu's point is a good one, however. An ounce of prevention is worth a pound of cure; and to that end, learn to Write Dumb Code.
Andy Jack wrote:More importantly, are there any tips or techniques to reduce the chances of having bugs in my final code ?
One more - although this has more to do with preventing bugs than finding them - protect encapsulation.
Put private in front of everything - that is: fields AND methods, whether instance or static - until (or unless) you know that they will be used outside your class. Additionally, don't create getter and setter methods (especially the latter) willy-nilly.
Also: put final in front of everything - especially classes - again, until (or unless) you know that they will need to be extended (in the case of classes or methods) or changed (in the case of fields).
You can always change a private or final entity later on if you need to. What you can't do is change it back.
Campbell Ritchie wrote:I don’t like multiple return or return in the middle of a method. I know people will disagree with me about that, but I can think of two ways to enhance their suggested solution, both making the programming more defensive:
IMO, the guard/sentinel clause is an acceptable exception to the single exit point "rule". While the enhancements you suggested are reasonable, they change the original behavior. The example is meant to show how much easier it would be to understand the logic if it were composed of method calls with intention-revealing names rather than have all the implementation details in a single method.
Some coding anti-patterns are addressed by the refactoring: arrowhead code (unnecessary complexity) and obscure intent (drowned out by implementation details) and multiple responsibilities in one method.
Winston Gutkowski wrote:at the very least, print out the stacktrace; but better still (especially while you're testing) may be to simply let the program fail, in which case you will get the stacktrace automatically.
My advice would be to always let the program fail, especially in production. Any exception to this rule must be well grounded and documented.
The reason is that error messages in logs may go unnoticed for
It's true that I've in my entire career developed applications where the accuracy of the results was crucial (financial applications and the like). One of my first projects involved Monte Carlo simulations, and the entire job took many hours to compute. To save the useful work in case of failure, I've decided - on my own - to skip failed iterations and not include them in the results (of course, warning were issued and printed), allowing the program to finish and produce results. I was quite reprimanded at that time, so my strategy was fail-deadly ever since. I've never regretted that.
But perhaps other domains are more forgiving.