Yesterday at work, a colleague had what I think is a common issue for developers: Code was behaving incorrectly, and he couldn’t see why. We’ve all been there; the trick is not hanging out. Today I’m going to talk about two great developers I follow online, Safia Abdalla and Eric Lippert, and what we can learn from them about not getting stuck in a debug issue.
What I think we can learn is that this type of “something is broken and I don’t know why” debugging is mostly about eliminating things that could be wrong by, for instance, making a minimal code sample that will reproduce the bug.
I’m going to quote Eric Lippert’s list of observations so that we can examine it more conveniently:
- The exception is not triggered by use of E. It is triggered by any attempt to access the type M. (As one would expect in the case of a type load exception.)
- The exception reproduces whether the field is static or instance, readonly or not; this has nothing to do with the nature of the field.
- The exception has nothing whatsoever to do with “invocation”; nothing is being “invoked” in the minimal repro.
- The exception has nothing whatsoever to do with the member access operator “.”. It does not appear in the minimal repro.
- The exception has nothing whatsoever to do with nullables; nothing is nullable in the minimal repro.
The actual issue Eric is working on is a somewhat arcane type issue in C#, and I’m not interested in it here. What I am interested in is how many situations Eric eliminated. All of those bullet points are lines of reasoning we don’t need to pursue. Earlier commenters were worried about whether nulls were part of the issue, so Eric wrote some code that reproduced the issue in a program where nothing is null, and nothing is nullable. Boom! No need to consider nulls.
Eric shrank the search space a lot. If you think of debugging as searching a space for a solution, this was a great example of a “branch and bound” – several branches of reasoning are eliminated from further investigation by a small code experiment.
Programming, like most tasks in engineering and sciences, involves iterating through a search space and identifying the optimal solution to a particular problem. Becoming a skilled programming is about reducing the size of the search space for a particular problem that you need to address — you become a good programmer when you need to try fewer things before you get to the “right” solution.
When I first started writing code all day, I would often push code around pointlessly, saying to myself, “I know this line isn’t the problem, but it looks weird so I’ll just change it,” and then changing that line wouldn’t fix anything, and I would go on to push more code around, making things worse because I was making changes with no sort of hypothesis.
I think that Safia and Eric both, when debugging, are eliminating potential sources of the problem every time they change any code. This skill allows them to arrive at correct code quickly, and is therefore immensely valuable.
So next time you’re trying to fix some code, make a hypothesis about what could be wrong, then make a change that definitively tests that hypothesis. I know this is something I can focus on more, and I hope this advice will help you as well.
Till next week, happy learning!