Automagical Thinking

I forgot what I was going to say here.

How to Read Web Analytics

The CEO of BigCorp just got the company’s web analytics report and is trying to make sense of it. He calls in his lead developer and asks him what the analytics report says.

“We’re doing great,” says the developer. “Look at all these hits and page views. We’re having no scaling issues. The report says everything is going well.”

Happy but not satisfied, the CEO calls in his head of marketing and asks her what the analytics report says.

“Not so good,” says the head of marketing. “We have a lot of page views, but no one is sticking around very long or drilling deeper into the site. We need to generate more content and revamp our web design.”

A little nervous now, the CEO also calls in the SEO consultant who’s been working with the company, and asks the consultant what the analytics report says.

The SEO consultant spends a few minutes reading the report. After looking around to make sure the door is closed and no one is listening, the SEO consultant leans forward and asks, “What would you like it to say?”

Too Clever to Debug

“Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.” –Brian Kernighan

This sounds nice, and can (maybe) be good advice in some cases. But it can’t possibly be true; in fact, it rests on a bit of a logically flawed syllogism:

  • A: Debugging is twice as hard as writing code.
  • B: Writing code is hard.
  • C: Ergo, if you write the hardest code you possibly can, you are not smart enough to debug your own code.

The problem sits in both in assumption A, and in an unspoken assumption about what it means for something to be “hard”.

Regarding A, there’s no backing behind this “debugging is twice as hard” assumption; it’s simply stated. Regarding the idea of relative hardness of a problem; the conclusion to the above syllogism assumes that there is a ceiling of “hardness” which you are capable of comprehending. If your code is at that ceiling, it argues, then debugging it will be impossible for you, because debugging it will be harder than writing it, which was the limit of how “hard” a problem you can solve. I reality, I don’t believe this to be the case; something may be “harder” than anything I’ve yet worked through, but that only means it might take me more time and more effort to understand it; not that I cannot possibly comprehend it.

Now, we all know that bugs can be hard to find and fix, so we tend to simply accept, even encourage, statements like this. But are they really true?

Think back to your Algebra, Trig, or Calculus classes; if you took these in the same sort of classes, with the same sort of textbooks, that I did, you were routinely given assignments whose answers were in the back of the book. The idea was, you would work through the problems, and then check your answer to see how you did. If you had an incorrect answer, then you would look at the steps you took to solve the equation until you found the point at which you had made a mistake.

This is, to my mind, completely analogous to programming. You write a piece of code to do a certain thing; then you either write a test, or you simply run it with some sample input, to ensure that it does what you think it does. If you test it thoroughly (note: this is actually hard, and is the reason that bugs often slip into production code), then you will either:

  • a) discover that your code successfully does what you intended, or
  • b) discover a case where your code returns an incorrect result; in other words, a bug, or a place where you made a mistake.

In case b, you’ll do the same thing you used to do with your math homework; you’ll start going through it until you find the place where you made a mistake, and you’ll fix it, and (ideally), you’ll write a test that mocks this scenario, and be satisfied that this bug is squashed.

Debugging is indeed hard; there are several things which make it harder than just solving a math equation. For one thing, in your programs, you may not have total control over what inputs (if applicable) are sent to your function. You might intend that factorial(num) always receives an integer as its argument. What if some malevolent person calls it and sends a String? An octal value? An object? An array? Do you care? Does your function try to detect this and either raise an error or return false? Is returning false or nil really what you want to do in that case? What if a number is sent that is larger than MAX_INT? Does it fail silently, interpret the number as whatever your programming language sees it, raise an error… ? In comparison, you can pretty much ensure that x, whatever it may be, in your Algebra exercise, is a number. It’s probably a Real number. And you can probably find it without too much trouble.

Another thing that I like about this example is that it basically shows that the process of learning math is essentially the process of locating and fixing your mistakes. The instructor, the course, assumes that you will not always get the correct answer. That’s the whole reason there are answers in the back of the book, and that you are assigned those questions which actually have answers. The basic premise is that you will get some questions wrong, and that through the process of realizing you are wrong and going back to find where you are wrong (and fixing it), you are doing something which we call learning math.

Programming, I’d argue, works the same way. You learn to program, not by consistently writing correct programs, but by writing bugs. Then, once you realize you have written a bug, you locate it, attempt to understand why it’s a bug, why you erroneously believed that it would work, what you should do instead, and how to do that thing.

We call this “learning to program”.

If you are not smart enough to find and fix bugs, you are actually not smart enough to learn to program. The good news is, you are smart enough to find and fix bugs, and you are smart enough to program.

The logical error comes in the assumption that writing the hardest/most complex/most clever code that you can is the theoretical limit of your intelligence, and that, since “debugging is harder than coding”, you are not clever enough to see the bugs in your own code.

That’s similar to saying that if you are doing the hardest Calculus question you are currently capable (or barely capable) of doing, if you make a mistake, you are unable to find and fix that mistake, because finding the mistake is (somehow) harder than solving the problem.

