The Devver Blog

A Boulder startup improving the way developers work.

Archive for March 2009

Lessons learned from our hiring process

We’re very happy to announce that we’ve had a great developer accept our offer to join our team. We’ll have more details on our newest team member soon.

This was our first attempt at hiring and we certainly learned a lot. While our process has improved since we started, it’s still a work in progress.

The first lesson we learned is that hiring can take a long, long time – in fact, much longer than we expected. I looked back through my emails and we officially started looking for someone more than four months ago. One thing we have heard time and time again was the importance of hiring only the best people – and, just as importantly, people that fit well within your company culture.

As a result, we were extremely picky when it came to candidates. As the process goes on and on, it’s easy to get frustrated and want to lower your bar. Avoid this temptation! If at all possible, go the other route: raise your compensation, improve your pitch, and raise your profile so you can attract candidates that meet or exceed your bar.

When we started, we severely underestimated how long it would take and we overestimated how many applications we’d get. Because of these bad assumptions, we made our first mistake – we chose to slowly “roll out” the announcement that we were hiring. First, we just told friends and mentors. A week or so later, we tweeted and put up a blog post. Later on, we posted on some news sites and newsgroups. And after that, we finally created a job posting at Startuply. Waiting between each step was a waste of time – we should have just broadcast as loudly as we could the first day.

We ended up getting a good number of applications, the vast majority of which were from great programmers. We assumed that going through them would waste a bunch of time. We were wrong. In reality, it was really easy to turn down many applications just from their resume (usually the applicant was good, but didn’t have the skill set we were looking for). Phone calls were a bit more time-consuming, but were never a waste of time. We always enjoyed talking to candidates, always found a way to improve our process, and usually learned something new about the Ruby ecosystem.

One thing that we improved on was learning to say “no” quickly. Even with the manageable number of candidates we talked to, it was important say “no” as soon as we discovered something that wouldn’t fit. Early on, we were a bit hesitant to do so and ended up continuing our process for too long. That wasn’t fair to either ourselves or the candidates. It became easier to shut things down quickly once we had interviewed a few people and started to gain confidence that new applications would show up, even if the queue was currently empty.

We also learned that it’s important to have a fairly short process. Of course, the biggest priority is to have a process that lets you learn enough about a candidate to be confident in your decision. That said, as you improve, you’ll find that you can shorten your process while maintaining your confidence. Shortening the process is better for you (less time spent) and better for your candidates.

The initial version of our process went something like this:

  1. Check resume, blog, Github account
  2. Introductory phone call to learn about candidate and convince them Devver is cool
  3. Second phone call to ask some high-level technical questions
  4. Ask for and call references
  5. Remotely pair on a project for a few hours with the candidate
  6. Fly candidate out to Boulder to meet and discuss technical and business issues

