Annals of Idiotic Bugs

Posted by amy on August 21, 2007

I’ve decided I like bugs. Not in production code, of course, but in development code, yay for bugs. Fixing my code teaches me far more than writing it did. Perhaps that’s because, with example code and snippets and tutorials all over the internet, much of the code that I write I don’t really write. I copy something I saw from something else and modify it to suit my own needs, or I look it up in a recipe book, or I see someone else’s solution on a mailing list… When you’re new to a language, you often use code that you don’t quite understand. I’m curious, so I try to understand it, but I don’t always have the time.

But then you get the bugs. And to fix the bug, you have to understand the code. (no, I know, that’s not really true. you can fiddle around with bug fixes just like regular code, and find someone else’s fix and implement it and it works and you’re in a hurry so you don’t care why it works, just that it does…)

Anyway, most normal developer bugs are stupid. I suppose there are great developers out there whose bugs are subtle and interesting. Most of mine, anyway, are quite stupid.

Here are some of my stupid bugs:

1) Implemented a slideshow. Stored stuff in the session to use during the slideshow. At end of slideshow, remembered to clear the slideshow stuff out of the session. Yay for me! Oops, child calling, check in code!

Max: “Um, Amy, how come playing a slideshow deletes all the photos in the slideshow?”

Me: “Dunno, maybe it has something to do with clearing the session? Why would deleting a thing from a session destroy it?”

Here’s what I had to clear the slideshow out of the session:
session[:slideshow].clear

Here’s what I should have had:
session[:slideshow] = nil

Oh, and here’s what was in the slideshow:
slideshow = domain_object.photos

So it’s not like my slideshow was just an array of any old ActiveRecord objects. It was a particular other ActiveRecord object’s personal array: i.e., an ActiveRecord AssociationCollection. AssociationCollections have a method called clear. Clear removes all the objects from the collection, like delete_all. Except delete_all doesn’t, according to the doc, destroy records, it just deletes them. And assoc_collect_.clear definitely destroys them, because not only did calling it kill the database records for my photos, it also killed the actual photos on the filesystem. So are clear and destroy_all the same thing? The doc is thin, so I went to the source:

# File vendor/rails/activerecord/lib/active_record/associations/association_collection.rb, line 67
67:       def clear
68:         return self if length.zero? # forces load_target if hasn't happened already
69:
70:         if @reflection.options[:dependent] && @reflection.options[:dependent] == :delete_all
71:           destroy_all
72:         else
73:           delete_all
74:         end
75:
76:         self
77:       end

So, domain_object.association_collection.clear does to the collection what domain_object.destroy would do, which is either to delete or destroy the associated records depending on what options you set in your call to has_many:

:dependent - if set to :destroy all the associated objects are destroyed alongside this object by calling their destroy method. If set to :delete_all all associated objects are deleted without calling their destroy method. If set to :nullify all associated objects’ foreign keys are set to NULL without calling their save callbacks.

I’d set :dependent => :destroy_all, so that’s what calling clear did. Oopsie!

Why did I use .clear anyway? Apparently I was thinking of my rake task rake db:sessions:clear.

In any case I didn’t want to be storing whole photo objects in my session anyway. I just wanted IDs: session[:slideshow] = domain_object.photos.collect { |p| p.id }

2) Wrote some boilerplate code for a create action. On failed validation, Rails complains there’s no create template to render. Why are you trying to render a create template, Rails, when I clearly ask you to render new upon failed validations:

render :action => new

Uh, because I told rails to render :action => nil. So rails defaulted to its standard "render the template whose name is the same as the action invoked" and went looking for a create template. What I needed to be saying was:

render :action => 'new'

Problem fixed. That one took me way too long to track down. I wish Ruby would complain to me about things that look like local variables but don’t seem to have any values attached to them. I’m not quite used to bug fixing a dynamic language, without the crutch of the compiler.

Right, what else?

3) If I want, temporarily, a list of all the photos in both a & b, where a & b are two different ActiveRecord objects, I should not do the following to get the list:

