How to debug small programs
- Transfer
A lot of the bad questions that I see on StackOverflow can be described with the following formula:
Note transl.: This is a translation of the article " How to debug small programs ", which is referenced in the reference section of the English StackOverflow , devoted to the creation of minimal, self-sufficient and reproducible examples. It seems to me that it perfectly describes what every programmer should know - the basics of debugging non-working code.
If you are reading this note, then most likely you followed a link that either I or someone else left under your question on StackOverflow shortly before this question was closed and deleted. (If you read this note for another reason, leave your favorite tips for debugging small programs in the comments).
StackOverflow is a question and answer site, but for very specific questions about specific code. “I wrote some kind of buggy code that I can’t fix” - this is not a question , this is a story , and not too interesting. “Why, when subtracting a unit from zero, do I get a number greater than zero, which is why my comparison with zero in line 12 is incorrectly calculated in
So, you ask the Internet to fix your broken program . Surely you have never been told how to debug small programs, because - attention - what you are doing now is not the most effective way. Today is a great day to learn how to debug your code yourself, because StackOverflow will not do this for you.
Below I assume that your program compiles, but does not work correctly, and, moreover, you have an example that demonstrates this incorrect behavior. So, how to find a bug:
First, turn on all compiler warnings . There is not a single reason why a correct program of 20 lines of code could receive a compiler warning. A warning is when the compiler tells you: “this program compiles, but does not do what you think,” and since this is exactly the situation you are in , you should listen to it.
Read the warnings very carefully . If you don’t understand why this or that warning was issued, this is a good question for StackOverflow, because it is a specific question about specific code . Add the exact warning text, the exact code on which it was issued, and the exact version of the compiler you are using to the question.
If your program still has a bug, find the rubber duck. If there is no rubber duck near, find another student-programmer - there is almost no difference between him and the duck. Explain the refiner in simple words why every line of every method in your program is obviously correct. At some point you will encounter difficulties: either you do not understand the method that you wrote, or there is an error in it, or both. Concentrate on this method; most likely the problem is in it . No, really, the duckling method works. And, as the legendary programmer Raymond Chen added in the comments below ( approx.per .: to the original post ): if you can’t explain to the duck why this or that line is needed - maybe this is because you started writing code before you got a plan.
If your program compiles without warnings, and the duck has no serious questions for you, and there is still a bug, try breaking the code into smaller methods , each of which performs exactly one logical operation . A popular mistake of all programmers, not just beginners, is to write methods that do several things at once and do them poorly. Small methods are easier to understand, and as a result, it is easier for you and the duck to notice errors.
As you remake methods into smaller methods, stop for a moment to write a technical specification for each method. Let it be one or two sentences, but the specification helps. The technical specification should describe what the method does, what parameters are valid, what return value is expected, in what cases an error will occur, and so on. Often when writing a specification, you realize that you forgot to handle some case, and the bug is exactly in that.
If you still have a bug, check and double-check that your specifications describe preconditions and postconditionseach method. A precondition is something that must be fulfilled before a method starts working. A postcondition is something that will be executed when the method finishes work. Examples of preconditions: “the argument is a valid non-zero pointer”, “at least two vertices are in the transmitted linked list” or “this argument is a positive integer”. Examples of postconditions: “in the linked list, it became one element less than it was at the input”, “a certain piece of the array is now sorted” or something like that. If the precondition is violated, this is an error in the place where the method was called. If after fulfilling all preconditions the postcondition is violated, this is an error in the method. Again, when formulating pre- and postconditions, you will often come across a forgotten case.
If the bug is still here, learn to write statements( assertions ) to check pre- and postconditions. A statement is almost like a comment, only it tells you when the condition is violated; and a violated condition is almost always a bug. In C # you can say
Then write tests or examples.for each method that verify that it works correctly. Test each part separately from the rest until you are confident in it. Use a lot of simple tests; if your method sorts lists, try an empty list, a list of one, two, three identical elements, three elements in the reverse order, several long lists. Most likely, your bug will appear on a simple test, and then it will be easier to analyze.
Finally, if there is still a bug in your program, write on the piece of paper an exact description of the action that you expect from each line of code when starting with your example . Your program only takes 20 lines. You should be able to write everything that she does. Now go through the code using the debuggerchecking each variable at every step and line by line checking the behavior of your program with a piece of paper. If a program does something that is not written on a piece of paper, then the error is either on the piece of paper (in this case you do not understand what your program is doing) or in the program (in this case you wrote something wrong). Correct what is wrong. If you don’t know how to fix this, at least you have a specific technical question that you can ask on StackOverflow! In any case, repeat the process until your description of the program’s behavior and the actual behavior of the program match.
While you are working in the debugger, I urge you to pay attention to the slightest doubt. Most programmers naturally believe that their code works as expected, but you are debugging it precisely because it is not! Many times I debugged the code and out of the corner of my eye I noticed a quick flicker in Visual Studio, which meant “this memory has just been changed”, but I knew that this memory was not related to my problem at all. So why has she changed ? Do not ignore these nit-picking, study strange behavior until you understand why it is either correct or incorrect.
If it seems to you that this is all very long and requires a lot of effort, then only because it is. If you cannot use these techniques on a program of twenty lines that you yourself wrote, it is unlikely that you will be able to use them on a program of two million lines that someone else wrote. However, developers in the industry do this every day. Start training!
And the next time you solve your homework, write the specifications, examples, tests, preconditions, postconditions and statements for each method before writing the method itself ! You will significantly reduce the likelihood of a bug, and if it does appear, you will most likely be able to find it pretty quickly.
This technique will not help to find all the bugs in each program, but it is extremely effective for those short programs that novice programmers write in their homework. It can also be used to find bugs in non-trivial programs.
Here is my homework solution. It does not work.And ... that's it.
[20 lines of code]
Note transl.: This is a translation of the article " How to debug small programs ", which is referenced in the reference section of the English StackOverflow , devoted to the creation of minimal, self-sufficient and reproducible examples. It seems to me that it perfectly describes what every programmer should know - the basics of debugging non-working code.
If you are reading this note, then most likely you followed a link that either I or someone else left under your question on StackOverflow shortly before this question was closed and deleted. (If you read this note for another reason, leave your favorite tips for debugging small programs in the comments).
StackOverflow is a question and answer site, but for very specific questions about specific code. “I wrote some kind of buggy code that I can’t fix” - this is not a question , this is a story , and not too interesting. “Why, when subtracting a unit from zero, do I get a number greater than zero, which is why my comparison with zero in line 12 is incorrectly calculated in
true?” - here is a specific question about a specific code. So, you ask the Internet to fix your broken program . Surely you have never been told how to debug small programs, because - attention - what you are doing now is not the most effective way. Today is a great day to learn how to debug your code yourself, because StackOverflow will not do this for you.
Below I assume that your program compiles, but does not work correctly, and, moreover, you have an example that demonstrates this incorrect behavior. So, how to find a bug:
First, turn on all compiler warnings . There is not a single reason why a correct program of 20 lines of code could receive a compiler warning. A warning is when the compiler tells you: “this program compiles, but does not do what you think,” and since this is exactly the situation you are in , you should listen to it.
Read the warnings very carefully . If you don’t understand why this or that warning was issued, this is a good question for StackOverflow, because it is a specific question about specific code . Add the exact warning text, the exact code on which it was issued, and the exact version of the compiler you are using to the question.
If your program still has a bug, find the rubber duck. If there is no rubber duck near, find another student-programmer - there is almost no difference between him and the duck. Explain the refiner in simple words why every line of every method in your program is obviously correct. At some point you will encounter difficulties: either you do not understand the method that you wrote, or there is an error in it, or both. Concentrate on this method; most likely the problem is in it . No, really, the duckling method works. And, as the legendary programmer Raymond Chen added in the comments below ( approx.per .: to the original post ): if you can’t explain to the duck why this or that line is needed - maybe this is because you started writing code before you got a plan.
If your program compiles without warnings, and the duck has no serious questions for you, and there is still a bug, try breaking the code into smaller methods , each of which performs exactly one logical operation . A popular mistake of all programmers, not just beginners, is to write methods that do several things at once and do them poorly. Small methods are easier to understand, and as a result, it is easier for you and the duck to notice errors.
As you remake methods into smaller methods, stop for a moment to write a technical specification for each method. Let it be one or two sentences, but the specification helps. The technical specification should describe what the method does, what parameters are valid, what return value is expected, in what cases an error will occur, and so on. Often when writing a specification, you realize that you forgot to handle some case, and the bug is exactly in that.
If you still have a bug, check and double-check that your specifications describe preconditions and postconditionseach method. A precondition is something that must be fulfilled before a method starts working. A postcondition is something that will be executed when the method finishes work. Examples of preconditions: “the argument is a valid non-zero pointer”, “at least two vertices are in the transmitted linked list” or “this argument is a positive integer”. Examples of postconditions: “in the linked list, it became one element less than it was at the input”, “a certain piece of the array is now sorted” or something like that. If the precondition is violated, this is an error in the place where the method was called. If after fulfilling all preconditions the postcondition is violated, this is an error in the method. Again, when formulating pre- and postconditions, you will often come across a forgotten case.
If the bug is still here, learn to write statements( assertions ) to check pre- and postconditions. A statement is almost like a comment, only it tells you when the condition is violated; and a violated condition is almost always a bug. In C # you can say
using System.Diagnostics;at the beginning of the code, and in the middle writeDebug.Assert(value != null);or something similar. Each language has a mechanism for formulating statements; ask someone to tell you how to use them in the language you are writing. Write statements for the preconditions at the beginning of the method, and statements for the postconditions before the line where the method ends. (Of course, this is easiest to do when each method has exactly one return point.) Now, if you run the program and some statement turns out to be incorrect, you will find out what the problem is, and it will be easy to debug. Then write tests or examples.for each method that verify that it works correctly. Test each part separately from the rest until you are confident in it. Use a lot of simple tests; if your method sorts lists, try an empty list, a list of one, two, three identical elements, three elements in the reverse order, several long lists. Most likely, your bug will appear on a simple test, and then it will be easier to analyze.
Finally, if there is still a bug in your program, write on the piece of paper an exact description of the action that you expect from each line of code when starting with your example . Your program only takes 20 lines. You should be able to write everything that she does. Now go through the code using the debuggerchecking each variable at every step and line by line checking the behavior of your program with a piece of paper. If a program does something that is not written on a piece of paper, then the error is either on the piece of paper (in this case you do not understand what your program is doing) or in the program (in this case you wrote something wrong). Correct what is wrong. If you don’t know how to fix this, at least you have a specific technical question that you can ask on StackOverflow! In any case, repeat the process until your description of the program’s behavior and the actual behavior of the program match.
While you are working in the debugger, I urge you to pay attention to the slightest doubt. Most programmers naturally believe that their code works as expected, but you are debugging it precisely because it is not! Many times I debugged the code and out of the corner of my eye I noticed a quick flicker in Visual Studio, which meant “this memory has just been changed”, but I knew that this memory was not related to my problem at all. So why has she changed ? Do not ignore these nit-picking, study strange behavior until you understand why it is either correct or incorrect.
If it seems to you that this is all very long and requires a lot of effort, then only because it is. If you cannot use these techniques on a program of twenty lines that you yourself wrote, it is unlikely that you will be able to use them on a program of two million lines that someone else wrote. However, developers in the industry do this every day. Start training!
And the next time you solve your homework, write the specifications, examples, tests, preconditions, postconditions and statements for each method before writing the method itself ! You will significantly reduce the likelihood of a bug, and if it does appear, you will most likely be able to find it pretty quickly.
This technique will not help to find all the bugs in each program, but it is extremely effective for those short programs that novice programmers write in their homework. It can also be used to find bugs in non-trivial programs.