Saturday, July 21, 2007

"Programmers are expensive, CPUs are cheap" does not justify rapid development of slow code

If you've seen at least one argument between a Ruby on Rails Fanboy and an Enterprise Java Fundamentalist - you've heard something like this:
"RoR is 100 times slower than Java."
"Developers are expensive and CPU cycles are cheap. It's more important to save developer time than CPU time!"
"You aren't a real developer!!"
"At least I don't use tables!!!"


I am troubled by how pervasive this "I'm expensive, CPUs are cheap, I don't need to concern myself with performance" attitude has become. Comparing the relative costs of processing cycles vs. a developer is just silly.

Everyone knows, and CPUs are cheap. This is obviously true. Developers like to point this out right before they tell you interpreted languages are better, or OR Mapping is better, or loose typing is better. Like stating the obvious will somehow qualify their point of view. Could anyone who likes to spout this please stop! You might as well say "Puppies are cute, roaches are ugly. Python is better than C++" or "Rocks are harder than pillows, pointers are evil".

First of all, it's not the development cost vs. operational costs. You have to consider how often the product is going to be operated. Think of it this way - "Automobile engineers cost 100k a year. Gas costs $3 a gallon. Engineers are way more expensive than gas. Who cares that it only gets 5MPG, or engineers were able to design the car twice as fast." If your product is successful, there should be many more man-hours of operation than there were development.

Secondly, CPU cycles are inconsequential when measuring the "cost" of slower code. How about the value of our customers' time waiting on all those extra CPU cycles? Think of it this way - "Engineers are expensive, electricity is cheap. It doesn't matter that our microwaves take twice as long to pop popcorn, we saved a lot of money developing them." People don't care about CPU cycles, but they do care deeply about their time. Imaging trying to use the argument on the other end: you are demoing your latest project to the big-wigs for the first time:

"And there you have it, what do you think."
"It feels slow."
"Don't worry, CPU cycles are cheap."


Please note: I am a big fan of interpreted languages and loose typing. These technologies are great and there are many situations where they are the most appropriate tools.

But, please, stop with the "Developers are expensive, cycles are cheap" bullshit. That's about as insightful as pointing out bananas are yellow and lettuce is green. The trade-off for slower code isn't just so-cheap-they're-free CPU cycles, it's your customers' TIME, and that is very expensive.

Sunday, July 15, 2007

Javascript image anchor highlighting

When using an image as an anchor, you very often want to make the image "highlight" when you hover over it. We've all seen this before - create a highlighted version of the image, add some javascript, and you get the effect. Here is an example:



Here is the markup:


<img src="http://brewer.designvigilante.com/files/imageHover/moveDown.png"
onmouseover="this.setAttribute('src','.../moveDown_over.png');"
onmouseout="this.setAttribute('src','.../moveDown.png');"
/>


This works for one image, but quickly become bothersome as you use more images. So, how to pull this out into a script that does it automatically for us? First thought - walk the DOM and get all the images, then add the event handlers to each one. This will work, but it is not optimal:

  1. Walking the DOM can be expensive.
  2. If you have a lot of images, you're adding a lot of event handlers. Each one takes up memory, and is also a potential memory leak if you delete the element later on without deleting the handlers.
  3. If you add new images after the page loads, you'll have to remember to add the handlers to each.


We can avoid all of these problems by using . Event delegation allows us to attach one event handler to the document body. When an event is fired on a node, the event will bubble up DOM tree unless is is canceled. The event object contains the original target element, so we are able to write one function to handle image highlighting for the entire page. Here is what the event handler code should look like:

var handleMouseOver = function(e)
{
e = e || window.event; // handle IE
var el = e.target || e.srcElement; // e.target points to the element that was moused over. srcElement is for IE
// If the element is an image, and it's parent is an anchor
if( el.tagName.toLowerCase() == "img" && el.parentNode.tagName.toLowerCase() == "a" )
{
// add _over to the name of the image.
var src = el.getAttribute( "src" );
var dot = src.indexOf('.');
el.setAttribute( "src", src.substring( 0, dot ) + "_over" + src.substring( dot ) );
}
};