Put that way, it seems absurd. Of course finding your mistake isn’t “harder” than solving the problem; finding the mistake is part of the problem. In fact, since generally every step in solving an equation or working out an integral or a derivative involves smaller steps based on things you have already learned and mastered (again… theoretically), you almost definitely can, for sure, find your error if you go through each of your steps carefully, thinking about what you are doing and why you’re doing it.

Sure, finding bugs is hard. That’s because if it was easy, you wouldn’t have written the bug in the first place. The bug represents that place in your algebra homework where you’d make that tiny arithmetic error, or forget to flip the sign when a number was/wasn’t negative, or where you’d mis-apply some other mathematical rule. Spotting where you made that error is simply the process of learning math.

That’s not the same as it being too hard for you to accomplish. I would guess (though I can’t know this) that Mr. Kernighan intended his little aphorism to be taken with a rather liberal grain of salt, that it was actually a bit of the subtle humor engineers and geeks are so fond of. The fact that we’ve taken it at face value and treated it as though it were actually true is our fault, not Brian Kernighan’s.

Now, the actual quote (according to wikiquote) seems to be from Elements of Programming Style, and goes:

“Everyone knows that debugging is twice as hard as writing a program in the first place. So if you’re as clever as you can be when you write it, how will you ever debug it?”

This still has that logical leap embedded in it (”Everyone knows that debugging is twice as hard”, etc.), but it does have a good, valid point, depending on how you define “as clever as you can be”. (If being clever is actually being smart, then “as clever as you can be” would be to only write code which you are capable of debugging… but I digress.)

What I think this really is intended to mean is: if you write code that you do not completely understand, you will (potentially) introduce bugs which you are not capable of identifying or resolving.

The key is code that you don’t understand. The reason you won’t be able to find the bugs is that you don’t understand what your code is doing in the first place. If that’s true, then yes, of course you have written un-debuggable software (for you, at least; someone who understands what you wrote can certainly find your bugs). The other side of this coin is that, if you take the time and effort to understand the code that you wrote, you can find the bugs. And you can remove them.

This is called programming. You’ve probably been doing it for a long time. The fact that we’re still creating and fixing bugs only means that we haven’t finished learning to do it yet.

Current Desktop

I’m a sucker for a nice desktop. Currently using NerdTool (thanks, drdrang and Brett Terpstra) and WallSaver.

UPDATE:

Forgot to detail, just for the sake of completeness, the items in the menubar. From left to right, we have:

PeepOpen: Great plugin for TextMate, MacVim, Emacs, Aquamacs, Coda or XCode for finding files. It’s really good. One of my favorite features is that it automatically orders by the date you late modified something; so if you look in a rails app for a file named index (of which you probably have dozens), the first one listed will be the last one you edited, which is often exactly the one I want.

Dropbox: Files, online. Wait, am I supposed to say “in the cloud” now? Anyway. The truth is out there.

Google Calendar/Mail Notifiers: If you want to be notified of that sort of thing.

Alfred: Instead of Spotlight. It has a few extra features (empty your trash bin with a few keystrokes), I find I like it a lot.

Evernote: I’m still not sold on this app, but I’m giving it a try.

Flux: You’ll just have to read the description on the site…

Littleipsum: Lorem ipsum text generator. I wish the UI was better, but it’s ok.

Caffeine: Keeps your mac awake, of course. Nice for presentations.

MenuMeters: So you don’t need to keep activity monitor open all the time. I mean, you will anyways, but you don’t need to.

The rest are just standard Mac OS stuff: Spaces, Displays, TimeMachine(not used, using Crashplan), Bluetooth, WiFi, Sound, Keyboard-switcher (for when I switch back to Dvorak… eventually), Battery, Date, Spotlight, phew.

Test First as a Productivity Hack

If you’re like me, and live primarily online, you have countless distractions in the form of blogs, news sites, photo sites, chat applications, twitter/facebook, web comics, etc. It’s really easy to sit down at your computer, and if you’re not focused, simply call up TIH or HN or bestofwikipedia and soon find the first hour of your day has been completely sucked up just looking at stuff.

Well, if you’re a contractor or freelancer, that may not be how you wanted to spend the first hour of your day. You just lost an hour of billable time; do that every day and you’re cheating yourself out of a lot of hours, especially if (guilty look) you’re prone to a similar web-surfing session after returning from lunch.

Something I’ve started doing, that I’ve found to be a great help in avoiding losing focus like this, is to sit down and make the my very first task to be writing a test for the app that I’m currently working on.

So, in this context, I’m not talking about “test first” in the TDD (Test Driven Development) sense, although I do practice that. I mean “test first” literally, as, when my seat hits the chair, and my fingers touch the keyboard, write a test.

