The case against the Founder’s Visa

Immigrantbowling
Photo by Vaguely Artistic

In my last post, I talked about why I'm in favor of tweaking the immigration system to provide a Founder's Visa. It's reasonable to ask what the downsides might be, so I thought it might be useful to lay out some possible objections and how they can be managed.

Gaming the system. Will people be able to pretend to be founders to get into the country? As Pat put it, can I just open a restaurant to claim the visa? The safeguard here is money. The proposal is that you need to have over 10% of a company with $250,000 raised in a recent investment. Once the founder's in the US, that investment will flow into the rest of the economy. It's already possible to get a visa if you self-fund a company with a large investment, the main change here is to open the door to raising money from external investors.

So could you figure out a shady deal to fake the investment? Certainly people have been caught using undeclared loans to raise the money needed for the standard E2 visa, but this proposal doesn't make that kind of fraud any easier.

More spaghetti code
. I grew to loathe the forest of obscure rules that make up the immigration system. It's truly mind-blowing how hard it is to get a simple answer to any question, there's so many special cases built-in. It's like a piece of code where years of bugs have been patched by adding yet another if statement to a monster function. The system fundamentally doesn't work, whether your goals are to reduce immigration (it has all sorts of obscure loopholes) or provide a fair way to encourage productive immigrants (it's so complex there's no guarantees you'll be able to stay).

I'm a big fan of the Canadian points system, where they give points for attributes they want in immigrants, like college degree, or work experience in an in-demand industry, and accept those with a score above a threshold. The Founder's Visa is yet another patch to the system, adding a little more complexity to the process, rather than the true overhaul it needs.

That said, I don't see any practical way to do a proper fix of the system, so patching it for this special case seems like the least-worst option.

Giving too much power to investors. There's a delicate balance of power between investors and founders, and knowing that the founder's very presence in the country depends on that term-sheet will give investors a lot of leverage during negotiations. A lot of founders are pretty nervous about this aspect, but I'm more sanguine. If you're a first-time founder getting investment, you're likely to be in a weak position anyway, and are to a large extent relying on the investors being sensible in their demands so that they don't weaken your motivation to make the company a success. In theory this tips the scales against you even more, but in practice the investors have their thumb on the tray anyway.

I'd like to finish by highlighting a comment from someone who ended up in prison for two days after he reduced his class schedule while here on an educational visa! My story's actually pretty tame, I know plenty of people who ended up having to leave the country, so I'm grateful mine has a happy ending.

My immigration story

Starsandstripes
Photo by Kalwa

I'm a big supporter of the Founder's Visa idea, but to anyone who hasn't been through the US immigration process it may be unclear why it's so important. I loved the way Manu Kumar talked about his experiences, so to contribute to the debate I'm going to give a brief run-down of my 7 year journey through the bureaucracy. A lot of friends and colleagues have been through the process too, so I know my story isn't particularly remarkable, but it may shed some light on why so many talented people give up on moving to the US. It's actually pretty long and boring, but that's kind of the point!

When I was 18, I maxed out my credit cards and took a three-month vacation in Juneau, Alaska, teaching archery at a boy scout camp in return for lodging. I fell in love with the US, there was a freedom here I'd never even imagined at home.

Back in the UK I completed a BS in Computer Science at Manchester University, which I'd chosen because of their awesome history in computers, with teachers from Turing to Steve Furber, designer of the ARM chip. After that I spent five years diving into game programming, and became a specialist in programming console GPUs, starting with the original Playstation, and moving onto the PS2, XBox and Gamecube.

The lure of the US was always in the back on my mind, and once I'd finally paid off my college loans and credit cards, I decided to take up one of the recruiters who kept contacting me, and spend some time working for an American company.

I ended up getting a job at Left Field, based just outside of LA. I accepted their offer in March 2001, but I had to wait until August for the H1B visa application to make it through the INS, not for any errors in the paperwork, that was just how long it took them to look at it.

By the time I got there, only 3 months were left on the project, but I had enough experience I was able to dive in and make a contribution. When the project was over, my manager took me aside and told me he and several other folks were leaving to start their own company. I eagerly followed, and for once the visa transfer process was trivial.

I stuck it out at the new company Kush for a year until the first project was done, but at the point where I was working 90 hour weeks and then got reprimanded for being late on a Sunday, I decided I needed a change. Over the last couple of years I'd developed some open-source image processing plugins to help out with my hobby of providing visuals for clubs and concerts, and I'd recently ported my 40 filters over to After Effects. I was astounded by the response, I'd stumbled into a market where people were charging several thousand dollars for a handful of effects, and I could produce popular ones very easily!

So, I left Kush and set out to build my own company with commercial versions of my effects. Unfortunately I quickly discovered that even with my modest savings and a proven market there was no way for me to set up a company in the US. As luck would have it, Apple approached me on the basis of my open-source technology, offered to buy what I'd created so far and give me a job. The most important part was they not only offered an H1B, they would also sponsor me for a green card.

Predictably the H1B process took longer than expected, and I was stuck back in the UK for 6 months again before I could start at Apple. Once I was there, I had to wait a year before the company would start the sponsoring process. Then, I began the first step, 'labor certification', basically proving my job was so specialized I wasn't shoving an American out. Once all the paperwork was in, I waited and waited, and it took over two years for them to finally look at my application. It was approved, and I was overjoyed, until I dug deeper through the results and realized my lawyer had filed me for an EB3 not an EB2. She'd mistakenly put me down as an unskilled worker, which would mean a decades-long wait! I had to refile under the right designation.

This time it went a lot more quickly, and I got the right certification back, and moved onto the next stage in my green card application. After lots more paperwork, including a medical examination to ensure I wasn't a psychopath or sexual deviant, I was back in another queue. This one took around 18 months, mostly because I got flagged for an FBI background check. I understand the need for checks, but it seemed slightly crazy that they had such a long delay, everyone was already resident in the US so anybody dangerous had a long time to get up to mischief.

Finally my green card came through in May 2008, five years after I started the process with Apple. The biggest sensation was relief, I could finally make plans knowing I'd be here permanently. I tidied up my work with Apple, and was free to start Mailana two months later in July.

So what's the point of this story? I love Apple dearly, I can't imagine a better large company to work for, but I'm a startup guy at heart and I ended up taking the corporate path for a long time purely because of visa issues. The Founder's Visa would have offered me a chance to create my own company much earlier, and hopefully employ a lot of Americans too! Beyond that, there was a constant sword of Damocles hanging over me all the way until I got my green card, at any point a paperwork SNAFU or employer decision could have kicked me out of the country.

How to follow your Apache error logs in a browser

Pawprinttrail
Photo by Hunting Glee

As I work to make the data import process more reliable, one of the patterns that was recommended to me was having your import processes append their results to a log file, and then have a database load process that watches the file and updates the database as it spots new content.

This potentially solves a lot of problems for me, but it's not at all obvious how to implement the file tailing functionality in PHP, so I implemented a standalone example to test some approaches. Since I've always wished there was a better way than ssh-ing into my servers to view a live version of the Apache error logs, I made the example a long-running PHP process that outputs updates from the logs to your browser. You can see it running here:

http://web.mailana.com/labs/logviewer/

The code is downloadable as logviewer.zip, or it's included below. Be warned, before you use this on a production server make sure it's password-protected, since sensitive information like passwords or credit-card numbers might leak into your error messages inadvertently!

If you're looking for a Perl version, you might want to look at the File::Tail module.

<?php

//! This function never exits!
//! It sits on the specified file, calling the process function for each new line of
//! input as it's appended by some other process. The cookie value lets you pass in
//! an opaque object to be used by the process function callback.
function tail_and_process_file($filename, $processfunction, $cookie=null)
{   
    $retrycount = 0;
    while ($retrycount<5)
    {
        if (file_exists($filename))
            $retrycount = 0;
   
        $filehandle = fopen($filename, 'r');
        if (!$filehandle && file_exists($filename))
            die("tail_and_process_file($filename, $processfunction, $cookie): The input file exists but I couldn't access it");
       
        $lastaccesstime = 0;
        $lastfilesize = 0;
        $lastfileposition = 0;
       
        while (file_exists($filename))
        {
            $currentaccesstime = fileatime($filename);
            if ($currentaccesstime!=$lastaccesstime)
            {
                $currentfilesize = filesize($filename);
                if ($currentfilesize<$lastfilesize)
                {
                    $fclose($filehandle);
                    $filehandle = fopen($filename, 'r');
                    $lastfileposition = 0;
                }
                       
                fseek($filehandle, $lastfileposition);
                while (!feof($filehandle))
                {
                    $currentline = fgets($filehandle);
                    if ($currentline!='')
                        $processfunction($currentline, $cookie);
                }
               
                $lastaccesstime = $currentaccesstime;
                $lastfilesize = $currentfilesize;
                $lastfileposition = ftell($filehandle);
            }
           
            // Without this, the results of the fileatime() may be cached
            clearstatcache();
            sleep(1);
        }
       
        // The file no longer exists, so wait a progressively longer interval
        // and try it again. After too many retries, die with an error
        $retrycount += 1;
        sleep($retrycount*15);
    }
   
    die("tail_and_process_file($filename, $processfunction, $cookie): The input file was not present after multiple retries");
}

?>

When Sears was a startup

Therainfallsalike

The rain falls alike on the Just and Unjust. All should be supplied with mackintoshes

I'm here in Seattle for a quick family visit (though I couldn't resist visiting a couple of the local startups) and I found myself flicking through a copy of the 1897 Sears catalog left at our B&B. I was astonished at the energy that leapt off every page, these guys were building their own startup! Here's what seemed so familiar:

