Soft errors require more finesse to avoid. Carefully aiming before attempting to blast down the problem will save time and frustration, although nothing beats the experience of trying all the possibilities.
I didn’t take the examples verbatim, instead I tried to reason what I thought I needed and make adjustments based on circumstance. This was a mistake as I didn’t understand what I really needed, nor did I understand what was necessary in the example. I should have copied the examples out of the documentation, gotten those to work, and then slowly evolved the code to where it needed to be. Instead I ended up reconstructing the bulk of a few different examples through trial and error. The code ended up being about 100 lines, which is embarrassing that it took me the better part of 2 days to get working. Which brings me to my major lesson.
With strict environments, exploring via failure can be efficient. There maybe only one right way to do it, and that will be the only one of a few options that will compile or not throw an error. With forgiving environments, not throwing an error is not a sign of confidence. It’s similar to looking at the last compiler error instead of the first one. You end up fixing what looks like a real bug, only to determine that it’s irrelevant because something above it misbehaves and so the bug fix was for a completely different problem that may or may not have existed.
This requires more refined up front expectations for environments with soft or hidden errors, the cmd line node debugger isn’t as good as Chrome but it gave me a decent picture of the state of the program. Without planning I didn’t know when my expectations were wrong vs when the code was wrong. I was adjusting my expectations based on what I was seeing the code doing, which was slowly leading me astray.
I’ll end with my worst mistake to illustrate this lesson: When attempting to add an external task to the grunt file, the grunt.loadTasks signature takes a path to the task. I incorrectly read this as path to the task file, when in fact the correct meaning was path to the task directory. Passing the task file didn’t produce any errors, unlike passing it an incorrect path (which did produce errors). This left me stumped for awhile thinking my task contained the problem, when in fact I was just hooking it up incorrectly. Luckily, asking for a quick sanity check from my office-mate solved this, just by asking about first principles I immediate saw my mistake. The worst part was when I corrected the error, I didn’t make the connection that grunt would then attempt to load all .js files in that directory into the node instance. It didn’t throw any errors or warnings when it loaded files without grunt tasks. So then I spent far too much time trying to figure out why my proxy would start with the incorrect arguments when called from the external grunt task!
These aren’t the fault of the tools, there are lessons I must internalize to become better in less restrictive (read: less safe) environments!