Head Hunter How “Not To” Guide

Technology is dominated by two types of people: those who understand what they do not manage, and those who manage what they do not understand.

As a “Senior” software developer I get a lot of job offering emails from head hunters, most of which get deleted. A very few of the emails only make it to a second response before deletion. The problem? It’s not the “email” like you may have been thinking, it’s that head hunters don’t understand developers or how to approach us.  The end result being the content of the job offering email is usually horrible.

Here are some generalized, but often dead-on assessments of most software developers, myself included:

As a software developer, I don’t like people, especially not strangers.  I only have a very small circle of close friends, and I don’t really want to make any new friends.  I want to be left alone to write great software.

The problem occurs when you pretend to act like you’re my friend who is here to “help me” find a new job.  Why not just be truthful and act like you’re here to make money from having me hired by your client, it’s OK, really.  The truth is, any software developer is fully capable of hunting down a new job when needed.  The market is currently in our favor, there are more development jobs than there are developers, and we know that.  Most developers you contact will already be employed.  You’re not here to help them find a job, you’re here to lure them away into a new job so you can make your placement commission.  Cat’s out of the bag guys, sorry.

I hate talking on the phone.  See introvert.  The worst thing you could ever ask me to do is get on the phone and gab.  I’m sorry you guys all feel the need to talk on the phone about the position before giving away any of the juicy details, but you know as well as anyone the important information can be sent via email.  Just send a simple email and include your phone number.  If the job is relevant and there is interest on my end, I will call you.

I don’t hate talking on the phone as much as I hate being unemployed.

As a software developer, I only want the precise information required to solve the problem and nothing more.  When you send an email offering a job you are confronting me with a problem to solve.  The problem is, do I delete the job email or do I pursue the job?  Any details you give about yourself, your big fancy “senior recruiter” job title, your head hunter company, none of that, is important.

Just once I’d like to get a job email from a “junior recruiter”.  Do they even exist?

So knowing these facts, when contacting a software developer about a job, here are the ONLY things to put in the email:

Who is the client you are working for?  If you can’t give that info, then at least say what part of the town/state/country/world the client is in.

Is this job telecommute?  A lot of developers work from home these days, and this is important information one way or the other.  If it’s not telecommute then is there even a possibility of working from home “sometimes”?

What specific technology would I be expected to know for the job?  Translation:  What databases and programming languages are in use?  It does not matter if your client is calling the position “front end” or “back end” or whatever.  What matters is what technology is being used there right now today.  I’ve never once had a job where I stayed within the original job description.  Eventually a software developer is asked to work on more than just things from the original job description, so we want to know more than just some fancy buzz words like “front end developer”.

What is the salary range?  Yes, this is the last, least important question, but very important all the same.  I need to know if this job would be a step up for me or not.  And do not tell me “salary is based on experience” either, I’m well aware your client gave you a salary limit for the position.  We already know I have the skills and “experience” for the job, otherwise why are you contacting me?

Now, specifically, a developer does not want to see the following things in your email:

“What is you current salary?”  Like that’s ANY of your business!  How about you tell me your commission rate if I take the job?  Yeah, didn’t think so!  On the topic of salary, you need to tell me what the (top end) range is for the position, and that’s it.  I fully understand I may not be offered the top salary, but if the top is lower than my current, I’m wasting my time talking to you.  It’s that simple.

“Can you please call me?”  No, at least not until all the other required items above are done, absolutely not.  I will not get on the phone and waste time with anyone until I know exactly what the position is all about.

“Hey, I have an awesome Ruby on Rails job, can you call me?”  Nope.

I do not care about your head hunter company profile.  I only care about the client’s company profile.  So many times I catch myself wasting time reading junk at the top of a job offering email.. junk the head hunter includes about their own company.  I DON’T CARE about you or your head hunter company.  I only want to know about your client, the guy who will ultimately be feeding me and my family, if I take the job.

Be Sociable, Share!

Stack Trace Driven Development

Motivation

If your software project doesn’t “get users” it will die, bottom line. Launching a software project into production sooner rather than later greatly increases the odds the project will “get users” and ultimately succeed.

Situation

You’ve seen it time and again in recent years. You start a new project. You have project specs and a strong desire to include a test suite with your project to prove correctness and efficiency. But along the way your project specs change, you don’t launch on time, and you have a gazillion tests to update. Your hopes of actually “getting users” dwiddles daily as you cope with all the code and test code changes. What do you do?

Solution

Throw out your test suite and launch the project into production.