This accomplishes a few different things.

  1. An immediate thing to focus on. Part of the reason I get distracted by the web is that I may not have an immediate action to start on. Having an immediate, easy to start, first action makes a world of difference.
  2. It gets me in the “work” mindset and flow. It doesn’t matter what the test is. It might be as simple as a throw-away “when I go to this page, I should see the correct page title” test – the point isn’t that this test needs to be written for the health of the application’s test suite; it’s simply a hack to get you into work-mode. Once I look at the code, add a test, make sure it runs, by then I’ve only spent about 2-4 minutes, but I’ve avoided getting side-tracked, and I’m already into the codebase, so it’s very natural to look at what needs to be done next, and start doing it.
  3. Hey, if I choose wisely, some of this uber-simple tests I’m adding as a first action may actually be useful. If a test is truly trivial, you probably just want to delete it; but chances are you can find something simple to test that actually does need to be true in the finished app; all the better. You have actually improved your test suite, while simultaneously avoiding that “morning distraction” time, and getting yourself into the flow of getting things done.

Your mileage may vary. But it’s been working for me. Adding something like the Pomodoro technique to this means that I’ll still get a chance to catch up on my blogs/etc during the day, but without the trap of spending my first hour surfing mindlessly.

What are your “get back to work” hacks?

Photo credit: flickr

UJS in Rails 3 Tonight at RUM

UJS in Rails 3, tonight at RUM

I’ll be speaking tonight on UJS and Rails 3 at RUM. Slides will be posted somewhere later this week, and I may follow up with a post that summarizes the content, if the slides don’t seem complete enough to cover the topic on their own.

How to Read Error Messages for Ruby on Rails

stack

Off and on (more off than on, recently) I lurk in the #rubyonrails IRC channel and try to be helpful. It’s sad to say, but it’s really common that people have questions – something isn’t working – and when they post the error message that they’re getting, their problem (and its implicit solution) is written in plain English in the first few lines of the error message. Not every time of course, but more often than not, error messages are trying to tell you something, and you should listen to what they’re telling you.

The greatest possible help you can give someone who is having this sort of problem is to advise them to read the error messages and try to figure it out. It sounds a little harsh, shades of the RTFM-type of “help” that some communities are known for. But in the long run, it’s going to help the new developer a lot more to be able to read an error message than to simply ask you to read it for them.

That said, here’s some things to look for in some fairly common Ruby and/or Rails error messages:

These are not the gems you’re looking for.

[gem] not found, not present, etc. : so, you think you have installed a gem or plugin. Make sure.

You can’t do that with this

no method error 'something' on SomeClass. You’re trying to call a method on an object which does not actually have that method. Yes, I know, you think it does, but if it did, then your code would be working. So what is happening is that either you have a different object than you expected to have, or some Module which provides the method has not been included, or (if it’s an ActiveRecord object) the column or association you’re assuming is there is not actually there. Also, you may have spelled the method wrong, or it may be that the method you think is there doesn’t actually exist.

A variation of this is when you have no method 'whatever' for NilClass… the issue here is that the object you’re looking for (almost certainly an ActiveRecord object) was not found; there was no record with a given id in the scope you were searching. So nil is what you were calling the method on, and NilClass has surprisingly few methods to call its own.

A slight variation, somewhat related to the above case, is the error that you’re attempting to call the id for 4 which is nil, or some such confusing warning. Well, again, your object is nil. The id of nil is 4. Open up irb and check: the id (object_id, really) for nil is 4.

Other things that can cause a nil error in a controller action and/or view are: not actually setting a value for an instance variable, forgetting to pass a local variable to a partial, and things of that nature.

I thought I would be writing more, but most errors I can think of off the top of my head fall into one of these categories: a gem/library/plugin isn’t present, a no-method-error, and/or having an object be nil when you really expected it to be something else.

Bottom line:

Read the error messages.

Read the error messages.

Read the error messages.

But the error message isn’t helping!

Yeah, sometimes you just don’t know quite what the issue is. Or you can see what it is, but you aren’t sure where exactly it’s coming from. That’s where the stack trace comes it.

Most error messages are accompanied by a stack trace which lists the method calls that led to the error that occurred. By going back down the stack trace, you can generally find the exact file, the exact method, and even the exact line number, which is ultimately responsible for causing the error.

In Rails, most often (maybe not every time, but most often) the error will be in a file that you wrote, not one of the rails files. When I need to look through a stack trace for an error, I generally just start scanning down the list, skipping over any/all files that belong to a Rails core gem or library until I get to something in app/, config/, or lib/; basically, until I get to something that I wrote. Then I look for the line number, and most likely there I’ll find the reason the error was called.

I still can’t figure out what’s wrong.

Sometimes you just have to think about it for awhile. The key is, you have found the reason the error is being raised. You might think that your code is correct, but if an error is being raised, something isn’t right. It may be an assumption you’re using that isn’t really true, a typo, a logical error, a syntax error, but somewhere, there is a problem. If you get to your line of code which is responsible for the error and you still don’t know why it’s failing, (and a google search for the error and/or situation reveals nothing new), that might be a good time to ask for advice on IRC. You can also just start asking yourself: okay, I know this line of code is causing an error. Why would it be failing? What assumptions is this line of code making which may have turned out not be true? Would any of those assumptions cause this error?

… and so forth.

But again:

  • Read the error message.

  • Read the stack trace.

  • Read your code.

  • Think about it.