On a mission with a new business model. They can't stop talking about how they're cutting out the middle men who've been gouging their customers, with pages devoted to messianic rants against the monopolies trying to put them out of business. They contrast their order fulfillment process (dozens of clerks dealing with tens of thousands of orders a day) with the inefficient country stores full of assistants being paid to idly wait for customers, explaining how they can offer such low prices despite the shipping.

The customers are their evangelists. Want to save on shipping? Here's some examples of how you can get $10 of goods for $6 by persuading your neighbors to order along with you.

Information wants to be free
. Want to know more than you ever believed possible about all the differences in pocket-watch mechanisms? Here's several pages of detail that your local jeweler will never tell you, but we want you to understand what you're buying so you'll feel comfortable buying sight-unseen.

Trust in technology
. The very notion of sending money to some company a thousand miles away and hoping they'll send you decent goods in return was a leap of faith even bigger than typing your credit card into a web site. Instead of SSL certificates, they have an engraving of their building and letters from their bankers.

Searsfarm

Selling a dream. They knew people weren't just looking to buy something when they picked up the catalog, so they offer a hope of a better life in their descriptions and illustrations. People didn't just want barbed wire, they wanted that perfect farm, and Sears used that to sell.

They were completely nuts:

Searsdogpower

Like most innovators, they weren't afraid to screw up. Happily I'm pretty certain this dog-powered butter-churner never hit the mainstream, but they have thousands of new product ideas they were constantly trying out, along with really zany ads.