Having users actually use your software provides near 100% code coverage. Having your “test suite” run in production on real data means you do not waste time creating fake data in the form of fixtures or factories. Continuous integration is provided as a side effect of server uptime. You can confirm your test suite is being executed by examining server load.

Implementation

STDD is super easy to setup. On Android there is ACRA.. force closes stop, stack trace emails start, simply beautiful. In Django it’s DEBUG=True in settings.py.. user gets an oops sorry page, you get a stack trace email.

Origin

I got the idea for STDD from staring at an unread copy of “Ship It” I had purchased some time ago. I don’t know what’s in the book because like I mentioned, I’ve never bothered to read it. I’m much too busy rolling out new versions of my software projects to read a book! But the title made me think, what IS the fastest way to deliver software while still maintaining some sanity with managing coding errors? I realized the actual code could easily be considered a test suite, and since I have the test results: either it crashed or it didn’t, I followed through with stack trace email implementation to close the loop.

Probability Of Success

I created these probabilities based on my experience. I’ve never worked on a large software project with a hand-coded test suite that actually launched into production. Meanwhile most every other project I’ve worked on, without a test suite, has launched.

P(L|T) = 0.01
P(L|¬T) = 0.99

The “probability” of an actual project “launch” seems greatly dependent on the amount of academic masturbation contributed to the project in the form of a “test suite”.

If you have a project where specs change constantly, you should try STDD. It will greatly increase the probability your project will launch, “get users”, and succeed.

Be Sociable, Share!

PostgreSQL Sequence Updates

I had a problem with PostgreSQL pgdump recently.  My setval() calls were all set to ’1′.  I whipped up this quick script to fix things:

#!/usr/bin/env python

DB_NAME = "my_db"

from subprocess import Popen, PIPE
import re

exclude = [ 'tablename', 'rows' ]
tp = re.compile( '[^a-z_]' )
ts = Popen( [ "/usr/bin/psql", DB_NAME, "-c SELECT tablename FROM pg_tables WHERE tablename NOT LIKE 'pg_%' AND tablename NOT LIKE 'sql_%' ORDER BY tablename" ], stdout=PIPE ).communicate()[ 0 ].split( ' ' )

tables = []
for t in ts:
    t = tp.sub( '', t )
    if len( t ) == 0 or t in exclude:
        continue
    tables.append( t )

for t in tables:
    sql = "SELECT pg_catalog.setval( pg_get_serial_sequence( '%s', 'id' ), ( SELECT MAX( id ) FROM %s ) + 1 );" % ( t, t )
    print Popen( [ "/usr/bin/psql", DB_NAME, "-c %s" % sql ], stdout=PIPE ).communicate()[ 0 ]
view raw file1.py This Gist is brought to you using Simple Gist Embed.
Be Sociable, Share!

How-to build latest Linux kernel from Linus’ git repo on Debian

Here’s a how-to for building a recent 2.6 Linux kernel on your Debian GNU/Linux box:

You will need to do all this as root. It’s serious business building new Linux kernels :)

su -

The dash after the su command makes it behave as if you had logged in as root directly, a full login environment is applied.

Make sure you have the required tools and libraries installed:

apt-get install build-essential module-init-tools initramfs-tools \
procps libncurses5-dev kernel-package fakeroot git-core screen \
zlib1g-dev

Use git to clone Linus’ latest git repo:

cd /usr/src

git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git linux-2.6

cd linux-2.6

This will take a long time.

Once you have the source you’re ready for configuration. I base my new kernel configuration on a known working configuration, then trim it down from there. Check to see what configurations you have in /boot:

ls /boot/config*

Configure your new kernel source using your chosen config file:

make menuconfig

Select “Load an Alternate Configuration File”, enter your config file path, for example I used /boot/config-2.6.26-2-686. Hit exit and save.

Build the kernel and package it:

make-kpkg clean

CONCURRENCY_LEVEL=9 screen fakeroot make-kpkg \
  --append-to-version=1 \
  --revision=1 \
  --initrd \
  kernel_image

make-kpkg clean cleans up the kernel source.

CONCURRENCY_LEVEL=9 translates into `make -j9` later. make -j9 means to compile things in parallel using all your processors, so adjust accordingly for your actual system. I usually go 2x the actual number of processors +1.

screen is a command used to run another command in a virtual screen. The new virtual screen doesn’t end if you disconnect. `man screen` if you’re not familiar, it’s a very useful tool.

fakeroot provides a fake root environment in which to build a package.