The final version was a bit more compressed:

  1. Check resume, blog, Github account
  2. Phone call to learn about them, introduce ourselves, and cover high-level technical questions
  3. Ask for and call references
  4. (In parallel with #3) Ask candidate to write small Ruby application and review their code
  5. Fly candidate out to do some pair programming and discuss technical and business issues

All in all, it was a good (if sometimes painful) learning experience. We deeply appreciate the time and effort spent by every single applicant (and their patience with us as we learned by trial and error).

If you’ve got your own lessons you’ve learned while hiring, please let us know. We know we’ve still got a lot to learn…

Written by Ben

March 27, 2009 at 12:41 pm

Posted in Boulder, Devver

Managing Amazon EC2 with your iPhone

I wanted a quick way when out and about to easily manage our AWS EC2 instances while out and about. It hasn’t happened often, but occasionally I am away from the computer and I need to reboot the instances. Perhaps I remember our developer cluster isn’t being used and want to shut it down to save some money.

I didn’t find anything simple and free with a quick Google search, so in a about an hour I wrote a nice little Sinatra app that will let me view our instances, shutdown, or reboot any specific instance or all of them. The tiny framework actually turned out to be even more useful as I now have options that let us tail error logs, reboot Apache, reboot mongrel clusters, or execute any common system administration task.

I won’t be going into detail on how to build a iPhone webapp using Sinatra and iUI, because Ben already created an excellent post detailing all of those steps. In fact I used his old project as the template when I created this project. I can’t begin to explain how amazingly simple it is to build an iPhone webapp using Sinatra, so if you have been thinking of a quick project I highly recommend it.

Here are some screen shots showing the final app. (screenshot courtesy of iPhoney):

ec2 manager home view

ec2 manager home view.

ec2 manager describe view

ec2 manager describe instances view.

ec2 manager instance view.

ec2 manager instance view.

This app uses the Amazon EC2 API Tools to do all the heavy lifting. So this app assumes that you already have the tools installed and working on the machine you want this app to run on. This normally involves installing the tools and setting up some environment variables like EC2_HOME, so make sure you can run ec2-describe-instances from the machine. After that you should just have to change EC2_HOME in the Sinatra app to match the path where you installed the EC2 tools.

Let me know if you have any issues, it is quick and dirty, but I have already found it useful.

To run the app:
cmd> ruby -rubygems ./ec2_manager.rb

require 'sinatra'

EC2_HOME = '~/.ec2'

use Rack::Auth::Basic do |username, password|
  [username, password] == ['some_user', 'some_pass']
end

get "/" do
  @links = %w{describe_ec2s restart_all_ec2s shutdown_all_ec2s}.map { |cmd|
    cmd_link(cmd)
  }.join
  erb :index
end

get "/describe_ec2s" do
  results = `cd #{EC2_HOME}; ec2-describe-instances`
  instances = results.scan(/INSTANCE\ti-\w*/).each{|i| i.sub!("INSTANCE\t",'')}
  @links = instances.map { |i|
    instance_link(i)
  }.join
  erb :index
end

get "/restart_all_ec2s" do
  @results = `cd #{EC2_HOME}; ec2-describe-instances`
  instances = @results.scan(/INSTANCE\ti-\w*/).each{|i| i.sub!("INSTANCE\t",'')}
  cmd="cd #{EC2_HOME}; ec2-reboot-instances #{instances.join(' ')}"
  @results = `cmd`
  erb :index
end

get "/shutdown_all_ec2s" do
  @results = `cd #{EC2_HOME}; ec2-describe-instances`
  instances = @results.scan(/INSTANCE\ti-\w*/).each{|i| i.sub!("INSTANCE\t",'')}
  cmd="cd #{EC2_HOME}; ec2-terminate-instances #{instances.join(' ')}"
  @results = `cmd`
  erb :index
end

get "/instance/:id" do
  id = params[:id] if params[:id]
  verify_id(id)
  @results = `cd #{EC2_HOME}; ec2-describe-instances #{id}`
  @links = "<li><a href='/shutdown/#{id}' target='_self'>shutdown #{id}</a></li>"
  @links += " <li><a href='/reboot/#{id}' target='_self'>reboot #{id}</a></li>"
  erb :index
end

get "/reboot/:id" do
  id = params[:id] if params[:id]
  verify_id(id)
  @results = `cd #{EC2_HOME}; ec2-reboot-instances #{id}`
  erb :index
end

get "/shutdown/:id" do
  id = params[:id] if params[:id]
  verify_id(id)
  @results = `cd #{EC2_HOME}; ec2-terminate-instances #{id}`
  erb :index
end

helpers do

  def cmd_link(cmd)
    "<li><a href='#{cmd}' target='_self'>#{cmd}</a></li>"
  end

  def instance_link(instance)
    "<li><a href='/instance/#{instance}' target='_self'>#{instance}</a></li>"
  end

  def verify_id(id)
    raise Sinatra::ServerError, 'bad-id, What you doin?' unless id.match(/i-\w*/)
  end

end

use_in_file_templates!

__END__

@@ index



@import "/stylesheets/iui.css";




<div class="toolbar">
<h1 id="pageTitle"></h1>
</div>


<ul id="home">
<li><a href='/' target='_self'>home</a></li>


</ul>





<li><strong>results</strong></li>

<ul id="home">
<li><a href='/' target='_self'>home</a></li>

&lt;%= @results.gsub(&quot;\n&quot;,&quot;<br />") %&gt;
</ul>




view this gist

Written by DanM

March 5, 2009 at 10:03 am