Grasssuits

Sure, online ad placement can be weird, but a real human being decided people interested in grass suits for hunting wild geese will also like a nice pram!

The BOSS, Bing and Google search APIs from a legal perspective

JudgewigsPhoto by Steve Punter

One of the most promising features of the cloud is the ability to leverage other companies' APIs to power your business. More than just saving money, it lets you do things that would be impossible for a startup to build in-house, like a search index for the whole web.

Of course there's always a downside. As Todd Vernon points out in his latest blog post, you're trusting a third-party with your company's future. This is an especial problem with Google since they tend to automate everything, so it can be near impossible to reach a real human being to fix any problems if they do decide to cut off your access. Jud Valeski spotted a classic example of this when some API providers 86-ed IP addresses on App Engine and EC2.

With those fears in mind, it was great to read Jay Parkhill's analysis of the BOSS, Bing and Google Search APIs. There's still plenty of ambiguity left in the agreements, and I've no doubt that the providers could arbitrarily cut me off despite anything in the terms, but it's a great insight into what the providers care about. It should make it a lot easier to skirt any uses that would hit hot-button issues for them, so I'm very grateful to Jay for taking the time to research this.

Why I switched my search API from Bing to Google

Googlelogo
Image by Mark Knol

Going through the Techstars program, a few of my mentors worried about how much I was revealing through my blog. Fundamentally it isn't a calculation, I love what I'm doing and I love talking about it, but I just ran into yet another situation where being open paid off.

Joehtweet

Joe Heitzeberg dropped me that note in reply to my last blog post on switching to Bing from BOSS, and it was gold-dust. I was aware of the Ajax API from a couple of years ago, but when I last looked into Google's offerings they were extremely restrictive about what you could do with the interface. Checking out their documentation I saw they talk about more than client-side apps, they offer a REST interface and even have some PHP examples! The terms-of-service don't prohibit non-client use, though they do specify that your application must be freely available to users.