make-kpkg is a kernel building and packaging tool.

The –append-to-version is whatever you want, I increment mine by one every time I build a new kernel, and usually start over when Linus releases a “stable” kernel.

The –revision is whatever you want, I set this simply for a shorter package name.

The –initrd option makes dpkg build a new initrd image when you install the kernel package later. An initrd image contains drivers your system needs before your kernel loads, for example, raid and ext3.

Build a new kernel using a distro’s (Debian in my case) default config takes a while. Everything will usually work on the first try using a distro config since everything is built as modules as much as possible, and all modules get built. You stand a good chance of successfully booting a new kernel built this way. Later you can remove stuff from the config and rebuild. Wash, rinse, and repeat until you get your kernel config down to just the hardware you actually have in your system.

Install the new kernel:

cd ..

dpkg -i linux-image-2.6.31-rc7_1_i386.deb

Reboot.

When your system comes back up..

> uname -a
Linux saturn.localdomain 2.6.31-rc7 #2 SMP Mon Aug 24 21:53:19 CDT 2009 i686 GNU/Linux
Be Sociable, Share!

Simple git setup for new git users

I recently set out to learn more about git, the new content manager for source code. “New”, as in much younger than Subversion, and a hell of a lot younger than CVS!

No one I know personally uses git yet, but I see the Ruby on Rails community starting to use it a bit, so I decided I had better get up to speed.

Being the sole developer on my own Ruby on Rail projects, I don’t really need the full powers of git. I just needed a simple setup, somewhere I could commit code over SSH. The initial steps for such a setup were not exactly simple to learn as a first-time git user. Hints were scattered across many different websites. So I’m documenting everything I’ve learned here in a blog entry, in case other git n00bs like myself might find it useful.

First make a new git repo on your remote host:

mkdir -p /git/foo
cd /git/foo
git init

git should return something like:

Initialized empty Git repository in .git/

Your /git/foo path will likely be different than mine, but it doesn’t really matter where you put stuff. I already had an /svn directory with all my Subversion repos in there, so it made sense (to me) to add a /git directory and put all my new git repos in there.

If you’re used to using Subversion you might think you can now “check out” this empty git repo, but you’d be wrong. You don’t check out code from a git repo, you “clone” it instead. But trying to clone your new git repo right now leads to the (initially confusing) error:

fatal: no matching remote head

The error occurs because your new git repo has no tree, no files, etc.

Instead of trying to clone your new remote git repo, make another git repo on your local machine, some place where you’ll be doing your actual code development later:

mkdir ~/foo
cd ~/foo
git init

git should again dazzle and impress you with the non-error message:

Initialized empty Git repository in .git/

Next add some files to the local repo you just created (still in same foo directory):