list = a.photos << b.photos

This moves all the photos in b over to a, permanently, in the database. This is one of the pitfalls of the syntactic sugar of methods that look like plain old operators. They’re not, really. ActiveRecord sometimes does more stuff than you want or expect it to, behind the scenes. Not a save or update in that code to warn me that I’m calling the db, but I am.

4) Oh, and the stupidest thing of all:


if  x = y
	#do stuff
else
	# do some other stuff
end

How come the ‘other stuff’ is never getting done!??
Duh, because X will always equal y if you assign it to y instead of checking to see if it == y!

Conclusion

This concludes the first edition of: Amy reports dumb things she did in Ruby code so you don’t have to do them too. C’mon, be original, and come up with your own bugs! I do not release my bugs under a Creative Commons license. I want royalties on all software including these particular bugs. I am going to implement DRM on these bugs. Don’t you dare steal my buggy code! It’s mine, mine, all mine!

Email is not a fat pipe. We forget this at our peril.

Posted by amy on June 11, 2007

The other day we put a prototype build of a web app up for our clients. Fire away at it, we said. Tell us what is wrong with it! We even had a section in the release notes called “How to be a QA person” (It’s a small project, we don’t have QA, hence our clients are, basically, QA).

Of course, we were pretty proud of our app. Of course, they came back with lists and lists of embarrassing bugs, complaints, features they didn’t understand, etc. The lists were not bracketed by nice polite “we love the app, we’ve just got a couple of teensy little issues with it.” Although the emails they came back in were entitled “Bugs 1, 2, 3, etc.” not everything in the lists were actually bugs. Developers are very defensive about “bugs”. The typical user considers anything that doesn’t work the way he wants/expects it to to be a bug. The typical developer considers bugs to be only things where they, personally, wrote code whose logic was not correct.

So Max comes in to me and says “wow, what do we do about the clients and all their bug reports?!” Our immediate emotional reaction to their emails was that they were irrationally pissed at us and didn’t understand anything about how software development works and were a pain in the ass and were probably going to fire us for giving them such buggy code.

But then we were able to sit back, take a deep breath, and reason together:

First, our clients did exactly what we asked them to do, and they did it well. They gave us fantastic feedback on the app that would help us focus on the most valuable changes we could make for them. When clients don’t give feedback, you’re screwed, because you don’t know how to make them happy. Our first emotional response is normal, and we’ll probably never get rid of it. In fact, we probably shouldn’t get rid of it, because the emotional response is there to kick our asses.

However. (And this is my completely based-on-popular-science-books-by-eminent-yet-readable-professors-theory). When dealing with other humans, we humans expect a fat pipe. We evolved for person-to-person, face-to-face contact. Our faces, bodies, and voices are unbelievably expressive. Compared to the fat pipe of person-to-person contact, words in email are a trickle of information. So when we get an email that elicits any kind of emotional response at all, our brains start flailing around wildly, looking for all the meta-information we expect to come along with it: the expression on the face, the tone of voice, the direction they were looking, and so on. And when we can’t find it, we make stuff up. In no time, we’ve filled up the fat pipe and satisfied our “what do we do now?” programming with _entirely false, absolutely imaginary information_. Those of us who are naturally pessimistic generate false, depressing information. This makes our initial, proper and correct ass-kicking emotional response escalate into a neurotic, counterproductive, self-perpetuating emotional response. Not good for business.

One moral of this story is that developers should not take user complaints personally. This is obvious but easily forgotten. The other moral of this story is the one I’m really interested in: when our brains don’t have the information they expect to have, they just make it up. And it’s really easy not to notice when it happens, because the made up information looks just like all the actual-based-on-fact information our brains have collected. Hence the unreliability of witnesses.

The lesson, then, is that, for stuff that matters, _don’t assume your brain has the facts right_. Take the time to sort out what you can possibly have evidence for from what you just made up because you _didn’t_ have any evidence.