After a bit of experimentation I was able to get it up and running, and it made me extremely happy. In the test case I'm running, Google finds 44 Facebook profile pages for Susan Fogg, Bing finds 6 and BOSS only finds 1. That makes a massive difference to the usefulness of the friend suggestion part of Mailana.

There are a few wrinkles to the API. By default it only returns 4 results per call, and I had to add the &rsz=large to get 8. Since I'm getting 50 at a time from the other providers, I then had to loop through adding &start=0, &start=8 , etc to pull in multiple pages. Google also don't include possible duplicate results by default, but adding &filter=0 fixed that.

Updated code included inline below, or you can download the complete source here

<?php

// You'll need to get your own API keys for these services. See

// http://developer.yahoo.com/wsregapp/

// http://www.bing.com/developers/createapp.aspx

// http://code.google.com/apis/ajaxsearch/signup.html

define('BING_API_KEY', '');

define('YAHOO_API_KEY', '');

define('GOOGLE_API_KEY', '');

function pete_curl_get($url, $params)

{

$post_params = array();

foreach ($params as $key => &$val) {

  if (is_array($val)) $val = implode(',', $val);

$post_params[] = $key.'='.urlencode($val);

}

$post_string = implode('&', $post_params);

$fullurl = $url."?".$post_string;

$ch = curl_init();

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);

    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);

curl_setopt($ch, CURLOPT_URL, $fullurl);

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

curl_setopt($ch, CURLOPT_USERAGENT, 'Mailana (curl)');

$result = curl_exec($ch);

curl_close($ch);

return $result;

}

function perform_boss_web_search($termstring)

{

$searchurl = 'http://boss.yahooapis.com/ysearch/web/v1/&#039;;

$searchurl .= urlencode($termstring);

$searchparams = array(

'appid' => YAHOO_API_KEY,

'format' => 'json',

'count' => '50',

);

$response = pete_curl_get($searchurl, $searchparams);

$responseobject = json_decode($response, true);

error_log(print_r($responseobject, true));

if ($responseobject['ysearchresponse']['totalhits']==0)

return array();

$allresponseresults = $responseobject['ysearchresponse']['resultset_web'];

$result = array();

foreach ($allresponseresults as $responseresult)

{

$result[] = array(

'url' => $responseresult['url'],

'title' => $responseresult['title'],

'abstract' => $responseresult['abstract'],

);

}

return $result;

}

function perform_bing_web_search($termstring)

{

$searchurl = 'http://api.bing.net/json.aspx?&#039;;

$searchurl .= 'AppId='.BING_API_KEY;

$searchurl .= '&Query='.urlencode($termstring);

$searchurl .= '&Sources=Web';

$searchurl .= '&Web.Count=50';

$searchurl .= '&Web.Offset=0';

$searchurl .= '&Web.Options=DisableHostCollapsing+DisableQueryAlterations';

$searchurl .= '&JsonType=raw';

$response = pete_curl_get($searchurl, array());

$responseobject = json_decode($response, true);

if ($responseobject['SearchResponse']['Web']['Total']==0)

return array();

$allresponseresults = $responseobject['SearchResponse']['Web']['Results'];

$result = array();

foreach ($allresponseresults as $responseresult)

{

$result[] = array(

'url' => $responseresult['Url'],

'title' => $responseresult['Title'],

'abstract' => $responseresult['Description'],

);

}

return $result;

}

function perform_google_web_search($termstring)

{

$start = 0;

$result = array();

while ($start<50)

{

$searchurl = 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&#039;;

$searchurl .= '&key='.GOOGLE_API_KEY;

$searchurl .= '&start='.$start;

$searchurl .= '&rsz=large';

$searchurl .= '&filter=0';

$searchurl .= '&q='.urlencode($termstring);

$response = pete_curl_get($searchurl, array());

$responseobject = json_decode($response, true);

if (count($responseobject['responseData']['results'])==0)

break;

$allresponseresults = $responseobject['responseData']['results'];

foreach ($allresponseresults as $responseresult)

{

$result[] = array(

'url' => $responseresult['url'],

'title' => $responseresult['title'],

'abstract' => $responseresult['content'],

);

}

$start += 8;

}

return $result;

}

if (isset($_REQUEST['q'])) {

$termstring = urldecode($_REQUEST['q']);

} else {

$termstring = '';

}