cp -r /path/to/your/project/* .

Your local git repo doesn’t know about your new project files yet. You need to “add” them (still in the same directory):

git add *

Now that git knows about them you can commit them:

git commit -m 'initial commit'

The -m is your commit message. If you leave that off you’ll be prompted to add a commit message, just like with Subversion.

At this point your project files do not yet exist in your remote git repo that you created at the very beginning. They have only been committed to your local git repo. To transfer them you’ll need to do a “push”, like this:

git push ssh://example.com/git/foo master

Again, your specific path may vary depending on where you created your remote git repo. If you’re like me, you probably run your ssh server on an alternate port other than the standard port 22. In addition, your local username might be different than the one you use on the remote server. Your push syntax might be something more exotic, like this perhaps:

git push ssh://user@example.com:8022/git/foo master

“master” tells git to push the code into the main branch of your remote git repo. If you were pushing to another branch you’d use that instead.

If all went well you’ll get a message something like:

 * [new branch]      master -> master

If you didn’t get a similar message, check your push URL for accuracy.

Next you have some options for how to tell your local repo where it’s origin/master is. I found the documented options workable, but I also found it’s just a hell of a lot easier to clone the remote repo, it now has content after all!

So I tossed out my local git repo and cloned it anew:

cd ~
rm -rf foo
git clone ssh://example.com/git/foo

If you want your local repo directory to be named something else, add a directory name on the end like this:

git clone ssh://example.com/git/foo bar

I found my normal workflow is a lot like Subversion, just with the added step of pushing out to the remote repo after the local commit. There are commit hooks to make that automatic if you like.

Be Sociable, Share!

Ubuntu 8.04 (Hardy), Dan’s Guardian, Squid, Arno’s iptables Firewall How-To

Using only free and open source software, this how-to will help you accomplish the following:

  • Setup a transparent web proxy (no browser configuration required).
  • “Bad internet sites” will be filtered and unreachable.
  • Setup an iptables-based NAT.

1) All these commands need to be ran as root. If you prefer to use sudo then obviously you’ll need to prefix all the commands listed here with `sudo`. I choose to just become root until I’m done. I use the command `sudo su -` once. Proceed however you like.

2) Make sure you can reach all the Ubuntu packages. Uncomment all the optional repositories in your /etc/apt/sources.list so it looks like this:

> cat /etc/apt/sources.list | grep -v ^# | grep -v ^$
deb http://us.archive.ubuntu.com/ubuntu/ hardy main restricted
deb-src http://us.archive.ubuntu.com/ubuntu/ hardy main restricted
deb http://us.archive.ubuntu.com/ubuntu/ hardy-updates main restricted
deb-src http://us.archive.ubuntu.com/ubuntu/ hardy-updates main restricted
deb http://us.archive.ubuntu.com/ubuntu/ hardy universe
deb-src http://us.archive.ubuntu.com/ubuntu/ hardy universe
deb http://us.archive.ubuntu.com/ubuntu/ hardy-updates universe
deb-src http://us.archive.ubuntu.com/ubuntu/ hardy-updates universe
deb http://us.archive.ubuntu.com/ubuntu/ hardy multiverse
deb-src http://us.archive.ubuntu.com/ubuntu/ hardy multiverse
deb http://us.archive.ubuntu.com/ubuntu/ hardy-updates multiverse
deb-src http://us.archive.ubuntu.com/ubuntu/ hardy-updates multiverse
deb http://us.archive.ubuntu.com/ubuntu/ hardy-backports main restricted universe multiverse
deb-src http://us.archive.ubuntu.com/ubuntu/ hardy-backports main restricted universe multiverse
deb http://archive.canonical.com/ubuntu hardy partner
deb-src http://archive.canonical.com/ubuntu hardy partner
deb http://security.ubuntu.com/ubuntu hardy-security main restricted
deb-src http://security.ubuntu.com/ubuntu hardy-security main restricted
deb http://security.ubuntu.com/ubuntu hardy-security universe
deb-src http://security.ubuntu.com/ubuntu hardy-security universe
deb http://security.ubuntu.com/ubuntu hardy-security multiverse
deb-src http://security.ubuntu.com/ubuntu hardy-security multiverse

Re-index your package repositories:

apt-get update

3) Install everything:

apt-get install dansguardian squid arno-iptables-firewall

4) Configure Arno’s iptables Firewall:

Edit /etc/arno-iptables-firewall/firewall.conf and uncomment this line:

CUSTOM_RULES=/etc/arno-iptables-firewall/custom-rules

Edit /etc/arno-iptables-firewall/custom-rules and place a custom NAT prerouting rule in for Dan’s Guardian:

/sbin/iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 80 -j REDIRECT --to-port 8080

This rule can likely be added in /etc/arno-iptables-firewall/firewall.conf by using the HTTP_PROXY_PORT variable. I have other custom iptables rules so I just put it in my custom rules file instead.

Restart the firewall:

/etc/init.d/arno-iptables-firewall restart

Arno’s script does lots of useful filtering, way more than I care to describe here. You need to configure it to your own setup obviously. You can reconfigure it using this command: `dpkg-reconfigure arno-iptables-firewall`

My setup looks like this for example:

> cat /etc/arno-iptables-firewall/debconf.cfg | grep -v ^#
DC_EXT_IF="eth0"
DC_EXT_IF_DHCP_IP=1
DC_OPEN_TCP="22 113"
DC_OPEN_UDP=""
DC_INT_IF="eth1"
DC_NAT=1
DC_INTERNAL_NET="10.0.0.0/8"
DC_NAT_INTERNAL_NET="10.0.0.0/8"
DC_OPEN_ICMP=0

Have a look at /etc/arno-iptables-firewall/firewall.conf for lots of setup options. And don’t forget `man iptables`.

5) Configure squid:

Edit /etc/squid/squid.conf.

Find these lines, uncomment them and configure:

acl our_networks src 10.0.0.0/8
http_access allow our_networks

Be sure to modify the network to reflect your own setup. For example you might be using the 192.168.0.0/24 network for your NAT instead of 10.0.0.0/8.

In the same file, find and modify the http_port setting to look like this:

http_port 10.0.0.1:3128 transparent

Again, be sure to modify it so it reflects your own network.

3128 is the default squid port. There’s really no reason to change it, but you can if you like.

Restart squid:

/etc/init.d/squid restart

6) Configure Dan’s Guardian:

Edit /etc/dansguardian/dansguardian.conf. Find and comment this line at the top of the file:

#UNCONFIGURED - Please remove this line after configuration

Set the proxyip to your internal network interface’s ip address:

proxyip = 10.0.0.1

Set the filterport address, this port should match the iptables PREROUTING –to-port value from above:

filterport = 8080

Point Dan’s Guardian to squid’s default proxy port:

proxyport = 3128

You can modify /etc/dansguardian/languages/ukenglish/template.html to customize what is seen when attempting to visit a blocked site. I made mine a bit more generic for example. My kids don’t need to know why, they just need to see “NO!”.

Restart Dan’s Guardian:

/etc/init.d/dansguardian restart

7) Test everything by visiting Google.com and Porn.com. One of those addressed will be blocked.

Be Sociable, Share!

How To Deploy Rails Using mod_rails On Apache

I’ve just spent my afternoon evaluating mod_rails. I must say I am very, very impressed so far. The install is fairly simple. Here’s how I did mine:

First I installed the “passenger” gem:

gem install passenger

Next, I used passenger to build a mod_rails module for Apache:

passenger-install-apache2-module

At the end of the mod_rails build process, passenger tells you to enter this into your Apache config:

LoadModule passenger_module \
    /usr/local/lib/ruby/gems/1.8/gems/passenger-1.0.5/ext/apache2/mod_passenger.so
RailsSpawnServer /usr/local/lib/ruby/gems/1.8/gems/passenger-1.0.5/bin/passenger-spawn-server
RailsRuby /usr/local/bin/ruby

I put mine in /usr/local/apache2/conf/httpd.conf, right below my mod_php stuff. I restarted my Apache.

Right below those Apache configuration instructions, it also tells you how to configure an Apache vhost for use with mod_rails:

<VirtualHost *:80>
   ServerName www.yourhost.com
   DocumentRoot /somewhere/public
</VirtualHost>

I have a number of production Rails applications, so I chose a new one I’m still working on a bit: http://pejorativewritings.com. I chose this one mostly because it’s not getting any traffic yet and I have yet to set a single thing to be cached. I figured it will be a good “test” site for mod_rails.

So my vhost entry ended up looking like this:

<VirtualHost 70.85.173.194:80>
    ServerAdmin webmaster@pejorativewritings.com
    ServerName pejorativewritings.com
    ServerAlias www.pejorativewritings.com
    ErrorLog logs/error_log
    CustomLog logs/pejorativewritings.com-access_log common
    DocumentRoot /rails/pejorative/public
    <Directory /rails/pejorative/public>
        Options FollowSymLinks
        AllowOverride None
        Order allow,deny
        Allow from all
    </Directory>
</VirtualHost>

The extra directory permissions are for static assests, CSS, JavaScript, etc. The hint is in the mod_rails manual, but I included here since I seem to need it both in development and in production modes.

I opened my browser, and w00h00! It was working with no apparent issues!

So then once my excitement died down a bit, I got to wondering. I’m now running a Rails application under Apache with mod_rails, what do I need to do to restart it when I re-deploy a new version? The mod_rails docs say all you have to do is touch a restart file, like this:

touch /rails/pejorative/tmp/restart.txt

So my altered my config/deploy.rb like this:

run "touch #{release_path}/tmp/restart.txt"
#run "sh #{release_path}/stop_mongrel.sh"
#run "sh #{release_path}/start_mongrel.sh"

I made a simple change to test it, re-deployed and sure enough, it seems to work! Now there’s no more restarting any servers when deploying Rails applications.

Be Sociable, Share!

Django Apache vhost

I thought someone else may need a complete working example, all in one chunk of code:

<VirtualHost 127.0.0.1>
  ServerName mysite
  <Location />
    SetHandler python-program
    PythonHandler django.core.handlers.modpython
    PythonPath "[ '/Users/destiney/django' ] + sys.path"
    SetEnv DJANGO_SETTINGS_MODULE mysite.settings
    SetEnv PYTHON_EGG_CACHE /tmp/.python-eggs
    PythonDebug On
    Order deny,allow
    Allow from all
  </Location>
  Alias /media /usr/src/django_src/django/contrib/admin/media
  <Location "/media/">
    SetHandler None
  </Location>
</VirtualHost>

Be Sociable, Share!