Developing software is all about problem solving. At the highest level we hope our software solves a problem for our customers and users. At the next level down we’re solving the problem of creating a system that moves data from here, does something with it, and move it over there. Below that we’re solving a problem by implementing sort of algorithm, and at the bottom level we’re solving the problem of properly telling a machine what to do with our functions and data.

When I first started programming it was often these low-level problems— getting the machine to do what I want— that would catch me up and prove the most frustrating- things like a nil object error, an unexpected return value or worse yet, no behavior when I expected something to happen. Over the years a few simple tactics have actually turned these frustrations into nothing more than speedbumps and micro-learning lessons… but there are still those times when I tell the computer to A, it does Z, and I can only respond with a big, fat wtf. Fortunately, by falling back on these steps I can usually find my way again.

[Note: if you’re looking for a great post on problem solving tips be sure to check out Doug Bradbury’s post.]

My “Low-Level” Problem Solving Steps (in order)

1) Read the error message.

More often than not when I hit a snag there’s an error message staring me in the face— and as soon as I learned how to properly read them they become problem solving tool numero uno. Good people put hard work into making sure that stack trace printed out for me– and bless them. Anyone who’s kind enough to make sure other poor saps get a nicely outputted stack trace should start including a link to a charity or beer fund they’d like us to donate to. But seriously, the error message often tells you exactly where to go and what went wrong. When I’m working on a toy project or playing around with a new language a lot of times I’ll just let the error messages guide me along- sure, we can call it error-driven development, but really I’m just messing around— and with good error messages, usually having fun.

2) Read the code, I mean really read the code.

You keep using that word, I do not think it means what you think it means.

When I’m working in a foreign code base I know that trying to understand all of it is not always required and I should trust my teammates and the developer before me. So I’ll look over the code to see what’s going on be all, “Yep, got it. I see that this function returns X”. Then I’ll write a test for what I’m trying to do, and of course I’m expecting the test to fail, but sometimes it fails in some spectacular way that makes my head explode. The error message directs me back to the source code that I was originally looking at, and only after carefully reading through it do I realize that the method is doing some entirely different from what I thought. (Also known as RTFM)

3) Pry! These eyes… are prying

Thanks to the one and only Josh Cheek for my exposure to pry. I think pry should be the first tool that every Rubyist learns. For newbies it’s incredibly valuable to be able to poke around under the hood and for experienced programmers it’s an extremely powerful tool for troubleshooting or fine-tuning an algorithm. In my perfect problem-solving world (well, not entirely perfect because steps 1 and 2 didn’t work, and then there’s still a problem to solve…) I have a spec I can run in conjunction with a well-placed pry statement and not only am I able to quickly solve the problem but I also gain a deeper understanding of whatever it is I’m working on. [Sidenote: Most languages have some form of debugger, but when I’m working in another language, or in a Ruby project that doesn’t have pry available- the best fallback is definitely the humble print statement (eg. puts, println, console.log, etc.).]

Bonus tip: Get your Grep On

I started with TextMate’s ‘find in project’ and moved on to Ack in MacVim and this past year I’ve become a decent grep'er. However, going code spunkeling is often one of the last things I do when solving a problem. I enjoy reading code and I probably have some subconscious fear that if I dive in I won’t come up for air. But a few weeks ago I rolled on to a team with Mark Grant and today he put on a spelunking clinic for me– he dove with both precision and speed. While implementing a feature I came across a method whose name signaled a potentially dangerous side effect, but I had no idea what it actually did. A quick grep revealed only one other use in the project and no method definition. The system we’re working on uses a service-oriented architecture so there were several different projects that could have defined or used it and I wasn’t sure where to start looking. I asked Mark if he knew and he wasn’t sure, but he did know the system well enough to strategically aim his greps. Within a few seconds he followed the trail and pointed me to the method definition and where it was being used. Score one for the grepper.

It’s the Small Steps

Before we can run we need to know how to walk, and when we fall the first step is to get back up. These four steps usually get set me back on the the right path. In a follow-up post I’ll tackle some of the problems higher up the chain.