?>

<html>

<head>

<title>Test page for Google, BOSS and Bing search apis</title>

</head>

<body>

<div style="padding:20px;">

<center>

<form method="GET" action="searchexample.php">

Search terms: <input type="text" size="40" name="q" value='<?=$termstring?>'/>

</form>

</center>

</div>

<?php

if ($termstring!='') {

$googleresults = perform_google_web_search($termstring);

$bingresults = perform_bing_web_search($termstring);

$bossresults = perform_boss_web_search($termstring);

print '<br/><br/><h2>Google search results ('.count($googleresults).')</h2><br/>';

foreach ($googleresults as $result) {

print '<a href="'.$result['url'].'">'.$result['title'].'</a><br/>';

print '<span style="font-size:80%">'.$result['abstract'].'</span><br/><hr/>';

}

print '<br/><br/><h2>Bing search results ('.count($bingresults).')</h2><br/>';

foreach ($bingresults as $result) {

print '<a href="'.$result['url'].'">'.$result['title'].'</a><br/>';

print '<span style="font-size:80%">'.$result['abstract'].'</span><br/><hr/>';

}

print '<br/><br/><h2>BOSS search results ('.count($bossresults).')</h2><br/>';

foreach ($bossresults as $result) {

print '<a href="'.$result['url'].'">'.$result['title'].'</a><br/>';

print '<span style="font-size:80%">'.$result['abstract'].'</span><br/><hr/>';

}

}

?>

Why I switched my search API from BOSS to Bing

Bing

I'm a massive fan of Yahoo's developer tools, I think they're massively underrated by geekdom, and I'm still heavily reliant on their geo-coding services like Placemaker. It makes me pretty sad to admit I've recently switched from Yahoo BOSS to Bing's search API, so I thought I'd share my reasons, together with some PHP sample code.

In a nutshell, BOSS wasn't finding enough results for the sort of work I'm doing. Here's an example search, looking for people called Susan Fogg with public Facebook profiles:

http://www.bing.com/search?q=site%3Awww.facebook.com%2Fpeople+intitle%3A%22Susan+Fogg%22
6 results

http://search.yahoo.com/search?p=site%3Awww.facebook.com%2Fpeople+intitle%3A%22Susan+Fogg%22
1 result

http://www.google.com/search?q=site%3Awww.facebook.com%2Fpeople+intitle%3A%22Susan+Fogg%22&filter=0
15 results

This is not a scientific survey by any means, but Bing seems to index a lot more of the obscure pages on social networks than Yahoo. If only Google offered an API, they would be even better, but switching to Bing still offers a big improvement for my application.

I was nervous that Bing would be crippled by usage terms, but luckily they are effectively unrestricted and can be used for non-user-facing applications like mine.

Here's the code I'm using, as a download or included inline below:

<?php

// You'll ned to get your own API keys for these services. See
// http://developer.yahoo.com/wsregapp/
// http://www.bing.com/developers/createapp.aspx
define('BING_API_KEY', '');
define('YAHOO_API_KEY', '');

function pete_curl_get($url, $params)
{
    $post_params = array();
    foreach ($params as $key => &$val) {
      if (is_array($val)) $val = implode(',', $val);
        $post_params[] = $key.'='.urlencode($val);
    }
    $post_string = implode('&', $post_params);

    $fullurl = $url."?".$post_string;

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
    curl_setopt($ch, CURLOPT_URL, $fullurl);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mailana (curl)');
    $result = curl_exec($ch);
    curl_close($ch);

    return $result;
}

function perform_boss_web_search($terms)
{
    $searchurl = 'http://boss.yahooapis.com/ysearch/web/v1/&#039;;
    $searchurl .= urlencode(implode(' ', $terms));
    $searchparams = array(
        'appid' => YAHOO_API_KEY,
        'format' => 'json',
        'count' => '50',
    );

    $response = pete_curl_get($searchurl, $searchparams);
   
    $responseobject = json_decode($response, true);
   
    if ($responseobject['ysearchresponse']['totalhits']==0)
        return array();
   
    $allresponseresults = $responseobject['ysearchresponse']['resultset_web'];

    $result = array();
    foreach ($allresponseresults as $responseresult)
    {
        $result[] = array(
            'url' => $responseresult['url'],
            'title' => $responseresult['title'],
            'abstract' => $responseresult['abstract'],
        );
    }

    return $result;
}

