The most effective debugging tool is still careful thought, coupled with judiciously placed print statements. — Brian W. Kernighan, in the paper Unix for Beginners (1979) page 12.
The best debugging tools are not just free to use but they are not programs in which you just step through code one line at a time while looking at the values of your variables and the stack, that is useful but too slow and when I used that debugging technique I found that it made me lazy and unproductive.
What I´m talking about in this series of articles are extremely simple and natural tools (eyes, paper, brain) that will be used in effective techniques (code reading, printing values and thinking). The title of this post is definitely misleading because I will not talk about eyes, notebooks and brains (as in tools), instead I will focus my efforts on the techniques mentioned above by writing one article for each one of them.
Before starting with the first point in the list, I must make something clear: You don´t need to wait for a bug to reveal itself before you can start tracking and killing them. Yes! debugging starts whenever you decide to do some…
I think an hour of code reading is worth two weeks of QA. It’s just a really effective way of removing errors. -Douglas Crockford in the book Coders at Work by Peter Seibel.
Code reading is proposed by Crockford as a weekly team activity in which one of the members of the team will read out loud the code of a program he or she wrote while its displayed on a big screen or projection for everyone else to see. Yes, it sounds super lame and dull but trust me is nothing like that. I came by that quote while reading the book myself and it was just so compelling and mind-blowing that I had to highlight it, write it down in my journal along with a reflection on it an try it for my self. To be honest, after a few months reading code I don’t think Crockford is exaggerating.
This practice accomplishes 4 big things:
- Potencial errors and stupidities are called out by the other programmers and many times by your self.
- Better solutions are proposed.
- Junior programmers learn a lot from the experienced programmers.
- Experienced programmers learn from their fellow experienced programmers. Big boys learn too.
But is not necessary that you do code readings with other people in weekly meetings or code reviews, you can and should read your own code regularly (and other people´s code, GitHub could be your new public library), read some code you wrote last week, last month or at any other point in the past. You will be amazed with the amount of problems you will find, among them you will notice:
- Bugs: Just by going through your code you will start catching bugs that have not even been found by QA or a user, things like a global variable that should be local, or the variable that is not in the right scope, the wrong assumption of an invariant that might not always be present, the asynchronous call that’s been working fine because the computer where it is running is slow (most of them usually translate in concurrency, damn concurrency!), etc. In other words code that is functional but fragile and hanging from a very thin thread.
- Poorly named variables and methods: You will see them, notice them and suddenly they become incredibly annoying, like white noise in your head and needles in your eyes every time you come by them in the code. You will know they are there when you read your code and can’t get a good idea of what that variable represents (what the hell is clrfsk, and what did I mean by stck_pdp?) or what the function is doing.
- Clever routines or functions: If you are reading the weirdest loop you’ve ever written in your life and even when you wrote it you just don’t get what in earth that thing is doing… then it is time to take note of it and write a simple and clear one. Cleverness is the worst enemy of any programmer, stay away from it and listen to Brian W. Kernighan and P. J. Plauger: “If you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.” as seen The Elements of Programming Style
- New approaches: you will start thinking of better ways to solve the problem you solved in the past, some of them will be cleaner, simpler and more reliable, you will notice if you’ve learned since you wrote that last code and how much.
There are basically two ways to read code, one is from top to bottom, the other one is from bottom to top. You can read code as you want, there is no right or wrong way to do it. Ken Thompson says he likes reading code from bottom to top, but I don’t feel comfortable doing it that way, I rather reading from top to bottom following a diagram, this way I can take notes on the abstraction of the program and see if the problems are solved in the correct domain, then I go to the next layer that should represent a new problem by itself with a new slang and all, and repeat this process until I’m done.
Code reading is, in my opinion, the most important practice or habit for debugging and learning.
No! Not the movie!
When you read code you must take notes or you will not get much out of it. Write a lot about everything you see:
- Write your thoughts on your approach. Was it good or bad and how bad? Can you make it better? Can you make it simpler? Did you find a bottleneck that will start impacting performance in the future? Will it scale? Is it reliable enough? What happens if suddenly some dependencies of this routine or process suffer a horrible death?
- Did you find a potential bug? Write where you found the bug and how? How it can be triggered and when you expect it to happen? What are the consequences of it? Why did you miss it when you first wrote the code or why you wrote it at all (take a guess)? Then explain how would you solve it and how would you prevent bugs like that in the future (Remember that Crockford wrote JSLint to prevent his own bugs). Pro tip: A metric, If the amount of bugs you repeat (as in type of bugs) decreases and is less and less every time, it means you are getting better, keep going. If not then change your method or be more disciplined following the current method.
- Draw diagrams of how your program works. You may have diagrams already in your notes from when you first designed the program, however I like drawing them again following the code just to see if I end up with the same diagram, this exercise can tell you a lot about the abstractions you used and the clarity of your code as well as how much you deviate from your original designs. Pro tip: A metric, If you deviate less every time from your designs means that the abstraction is good, design is good and your skill is improving.
- Write about code style. Every ugly line of code must be documented in your notebook, tell yourself that you wrote an ugly line of code and why. Is it because the whitespace and indentation is bad? Is it because you don’t understand what that line does? Is it too clever? Bad naming? Pro tip: A metric, If you are writing less and less ugly lines of code every time, then you are getting better.
The more you write down, the more you learn. My preferred place to do these writings is my journal, I add a date to it and then index the entry at the beginning, this allows me to find the notes easily and revisit them whenever I need them. I also put small notes in the side every time I go and read the entries.
Code reading is probably the most important habit in your development as a professional, it will help you debug your programs even if the bugs have not been found by users or testers but without discipline is not as useful as it could be. Do it often and with discipline, write down all you find and be hard on yourself, make your results available for you to revisit whenever you need them and measure your own growth.