Thursday, April 23

Contractor Math for Dumbheads (and Hiring Managers)

dumb-dog-crossing.jpg 368×336 pixels
Uploaded with plasq's Skitch!
I've done my fair share of hiring and firing as a Chief This and Vice President of That over the last fifteen years. Since I've taken the dive into independent consulting recently - putting me on the other side of the interview desk - I've discovered that there's a lot of hiring managers out there that just don't seem to understand the simple math of contracting.

In short, the ever-repeating story goes something like this, "Thank you for your time, but we've decided that your rates are higher than what we're looking for, so we've decided to go with another candidate," followed two or three weeks later with, "Hello, do you have time to speak again? The other candidate didn't quite work out."

So what's the math? It's quite simple. Take our fictional contractors, Dudley, the hobbyist contractor, dabbling in many technologies, master of none, and Clark, the seasoned veteran of the dot-com bubble who spent the last decade specializing in the particular technology you're using. Dudley, for some inexplicable reason that doesn't seem to concern the hiring manager, has had several jobs over the last nine months, but he only charges $50 an hour. Clark, whose resume lists all of three companies over the last decade, including publications in trade journals, charges a whopping $90 an hour, nearly twice Dudley's rate.

Of course the logical choice is cheap Dudley, right? Bzzt. Project X is pitched to both candidates and they both come back with a one-week estimate. For the sake of our hypothetical scenario, both candidates tackle the project independently. Dudley was a little off on his estimate, and it actually took him two weeks. Cut him some slack; he just started reading the O'Reilly book before the first interview, and he had to start over once because he lost all his work to a hard-drive crash. What's version control? Clark, of course, delivers on time.

This brings us to the first part of the math. Dudley took 80 hours at $50 an hour for a cost of $4000. Clark took 40 hours at $90 an hour for a cost of $3600. Ooh boy look at those savings, right? Yeah, Clark's value is evident in the invoice cost, but the real value is even deeper.

Dudley's inexperience has lead him to write some brittle code. How brittle? We don't really know because he was pressed for time and didn't bother writing tests. Ah, and don't lose any sleep over the fact that the his version works fine on his laptop but not on your servers. Something must be wrong with your hosting provider. If you extend his contract for another couple weeks, he'll figure it all out for you.

Clark, on the other hand, has provided a system with a comprehensive suite of tests, and an automated deployment script. How quaint. As an added bonus, he's also developed a clean object model which will make future enhancements and integration relatively painless; it's the gift that keeps on giving.

In the bigger picture, by going with Dudley you've paid more money for a far worse system. You'll be paying the technical debt for a long time to come. Don't make your hiring decision based solely on rates. Look at the candidate's job history. Can they hold a job? Do they have repeat/long-term clients? Look at their technology portfolio. Are they "experts" in what you need, or do they dabble in whatever happens to be shiny right now? And don't be afraid to pick up the phone and call references. Former employers will rarely say anything bad about a hire, for legal cover-your-ass reasons, but if they liked the candidate, they'll usually praise them and their work.

Monday, April 20

Put a progress meter in your long-running migrations

SLOW
Uploaded with plasq's Skitch!
I'm working on a Rails project now that requires lots of database massaging and repair. This repair work needs to be tried and tested on a development workstation, reviewed on a staging server, then applied to the production system. Since the work needs to be repeatedly applied to several environments, I'm logically using migrations.

One nuisance I got sick of real quick is staring at a terminal running a long migration and wondering "is it doing anything?" and "how much longer is it going to take?" So I decided to add some progress indicators.

The simple yet effective method I've settled on is an on-screen count-down. Most of the migrations consist of the same pattern: 1) get a list of the records that need repaired, then 2) iterate over each record and repair it. I set the counter to the size of the record set I'll be iterating over, decrement it on each iteration, and print it to the screen. Seeing the numbers scroll across the screen lets me know the migration is working, and since the migrations count down to zero, I can gauge how long it's going to take to complete based on how fast the numbers are shrinking.

Here's an example:

class FixTheThingsWithTheStuff < ActiveRecord::Migration
def self.up
query = <<-SQL
select name
from things
where stuff = 1972
and deleted_at is null
group by name
having count(id) > 1
order by name
SQL
broken_rows = select_all(query)
count = broken_rows.size
broken_rows.each do |row|
printf "[#{count-=1}]"
# ... fix it! ...
end
end

def self.down
# ... re-break it! ...
end
end

Saturday, April 11

Recording Skype Calls on OS X with Audacity (for free)


UPDATE: I've posted a follow-up with a slightly better configuration.


Over the last decade, I've had many entertaining and enlightening "debates" with an old colleague of mine, so I asked if he'd be willing to record a few over Skype and see if they might be worthy of releasing as podcasts. Since it was my bright idea, he left it to me to figure out how to pull it off. I figured it couldn't be too hard since so many other people are doing it - boy was I wrong.

I wasted a few good hours today trying to figure out how to record Skype calls on my Macbook Pro using free tools, piecing together fragments of vague and out-dated blog posts and pleas for help on various support forums. I finally got it all working and thought it would be a good idea to put it all down on [virtual] paper for posterity in case I ever need to do it again, and by putting it here others might save themselves the trouble I went through.

All you need is to download and configure four pieces of free software:

1. Soundflower doesn't require any configuration - just a reboot after install.

Soundflower
Uploaded with plasq's Skitch!


2. LineIn (scroll down the page and look for the microphone icon). Set the input to the microphone (I'm using the built-in) and the output to Soundflower (2ch), then press the "pass thru" button to activate it.

LineIn
Uploaded with plasq's Skitch!


3. Skype. Set the input and output, both, to Soundflower (2ch). I worried this would cause feedback, or an echo of my voice, but apparently I don't know the first thing about audio.

Skype
Uploaded with plasq's Skitch!


4. Audacity. Leave the playback device set to built-in output and change the recording device to Soundflower (2ch). Also don't forget to check the "playthrough" box or you wont be able to hear what you're recording, which includes the Skype call.

Audacity
Uploaded with plasq's Skitch!


5. Leave your system settings alone. They should look like this:

System Output
Uploaded with plasq's Skitch!


System Input
Uploaded with plasq's Skitch!


6. Back in Audacity, hit the record button, then switch over to Skype and start the call. Now you're in business.

Friday, April 10

Book Review: Never Eat Alone

Book Cover
I think I can pretty much sum up the entire book in one sentence: Be a networking whore, but be sincere, and pay it forward.

Those are the three main points author Keith Ferrazzi drills into your skull, but he uses a few more words, and couple hundred more pages. Everybody in your life is an important networking contact, from your garbage man to your employer's president. Never be a phony; always be yourself. Don't schmooze only the people you think are important or powerful and likely to advance your career. Don't piss on your underlings or step on your peers to climb higher up the ladder. And last but not least, always come to the party offering to give something first, don't start out by asking for something, and don't expect reciprocation.

Ferrazzi is a fine writer, and he peppers his words of wisdom with entertaining self-deprecating stories of learning from his mistakes. The book makes it sound like the only thing he does all day is make phone calls, send e-mails, and throw dinner parties, non-stop. That sounds like a career in itself to me, and in his case (books, speaking, consulting, etc.) I believe it is. That doesn't leave much free time to focus on your main career, for those of us who have jobs other than full-time networking. But, it's a good eye-opener to the means and method of those who are well-connected. I'm sure I'll cherry pick a few of his techniques (that I'm not already doing) and I'll be a better person for it. My only real gripe with the book is the same with most books: too many words. He could have got his message across in a fraction of the verbiage.

Friday, April 3

Disabling third-party services when they stop performing (in Rails)

Chain
Uploaded with plasq's Skitch!
One of my clients uses the hosted version of CompanyX (not their real name) to serve ads on their site. A couple weeks back, CompanyX applied some "upgrades" and things didn't go as planned, so for nearly a week their service was up and down like a yo-yo. That resulted in me getting calls along the lines of, "Hey our site is loading slow because of the CompanyX ads, please take them all off," followed a few hours later with another call, "Hey CompanyX seems to be OK now please turn their ads back on," and a little while later the cycle repeats itself. That got real old, real quick.

So, I decided to whip up a little automated solution. I needed two core components:

1. A way to programatically turn the ads on and off.

2. A way to periodically test the third-party service, and enable or disable the ads based on its response time.

For disabling and enabling the serving of ads, I created a new model called CompanyXStatus which is essentially a toggle switch, it's either on or off, and for auditing purposes I have it store the date and time whenever it's flipped. The database table looks like this:
create_table "companyx_statuses" do |t|
t.column "enabled", :boolean
t.column "created_at", :datetime
end

And the app-facing API of the model looks like this:
class CompanyxStatus < ActiveRecord::Base

class << self

def enabled?
latest.enabled
end

def disabled?
!enabled?
end

def disable!
CompanyxStatus.create!(:enabled => false) if enabled?
end

def enable!
CompanyxStatus.create!(:enabled => true) if disabled?
end

private

# I'm not using a named scope because this client is on an OLD version of Rails...
def latest
CompanyxStatus.find(:first, :order => 'created_at desc') || CompanyxStatus.new(:enabled => true)
end

end

end

So in the views when I'm building a page I just have to check if CompanyxStatus.enabled? before rendering an ad tags.

Now, for the actual toggling logic, I'm using the Benchmark module to call out to the service and measure the response time. If if exceeds the threshold (2.5 seconds in this case) the service is disabled, otherwise enabled. Here's the rest of the model:
  def test
if 2.5 > latency
CompanyxStatus.enable
else
CompanyxStatus.disable
end
end

private

def latency
Benchmark::measure{ connect }.real
end

def connect
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 80, 'blah.companyx.org' )
socket.connect( sockaddr )
socket.write( "GET /blah.php HTTP/1.0\r\n\r\n" )
results = socket.read
end

Finally I need a way for the system to periodically make these checks and toggles so my client and I don't have to worry about babysitting the site. For this I wrote a simple rake task:
namespace :companyx do
desc "Ping CompanyX and disable it if too slow"
task :ping => :environment do
CompanyxStatus.test
end
end

And scheduled it as a cron job to run every minute:
* * * * * cd /home/client/apps/production/site/current && RAILS_ENV=production rake companyx:ping

That's it! Now I can get a good night's sleep knowing that the next time CompanyX has a burp in their service, my client's site is going to automatically shut them off until they get their act back together again.