My favorite tool for debugging code over my 16 year career is println().
Keep It Simple, Stupid!
Back in graduate school, I worked on a real time control system that managed a hydraulic tractor wheel control. It was for testing tire performance. It used C code on the QNX 2 real-time operating system (before they became POSIX compliant). If there is one universal truth I learned in all that crazy, multi-process, highly concurrent work, it was to keep things as simple as possible.
When something broke, I had to figure it out. I didn’t have Google back then. I had a couple manuals for the operating system, and given that this was a very specialized OS, I was basically on my own. When I hit problems that took a long time to figure out, I would get slammed anytime one chunk of code appeared to be have two responsibilities. It took a lot more effort to isolate which behavior had gone wrong.
Since QNX had very simple and easy to use primitives for communicating between processes, it was usually easier to split up these two responsibilities into separate components, and then try things out again. Then when it broke again, I could pinpoint which thing had gone wrong.
So why did I combine two things in one module in the first place? Usually it was because I tried to do too much at once. I would start coding one behavior, and instead of spending the extra effort to isolate the other behavior, I would lump it in. Well the lesson there was strong. Invest a little extra to isolate things, and then bugs will be orders of magnitude easier to spot and fix.
Do you use that with the most features or that which is most convenient?
When I was in college, I learned how to use SunOS on big workstations. It was painful to pick up UNIX and took real pain to ingrain those commands into my fingers, but I will always be thankful I went through that. I have hired co-workers in the past who did college in Windows and didn’t particularly enjoy pseudo-teaching them Solaris.
But one thing I resisted hard was the idea of using vi. Instead, I preferred the rich text editor the system had. I was even in grad school, and when I saw someone using vi. I asked her point blank, “why do you use that?” I don’t remember the response, probably because I thought she was crazy. Today, I realize she was probably a light year ahead of me. Before you get too inflamed, you can just as easily substitute emacs and the same point applies. A rich text editor may be nicer, but are you going to find it everywhere and through every channel you use?
When I hired people to help me maintain a 24×7 ops center, I made them learn vi. We had Solaris servers and Windows desktops. Many times, we had to access these servers through a terminal server. No X-Windows support so no GUI-based text editor. One of my people asked if he could use emacs. “If you install it, you can use it.” He never did. Basically, vi was available everywhere, worked everywhere, and got the job done. So why spend effort fighting that, when you could be focused on software development.
Which leads me to final point, the piece de resistance
Don’t confuse logging for debugging
Many teams that I joined had no logging solution in place. This dates back to before I wrote Java and certainly before I wrote C code. I worked for a few years on an Ada system, testing other people’s stuff. I essentially wrote tests that printed stuff out on the screens. I would print expected results and actuals, then visually check that they matched. (Hadn’t heard of automated testing yet!)
My first warm fuzzy was when a co-worker wrote her own unit tests and had clearly embedded the same print statements in her tests. Anyway, since I kept writing the same stuff over and over, I build a type of logging class so I could have a consistent styling. Code re-use, right?
When I transitioned to other teams, I would commonly see a lack of some logging class, so I would add one. People always seemed to like my logging libraries. They were basic, primitive, but provided a core feature that was needed.
Suffice it to say, I was happy to see things like log4j hit the scenes and become very popular. But I have always run into situations where plugging in a logging library doesn’t work. Sometimes, the log levels would mask things. Sometimes I hit conflicts because someone else also had a log4j.properties file embedded that would interfere with my own.
But I never confused logging with debugging. To me, logging was something very handy to release to production so you could gather logs of what was happening in production. If necessary, you could turn up the levels to get a more detailed picture of what was happening. But when I am noodling around a new block of code and want to get all the details, I fill it with simple print statements. This works in Ada, C/C++, PERL, Java, and anything else you do. Just print out everything that’s happening and don’t worry about configuring a logging system.
Years ago, I built a system used to move data files between six different sites. I used Java’s file APIs to detect and manage files, but I had it system.exec PERL scripts to actually manipulate the files as needed. One of our partners asked me, “What’s your favorite debugger?” I replied “println.” They seemed taken aback by that. But when I explained that there was no hurdle and it worked everywhere without installing extra tools, he smiled and understood my point exactly.
After I get stuff working right, then I pull our my debug statements and start to think about what sort of production logging I want to add, and what levels will be useful when it goes into the field.
In essence, println statements are great for debugging but not for production. For production, you use log statements.