function perform_bing_web_search($terms)
{
    $searchurl = 'http://api.bing.net/json.aspx?&#039;;
    $searchurl .= 'AppId='.BING_API_KEY;
    $searchurl .= '&Query='.urlencode(implode(' ', $terms));
    $searchurl .= '&Sources=Web';
    $searchurl .= '&Web.Count=50';
    $searchurl .= '&Web.Offset=0';
    $searchurl .= '&Web.Options=DisableHostCollapsing+DisableQueryAlterations';
    $searchurl .= '&JsonType=raw';

    $response = pete_curl_get($searchurl, array());
   
    $responseobject = json_decode($response, true);
    if ($responseobject['SearchResponse']['Web']['Total']==0)
        return array();
   
    $allresponseresults = $responseobject['SearchResponse']['Web']['Results'];

    $result = array();
    foreach ($allresponseresults as $responseresult)
    {
        $result[] = array(
            'url' => $responseresult['Url'],
            'title' => $responseresult['Title'],
            'abstract' => $responseresult['Description'],
        );
    }

    return $result;
}

if (isset($_REQUEST['q'])) {
    $terms = explode(' ', urldecode($_REQUEST['q']));
} else {
    $terms = array();
}

$termstring = implode(' ', $terms);
?>
<html>
<head>
<title>Test page for BOSS and Bing search apis</title>
</head>
<body>
<div style=&quot
;padding:20px;">
<center>
<form method="GET" action="index.php">
Search terms: <input type="text" size="40" name="q" value="<?=$termstring?>"/>
</form>
</center>
</div>
<?php
if (count($terms)>0) {

    $bingresults = perform_bing_web_search($terms);
    $bossresults = perform_boss_web_search($terms);

    print '<br/><br/><h2>Bing search results ('.count($bingresults).')</h2><br/>';
    foreach ($bingresults as $result) {
        print '<a href="'.$result['url'].'">'.$result['title'].'</a><br/>';
        print '<span style="font-size:80%">'.$result['abstract'].'</span><br/><hr/>';
    }

    print '<br/><br/><h2>BOSS search results ('.count($bingresults).')</h2><br/>';
    foreach ($bingresults as $result) {
        print '<a href="'.$result['url'].'">'.$result['title'].'</a><br/>';
        print '<span style="font-size:80%">'.$result['abstract'].'</span><br/><hr/>';
    }

}

?>

Net Promoter Score (NPS) Example Code

Orb
Photo by Jjjohn

I first ran across the Net Promoter Score through a post by Eric Ries and it seems like a simple but effective measure of how happy your customers are. Its beauty is how basic it is, which both makes it easy to interpret and straight-forward to gather without annoying your customers.

Unfortunately there don't seem to be any off-the-shelf solutions to help gather the information. In the past I've created surveys through companies like SurveyMonkey, but that's both a pretty intrusive experience and they don't give you any way to calculate an NPS without downloading the raw data into Excel and doing it yourself!

What I wanted was a way to survey my customers from within the site, without sending them to an external page or another window, store the results on my own server and then have a simple way of viewing reports on the data over time. Since there was nothing else out there, I wrote a simple Javascript/PHP/MySQL module to handle these requirements, and since I'm sure there are other people who could use something similar and I hate seeing wheel-reinvention, I've released it under a BSD license.

It works by randomly bringing up an in-page popup, asking the user whether they'd recommend the service to a friend, and then requesting any other comments. The data gets passed back to the server and stored in the database, where you can then get a very basic HTML report, or pull the data as JSON to pass into your metrics pipeline, for things like daily email reports.

You can download the code here:
http://code.google.com/p/npspopup/

If you want to try in action, here's the demo page:
http://web.mailana.com/labs/npspopup/index.php

I'm testing it on my own site but the code is still pretty pre-alpha, so don't be shy with bug-fixes and other modifications.

Lean Startup Workshop Review

Workshop
Photo by Dammit Jack

Eric Ries is on a nationwide tour for the next couple of months, and I was lucky enough to attend his Lean Startup Workshop right here in Boulder. If you're not familiar with Eric's work, he's a serial startup founder who's made it his mission to save the world from the avalanche of tech companies that fail. At the start of the workshop he brought up a wall of well-known web 2.0 company logos, with all the dead ones marked. It was sobering, but what really drove him crazy wasn't that most failed tech startups never had a single paying customer!