// Mouse out is the same, except removing '_over' from the image
var handleMouseOut = function( e ){
e = e || window.event;
var el = e.target || e.srcElement;
if( el.tagName.toLowerCase() == "img" && el.parentNode.tagName.toLowerCase() == "a" )
{
var src = el.getAttribute( "src" );
el.setAttribute( "src", src.replace( "_over", "" ) );
}
};


Note that you need to upload a highlighted version of the image for this to work.

So, how to bind these to the document body? We need o be concerned with handler collision - that is multiple handlers binding to the same event and overwriting each other. We will explore this more in a upcoming post, for now we'll just ignore this case.


var ImageAnchorHoverHighlighter=function(){
var handleMouseOver = function(e)
{
e = e || window.event;
var el = e.target || e.srcElement;
if( el.tagName.toLowerCase() == "img" && el.parentNode.tagName.toLowerCase() == "a" )
{
var src = el.getAttribute( "src" );
var dot = src.indexOf('.');
el.setAttribute( "src", src.substring( 0, dot ) + "_over" + src.substring( dot ) );
}
};

var handleMouseOut = function( e ){
e = e || window.event;
var el = e.target || e.srcElement;
if( el.tagName.toLowerCase() == "img" && el.parentNode.tagName.toLowerCase() == "a" )
{
var src = el.getAttribute( "src" );
el.setAttribute( "src", src.replace( "_over", "" ) );
}
};

return {
init : function(){
var b = document.body;
document.onmouseover = handleMouseOver;
document.onmouseout = handleMouseOut;
}
};
}();




Later on, I'll look at solving event collision, handling missing highlight image, labels, and background images.

Thursday, July 12, 2007

When comments suck

Here are some big dumb comments I stumbled upon today:

con = getConnection(DB_POOL); //get the database connection
stmt = con.prepareStatement( query ); //prepare the statement
rs = stmt.executeQuery(); //execute query


This code is Department of Redundancy Department Approved /*approved by the department of redundancy department*/

I love that the variable names are so short and unclear.

Tuesday, July 10, 2007

Steve Jobs gives the iPhinger to Mac developers

Apple is waving a white flag. They are in the same API wars as Microsoft - it's the OS vs. the web, and Apple is changing sides. You see, Apple has "very sweet solution" for development on the iPhone - and it doesn't include core animations.

If you go cuckoo for Cocoa, faithfully attend the WWDC, and live by the covenants of the HIG, you would probably expect to have a leg up when it comes to "the next big thing" from Apple. Well, sorry, you lose - web apps will be the only 3rd party apps allowed on the iPhone. While you were standing in line outside an Apple store, web developers were able to build and launch the first 3rd party iPhone apps. Steve Jobs has repaid you for years of loyalty with a big, revolutionary, game-changing iPhinger.

It's a web app world.

Saturday, July 7, 2007

People Hate Disappointment

I hate being disappointed. I love Jimmy John's Sandwiches. The bread is great, the meat is great, the cheese is great, it's about two miles from the office - so it gets a lot of my lunch business. They advertise their delivery service and its no-minimum-for-delivery a lot. My boss and I have tried it out a couple of times and were pleased with the results - a quality sandwich delivered fast.

Yesterday, we wanted Jimmy John's and didn't have time to drive there. So, we called them up, and were told we were outside their delivery area. This is not what we expected. "Did you change your delivery area, because you have delivered here before?" "Well, generally it is, but we are busy today and don't want to drive over there for 2 sandwiches." Disappointing! I hate being disappointed. We ate at Quiznos instead. The bread wasn't as good, the meat wasn't as good, the cheese wasn't as good, and they don't deliver - but I was not disappointed. I got exactly what I was expecting - no disappointment.

Jimmy John's set my expectations higher than they could consistently deliver. As a result, I am disappointed and will be heading to the Quizno's more often. Quiznos understands what Jimmy John's does not:

People hate disappointment.

As a software professional, you need to realize this. If you want to be a successful developer, don't disappoint your boss. You boss has certain expectations of you: meet or exceed these expectations and you will please, otherwise you will disappoint. And people hate disappointment.