debugging

Problem? No problem.

Painful revelation #5473984 since beginning to code: I need better problem-solving habits.  Sure, I’m plenty smart and can use problem solving skills if I really need to, but step-by-step problem-solving is not yet second-nature.

I didn’t realize how sloppy my problem-solving process was until I started school last year.  At that point, I had been doing JS on my own for about 9 months, and my approach to problem solving was as follows:

  1. puzzled stick figureSee a problem
  2. Brainstorm about what could be causing the problem -> {usually no idea}
  3. Fiddle with a few things and look for typos -> {typically creates even more problems}
  4. Get super frustrated
  5. Print out my entire database
  6. Anger-eat a sleeve of Oreos, taking no pleasure in them
  7. Undo everything back to just the one problem
  8. Binge-watch Parks and Rec until I can calm down enough to think about the problem again

Now, having looked on with some brilliant colleagues, I see that there are much less maddening ways to find bugs.  Even though many problems still start out as a ‘black box’ in my mind, I now take a much more scientific approach — something like this:

  1. See a problem.
  2. Distill the problem.  Say what you expect vs. what you are getting, or draw it on paper if it is complicated.  This helps you crystallize the problem in your mind, and sometimes even helps you solve it at this step.
  3. Isolate the problem function(s). This may mean commenting out sections of HTML or JavaScript in order to focus on just one part of the code.
  4. Simplify inputs and outputs.  Sometimes cluttered code or complex variable names can make a problem seem overly complicated.  Separate long conditionals into into multiple lines when you can; replace variable names with simpler names.  (Classic blunder: Just because a function is named “postToApi” doesn’t mean that calling this function will actually post to an API.  Sometimes semantic naming can trip you up by creating an assumption of functionality.)  If your program isn’t logging the object you expected, replace the object with a string like ‘foo’ make sure your log statement is being executed.
  5. Break the problematic part into smaller pieces and run through them one by one.  Split a complicated function into smaller pieces.  You can run each piece individually, then start putting pieces together one by one.  As you add each piece in, you’ll be able to see which addition breaks the combination. (Keep in mind — it may be more than one problem and likely is if you’ve gotten this far…)
  6. Watch EVERYTHING.  Watch each variable in the buggy snippet of code from start to finish using logs or a debugger statement.  See where the variables diverge from what you expected.  The nice thing about debuggers is that they often uncover contributing factors that you might not have suspected.

If all else fails, rewrite from scratch.  Sometimes you hit a point where you’re not making progress by debugging.  This can be caused by a stupid mistake, like a typo that you don’t see, or by a bigger logical flaw.  In either case, sometimes the quickest way to a solution is to re-write one or many parts of your function from scratch, without looking at the original.  This might sound like a cop-out, but you don’t always have time to walk away from a problem 10 times and come back to it; sometimes you just need a clean slate to rethink your approach.

Keep breathing.  Keep eating and drinking.  Go watch Ron Swanson eat a Meat Tornado, or whatever gets your mind in a different space.  An exhausted and frazzled engineer is like a bull in a china shop, perpetuating code problems through little mistakes and oversights, but a happy and healthy engineer is better than the best debugger there is.

Advertisements

Certainty is a fickle friend

Later on my life, when I look back on this time of starting my job, I imagine I’ll remember a long period of mental haze.  Every day, the rug gets pulled out from under me in a new way, forcing me to adapt my working style to a new variable.  When I think I’ve found the last thing that could break, I inevitably discover a new ‘weakest link’, thus blowing all my earlier assumptions out of the water.

Before I got into software, I worked as a program manager at a mentoring program.  Part of my job was training team members into their roles.  Most of my team members were doing their first job out of college, so I got to play expert, not only teaching them best practices about mentorship, but also how to organize and schedule themselves.  I knew things, and I relished the opportunity to share them with colleagues.  I was a super-hero of information synthesis!

Those days are gone.  Now I only fantasize about knowing things as I dive headfirst into a bottomless pit of bug-wrangling uncertainty.

First it was string disappointment.  Of all of the JavaScript data types, I felt I could most depend on strings, because they were just chstick-man-with-capearacters between quotes.  Then I started writing keyboard shortcuts for a web app, and the browser threw an error because “a” != “a”.  WTF?!!  I had no idea where to go from there, until my colleague suggested that i might be dealing with unloggable unicode characters, wicked invisible entities that live to screw your validations over.  I left that experience feeling that my trust in strings had been misplaced indeed.

Next, my libraries let me down.  Although I painstakingly followed the exact directions of a d3 library, nothing happened on the canvas.  No error thrown either.  Just a whole lot of nothing.  I debugged, tried variations, checked the github issues to see if anyone else had this problem.  Then finally, on inspecting an object, I found that one of the methods in the library’s initialize instructions didn’t actually exist.  Library FAIL.

Finally, Chrome let me down.  After I pushed a big commit, I felt like an engineering superheo until my colleagues informed me that the commit had broken their apps in Chrome.  There was  no small amount of embarrassment on my part as I perused the code, trying to figure out how the changes I made could possibly be causing these errors.

As it turns out, Chrome had saved a JSON draft in local storage that was not compatible with my changes.  A quick localStorage.clear() was all we needed.  (Shouldn’t local storage be cleared automatically when one pulls from a github repo?)  From a new JavaScripter’s perspective, it seems that no good deed goes unpunished: tiny half-assed changes have tiny repercussions, but hard work results in big changes that lead to big, embarrassing errors.

But I’ve got to keep trucking.  I may not be a superhero of information-synthesis anymore, but I have Tom-Haverford-inspired hopes of donning a new cape: the superhero of information-FAILURE-synthesis.  Knowing why something is failing is the first step toward better understanding, which beats blind certainty by a long shot.

Fail early, fail often, fail purposefully: that’s the developer’s credo.

TUTORIAL: Debugging Blanket Errors

Note: this post is an extension on my earlier debugging guide: JavaScript Debug Chart, the second in a series about debugging.

Sometimes, your browser will give you an error so unspecific, you’ll want to throw tomatoes at it. For example: “TypeError: Undefined is not a function”.  This happened to me the other day while working on an exercism.

No matter what I did, it seemed like the error stayed the same and my frustration level grew. And tools like repl.it and JS Hint weren’t helpful; they just gave me the same error as the console with no explanation.

A few days later, I’ve now fixed my code and wanted to post some lessons learned for those in a similar situation, because If you’ve already ruled out everything you know, blanket errors like this one may need their own debugging process.  Here are a few steps to get you started:

1. What did you last work on?

If your code was working 5 minutes ago, you are the culprit.  Try undoing or going back to an earlier git version.

2. De-nest.

You’re going to have to pull your code down to the smallest building blocks so that you can examine each piece and see where the code is going wrong.  You can test smaller code chunks individually in repl.it, using repl’s console to plug in variables.

3. Re-organize.

If you’re working with a number of interdependent functions, it’s likely that one of your functions or variables is simply defined too far down in your code, causing the browser to throw an “undefined” error.  Look through and make sure each variable, function, script is defined before it is called.