The Customer Development process is aimed at solving that problem, and Eric started us off with a little taster by splitting us up into groups to write down a description of who we thought the customers of the workshop would be. We then took those descriptions and tried to turn them into yes/no qualifying questions that we could use to distinguish customers from non-customers.

After that I had a chance to quiz Eric about one of the underlying assumptions of the philosophy, that market risk is far more important these days than technology risk to software startups. This wasn't true in the dot-com era, it took millions of dollars and obscure technology to build an interactive website, and a lot of the startup world has been slow to adapt as things have changed. As he put it, democratizing technology means the technology risk is reducing.

This is a bitter pill for me to swallow in some ways, my background is tech-heavy so it would be to my advantage to have more technical barriers, but as an unfunded startup it's also amazing how much I can accomplish on a shoestring budget these days.

Eric spent the rest of the workshop doing a fireside chat as we went through his slides. My favorite part was the cringe-inducing video of Ali G pitching his 'icecream glove' to VCs. I defy any entrepreneur to watch that and not feel a twinge of recognition. What I got out of the talk was a much stronger grasp of the customer discovery process. He spent a lot of time talking about their experiences at IMVU, as all the pesky teenagers using the service prevented them getting in touch with who they thought their real customers were, stay-at-home moms. Of course the punch-line was that their real customers, at least at first, were those kids and they had to learn to build the business around them.

One of his observations was that there was no substitute for bringing in customers and having a face-to-face chat with them about the product, the bandwidth is massively higher thanks to body language and other subtle clues. The same goes for this workshop, compared to reading Eric's writing. We had a chance to ask questions and to watch Eric jam on a topic, really getting across the experiences that drove him to his conclusions.

A lot of my focus has been on the metrics side of the process, maybe unconsciously because as a techie that's a lot less threatening than spending lots of time meeting people! The workshop helped me take a wider view of Customer Development, but it did also make me wish there were more tools being shared for some of the common techniques. I've posted some code to generate a simple daily metrics email, and it sounds like Eric might be working with KISSmetrics to produce an A/B testing framework, but there's still a lot of us reinventing things like a NPS popup and recording system. I'm hoping to put some of these components into the Lean Startup Wiki over the next week.

I'd highly recommend this workshop to anyone who wants customers for their startup, it's a great way to learn both from Eric himself and from the like-minded people who'll be there with you. Check out the wiki or Eric's blog to see if there's going to be one near you.

Three books for entrepreneurs

Threebooks Photo by Tim Green

I don't like Fred Wilson and Brad Feld's entrepreneurial books you should read before you're 21. Actually, I love most of the books they mention, but they're the same list I'd recommend to anyone who wants a positive and fulfilling life. You need a special brand of insanity to be an entrepreneur, and stories from the trenches are a much better introduction to that world.

Read these three books, with all their years of painful struggle, frequent failures and rare shining moments of achievements and picture yourself going through the same journey. For me they're inspirational, but a lot of people seem to run away screaming when confronted with the reality of startup life.

The Mousedriver Chronicles

Two MBA students are inspired to turn their business plan of a computer mouse shaped like a golf driver into reality. They start off with no clue how to sell or even make the product, but they spend a year working extremely hard and by the end they finally start to take off. The best part is their honesty about the grueling process of connecting with the right people. They would literally be cold-calling companies from the yellow pages, and if they got someone vaguely sympathetic and knowledgeable, pump them for any help they could.

Starting Something

Wayne McVicker was a founder of Neoforma, a healthcare startup that IPO-ed in the dot-com boom. What marks the book out as something special is his focus on the failures and disappointments that accumulated as he built his company, and his personal run-ins with depression. What kept him going through the dark times was the "hope that someday it would again deliver amazing value to customers", and that shines through his story.

No Vision, All Drive

Pinpoint went through multiple near-death experiences to a very successful exit, and David Brown chronicles everything from the early stumbles to the final sale. Despite the title they did have a vision, but it was just to serve their customers incredibly well, and the book shows much work and struggle it takes to turn that simple phrase into reality. The strength of the book is how well the employees are sketched. My favorite part of being an entrepreneur is that if you're successful, you have a massive positive impact on everyone around you. The employees stories show how Pinpoint literally changed their lives, not only giving them a salary but helping them develop new skills and finally sharing in the company's acquisition.