When I first started at my current job, I lost a lot of time spinning my wheels.
Spinning wheels, in the literal, automobile-stuck-in-mud usage, has two characteristics: The wheels are spinning, and the car is not moving. For programming, this means frantic action, but without forward progress.
Here’s an example of wheel spinning: Method A in project B threw an exception. That’s probably because it called Method B, which got some data from a database, and that data was weird. Was the data weird, or is Method B wrong? What about Method A? Maybe Method A is broken, and the data was right? Is one of the more senior developers busy? Maybe if I step through Method A six more times, I can just figure it out? (For best effect, please read this paragraph aloud four times without inhaling.)
Here are the symptoms of wheel spinning for programmers. You are stuck in a loop where:
- There are multiple causes to a problem, or multiple related problems.
- You have considered each of these problems more than once.
- Your last time through this loop, you did not either decompose the one of the possible causes, prove that one of the possible causes was irrelevant, or find someone to ask.
And here’s why you’re spinning your wheels: If you consider the same problems, without decomposing, eliminating, or asking about them, you will be stuck in this loop forever, because you will always have the same amount of information about the same set of problems. When you are here, the only wrong answer is to stay stuck in the loop.
Instead, you have to make some progress. Pick one possible cause, and temporarily ignoring the others, either:
- Figure out that it is composed of other possible causes, and recur OR
- Prove that your problem still exists in its absence, so you can stop looking at it OR
- Decide that you need to ask someone about it.
“Obviously,” you might object, “but you just told me to break out of the loop by breaking out of the loop. What do I do?”
Here are some specific things that work for me:
Heuristic approach: the most recently changed, most specific code is probably the problem. My first week at the new job, I was stepping through some method, and a security exception was thrown in the middle of our most general purpose, most widely used library. Did I find a bug in the database access code that a bunch of client projects depend on? Of course not. If that code were broken, everything would be broken. Instead of suspecting old, reliable code of suddenly failing, I should look up the call stack for the most specific or most recently changed code, and start there. (Or maybe, you know, check the credentials. It was a security exception, after all.)
Algorithmic approach: make a “what else must be true” graph. I list the possible causes on a piece of paper, choose one, and start finding facts that it would imply if true. “If Method C retrieves the wrong data, either the query it executes is wrong or the data itself is wrong.” This procedure makes a tree where the root is the original problem, the inner nodes are hypotheses about its cause, and the leaves are things I should test. After making the graph, I look at each leaf. If I can determine its truth value on my own, I do so and update the graph. If not, I mark it as something to ask about and continue processing the other leaves. At the end, I have either solved the problem on my own, or proved to my own satisfaction that I need to ask for help.
Surrealistic approach: try “rubber duck debugging.” Put a rubber duck on your desk. Explain the code to the duck as through to a child or a person completely unfamiliar with your technology; justify every line of code. Rubber duck optional. (I believe this technique comes from The Pragmatic Programmer: From Journeyman to Master.)
I hope this post gave you a push out of the mud. Till next week, happy learning!