Archive for the ‘debugging’ tag
Debug It: Book Review
David Agans' 'Debugging' is the best debugging book that I've read so I was intrigued to see that there was another book being written on the subject.
Paul Butcher offered me a copy of the book to review so I was keen to see whether it was more like 'Debugging' or 'Release It' as Ted Neward suggests.
The Book
Debug It by Paul Butcher
The Review
Much like Krzysztof Kozmic I found that a lot of the ideas early on in the book were similar to what I've been taught by my ThoughtWorks colleagues over the last 3 1/2 years.
I do think it's really good seeing these ideas in words though because it's quite easy to forget about the best way to approach problems in the heat of the moment and the approaches suggested by Paul certainly aren't done everywhere in my experience.
These were some of my favourite parts of the book:
- When chasing a bug Butcher suggests that a useful technique to use is to try and disprove your theory of why the problem has happened. Too often we come up with a theory and just adapt any data to fit our thinking. This is also known as confirmation bias.
In his talk 'Pimp my architecture' Dan North suggests a similar approach more generally when working out how to tackle any problem. Each person has to take the other person's argument and then fight for that to be used instead. I quite like this idea – certainly something to try out.
- When discussing the need to refactor code as we go along, the author points out that if the code we want to change doesn't have any tests around it then we need to write some to provide us with a safety net.
Remember, however, that refactoring crucially depends upon the support of an extensive suite of automated tests. Without tests, you’re not refactoring. You’re hacking.
Hamlet D'Arcy makes a similar point but perhaps more forcibly in a really good blog post and Michael Feathers' 'Working Effectively With Legacy Code' covers the topic in much more detail.
- One tip which seems obvious but is still one I've tripped up on many times is to go through the list of changes that we've made before checking in! It's incredibly easy to forget about some seemingly insignificant change that we made before checking it in and perhaps breaking our application unexpectedly.
Somewhat tied in with this is the idea of checking in small changes more frequently and only changing one thing at a time which I wrote about previously.
- I like that Butcher puts a lot of emphasis on ensuring that we actually know what's going wrong before we attempt to fix anything.
Without first understanding the true root cause of the bug, we are outside the realms of software engineering and delving instead into voodoo programming or programming by coincidence.
This is particularly true when addressing performance problems where he rightly suggests that we should look to profile the code before making a premature optimisation.
He also suggests using the debugger so that we can get a good idea about what the code is actually doing when it's running. While I think this is useful I feel that the need to use the debugger in this way frequently might suggest that our code is difficult to reason about which could well be something to address.
- A couple of other cool suggestions are to call on team mates to help us out if we're getting stuck trying to fix a bug and if that's not possible then to either write out the problem or talk to the rubber duck.
If you don’t have someone to play the role of cardboard cutout, all is not necessarily lost. Try scribbling down a narrative of the problem on paper or perhaps composing an email to a friend. The trick is not to censor yourself — just like a writer would.
I don't think the importance of communicating with team mates can be underestimated and Butcher points out that if we notice a bad pattern in the code than it's no good just going through and changing it everywhere. We need to talk with the rest of the team to decide whether we can get an agreement on the way we'll develop code going forwards.
- The only idea I disagreed with is that of putting assertions into the code which I feel adds clutter to our code even though it makes it fail faster than would otherwise be the case. From my experience if we write good enough unit tests and have good logging in our code then the assertions aren't needed.
In Summary
The book is pretty quick to read at around 200 pages and packs a lot of useful tips into that space. I'd say it's a pretty useful book to keep by your desk to refer to now and then.
Debugging: Get to a stage where it works
When debugging a problem I've learnt far too many times that where possible the most effective approach is to try and get the application back into a state where it does work and then analyse the changes that have resulted in it no longer working as expected.
About 7 or 8 years ago when I used to code PHP at school and university that pretty much was my default approach – I didn't really know how to program well enough to work out how to fix something that was broken so I would always just revert back all the steps I'd done until it worked.
I didn't think it was a particularly good approach at the time – I wasn't using source control so I always used to lose my changes – but looking back at that approach now I think it is one of the most effective ones when debugging.
A recent example of this came when we were trying to work out why our selenium server was no longer being launched when we ran the build locally.
There had been some changes on the files involved but they didn't seem substantial, so my pair and I started looking through the code as it was now and trying to work out what it was doing wrong now.
We didn't really succeed in getting anywhere – there wasn't anything that stood out as being wrong.
Somewhat luckily another colleague happened to come across the code that we were looking at and pointed out something else that seemed strange about what was going on. It turned out that more of the code had been changed that we originally thought.
We went back to the subversion logs and realised that the paths to the selenium jar had been changed to be relative rather than absolute – it was a valid change designed to allow us to run the build for different branches locally – but the relative path wasn't correct and therefore the selenium server was never being started even when the build was run from the trunk.
We had actually been attempting to fix this problem on a branch so we had actually made the problem more difficult for ourselves than it needed to be. Not only was the absolute path not going to work for us but the relative path that we needed was also different as we had checked out the code a level up in the directory structure.
In an attempt to get something working we reverted the changes back to a state where we were able to run the build on the trunk and the selenium server starting working correctly again.
We then had a more stable base to make the changes from.
The lesson for me here was that if things go wrong it's more effective to get back to a working state as quickly as possible and go from there instead of theorising on what went wrong and trying to fix it that way.
Coding: Making the debugger redundant
I recently wrote my dislike of the debugger and related to this, I spent some time last year watching some videos from JAOO 2007 on MSDN's Channel 9. One of my favourites is an interview featuring Joe Armstrong and Eric Meijer where Joe Armstrong points out that when coding Erlang he never has to use a debugger because state is immutable.
In Erlang, once you set the value of a variable 'x' it cannot be changed. Therefore if the value of 'x' is incorrect at some point in your program you only need to look in one place to see why that has happened.
With imperative languages like Java and C# variables can be set as many times as you like assuming they've not been declared as readonly for example.
It got me thinking about how the way that we can reduce the need to use the Debugger when writing code in imperative languages. Debugging is so boring and takes so long that spending large amounts of doing it both crushes the spirits and slows you down considerably.
Test Driven Development
Before I learnt TDD if I had a problem with my code the only way I could really find out more about that problem was to turn to the debugger.
One of the aims of writing code test first is to remove the need to debug. As Pat Kua points out in his blog, when you use a TDD approach to writing code, a nice side effect is that you tend to stop using the debugger so much.
Doing TDD is not enough though, we want to look to design our tests for failure so that they do fail we have a useful error message that helps us work out why something failed rather than having to get out the debugger to work it out. Hamcrest matchers are really useful for this, particularly when it comes to analysing test case failures from a continuous integration tool's console.
Writing our tests in a consistent style also helps especially when it comes to setting up mocks and stubs from my experience. If we know how and where these have been setup then we don't need to resort to the debugger to work out why one was or wasn't called – it should be obvious just from reading the test.
Immutability
This is an idea which I touched on in a post I wrote around how writing clean OO code can help reduce the cost of change in our applications, the suffering that having too much mutable state can cause you becoming abundantly clear to me after a coding dojo session where we did just that.
Even using the debugger was difficult because we were trying to remember what the state was meant to be compared to how it actually was.
Greg Young has an interesting presentation which he gave at a Europe Virtual Alt.NET meeting in February (there is also a similar interview on InfoQ) where he talks about how we can model state transitions explicitly by using command objects rather than implicitly by having domain objects keep track of a lot of internal state.
He also describes the use of getters/setters as a domain anti-pattern which I would certainly agree with as it results in behaviour being defined away from the data, usually resulting in unexpected state changes in our objects which we can't figure out without getting out the debugger.
Minimise dependencies
Ensuring that our classes don't have too many dependencies is another useful approach – an anti-pattern which tends to happen quite frequently in the controller of the MVC pattern.
When too much is happening in classes they become difficult to understand and by virtue difficult to test, resulting in increased debugger usage because we've probably missed out some paths through the code inadvertently.
When this happens we want to try and pull some of the similar operations out into another controller to make our life easier.
In Summary
These are some of the ways that I have noticed help reduce our need to rely on the debugger.
Using TDD as an approach to coding helped me cut down my debugger usage a lot and it is no longer my first choice of tool when there is a problem with code.
I'm sure there are other ways to reduce the need to debug, I just haven't discovered them yet!
Saff Squeeze: First Thoughts
While practicing some coding by doing the Roman number conversion last weekend I came across an article by Kent Beck which talked of a method he uses to remove the need to use the debugger to narrow down problems.
He calls the method the 'Saff Squeeze' and the basic idea as I understand it is to write the original failing test and then inline the pieces of code that it calls, adding assertions earlier on in the code until the actual point of failure is found.
The thinking behind this is that we will now have another much smaller test which we can add to our test suite although he did point out that it may take longer to solve the problem this way rather than using the debugger.
I'm not a fan of debugging through code and I believe if we are using TDD effectively then it should reduce the need to use the debugger.
When I got a bit stuck with the parsing of the input for my Roman number conversion I decided this would be a good time to give the approach a trial run.
The actual problem was that I was trying to parse a value such as 'XI' by called String.split("") which looking back of course was ridiculous. This was resulting in an array with 3 values – X,I and "" – which then gave a null pointer exception when I tried to convert it to a numeric value.
Applying the Saff Squeeze allowed me to narrow down this problem and change the implementation when I realised my approach was never going to work.
Although I wasn't able to keep the test which I had created from all the inlining, it did become clear to me from this exercise that my tests around certain areas of the code were not fine grained enough. I find I lose the discipline a bit when I test from the outside in which is something I am trying to improve. This method proved to be a good way of keeping me honest.
Kent suggested that it would take much longer to debug using this approach but I found I was able to solve my problem almost as quickly as if I had debugged through it with the added benefit that I wrote a few extra tests while I was narrowing down the problem.
It is certainly an interesting approach although one which I need more practice with before trying to introduce it into a work environment.
Debugging 3rd party libraries more effectively
Debugging 3rd party library code quickly and effectively is one of the skills which most obviously separates Senior and Junior developers from my experience.
From observation over the last couple of years there are some patterns in the approaches which the best debuggers take.
Get more information
Sometimes it's difficult to understand exactly how to solve a problem without getting more information.
Verbose logging mode is available on the majority of libraries and provides the information showing how everything fits together which is normally enough information to work out how to solve the problem.
Look at the source code
My natural approach, when stuck using a 3rd party library for example, is to read the documentation but I have noticed that better debuggers than myself head to the source code much earlier and try to work out what is going on from there.
This approach certainly makes sense and when I have problems with project code my instinct is to look at the offending code straight away and try to work out what's going on. I haven't quite got this discipline when it comes to library code just yet.
The art of this approach comes in being able to read through the code and realising quickly which parts of the code are important and which can be skimmed over.
An article I read about the expert mind seems to confirm this, stating that an expert in any discipline doesn't analyse more options than others, only better ones.
Don't assume it works
Closely linked to the above is the assumptions we make when debugging.
The best debuggers don't assume that the code does exactly what they expect it to, they take a more critical approach and try to work out whether or not it is actually working by changing small things and seeing what the impact is.
If it does turn out that there is a bug then we can look at the source code and work out whether there is a way around the problem or if there is only one way to solve the problem, submit a patch to fix it.
One change at a time
I think this is probably the most important yet the simplest of the approaches.
It sounds so obvious yet it's so easy to make change after change after change and eventually solve the problem but have no idea which change or combination of changes it was that fixed it.
We need to ensure that we know why we are making each change and revert it if it doesn't have the desired effect.
Read the error message
Reading the error messages that we get and not the error message that we think we saw is yet another obvious approach but one which I have certainly violated.
Sometimes we need to slow down a bit when debugging problems and read exactly what the message is telling us and work from there.
In Summary
Most of the approaches I've noticed seem very obvious but I find that it requires quite a lot of discipline to apply them. When I do approach debugging with this more logical approach problems become much easier to solve.
Some debugging books
One of the best books I have read with regards to debugging code is Debugging – The 9 Indispensable Rules for Finding Even The Most Elusuive Software and Hardware Problems – I actually had the 9 rules stuck to my machine for a couple of months earlier this year, and probably should do that again.
I also noticed that the Pragmatic Programmers have a new book coming out called 'Debug It' – it will be interesting to see the similarities and differences between this book and the other one.