Author: mfoster

  • Review: Lego 6211 Star Wars Imperial Star Destroyer

    Jen gave me the Lego 6211 Star Wars Imperial Star Destroyer for Christmas and I finished building it over the following week.

    The build is pretty easy (even for a 35 year old) and a lot fun. The kit comes in over 1300 parts so you really feel like your building something and not just putting a couple halves together. The parts come in numbered bags that correspond to the numbered sections in the instructions. There are often multiple bags with the same number for a single section. Lego included a few extras of the tiniest parts that tend to get lost in the carpet.

    There are only a couple downsides to the kit. The two “laser blasters” that launch projectiles work by you just quickly depressing the launch button that drives a wedge piece behind the projectile to push it out. This is kind of silly to me and I think the kit would have been better if they were just static elements in my opinion.

    The other negative was the capsule that goes inside. It is assembled by connecting two specially molded pieces together. I don’t like this kind of Lego construction and would have preferred it if they just left it out or designed it to build from smaller, standard Lego pieces.

    I put together a list of pros and cons:

    Pros

    • Over 1300 parts so it will keep you building for a bit.
    • Numbered bags.
    • Spare tiny pieces.
    • Looks great!
    • It’s big. About 23 inches long.

    Cons

    • The laser blasters that shoot projectiles are kind of cheesy.
    • The capsule that goes inside is mostly just two halves that go together.
    • Doesn’t shoot real bolts of green light. 😉

    Build gallery

    I took some pictures while my Imperial Star Destroyer was under construction:

    Embellishment

    I felt the kit would have been better if they included lights and a small universe. Fortunately, I was able to accomplish this with a small string of Christmas lights, a telescope photo of the stars, black velvet, and a little Photoshop magic:

    Conclusion

    I easily give this kit 5 out of 5 stars. It was fun to build and looks great.

  • How to: PECL HTTP request exception and error handling

    In a previous post, we created a simple PHP page that told us if http://www.example.com is up or down by using the PECL HTTP extension to make an HTTP request to the site and look for the string “example” in the response. Here is the code for our test page, httptest.php:

    <?php
    
    $http_req = new HttpRequest("http://www.example.com");
    $http_req->setOptions(array(timeout=>10,useragent=>"MyScript"));
    $http_req->send();
    
    if (stripos($http_req->getResponseBody(), "example") === false){
        echo "The page is down!";
    } else {
        echo "The page is up!";
    }
    
    ?>
    

    The problem with this code is that there is no error handling. Below are a few examples of what can go wrong and the resulting errors:

    DNS Lookup Failure

    If the DNS lookup fails the page will return the following error:

    Fatal error: Uncaught exception 'HttpInvalidParamException' with message 'Empty or too short HTTP message: ''' in /var/www/httptest.php:12 inner exception 'HttpRequestException' with message 'Couldn’t resolve host name; Couldn’t resolve host 'www.somewebsitethatdoesnotexist.com'
    (http://www.somewebsitethatdoesnotexist.com/)' in /var/www/httptest.php:4 Stack trace: #0 /var/www/httptest.php(12): HttpRequest->send() #1 {main} thrown in /var/www/httptest.php on line 12

    Since www.example.com is a valid DNS name I used “www.somewebsitethatdoesnotexist.com” instead to demonstrate what happens with an invalid name or failed DNS lookup. Note the “inner exception” that says “Couldn’t resolve host name”. More on “inner exceptions” in a bit. This is not very pretty for a diagnostic page.

    Connection Failure

    In this example I again used “www.somewebsitethatdoesnotexist.com” but I added the following entry to the /etc/hosts file on the server:

    10.10.10.10 www.somewebsitethatdoesnotexist.com

    Now the DNS entry will resolve using the /etc/hosts file but this is not a valid IP for any machine on my neetwork so I see this error:

    Fatal error: Uncaught exception ‘HttpInvalidParamException’ with message ‘Empty or too short HTTP message: ''' in /var/www/httptest.php:12 inner exception ‘HttpRequestException’ with message ‘Timeout was reached; connect() timed out! (http://www.somewebsitethatdoesnotexist.com/)’ in /var/www/httptest.php:4 Stack trace: #0 /var/www/httptest.php(12): HttpRequest->send() #1 {main} thrown in /var/www/httptest.php on line 12

    Again we have a inner exception buried in all of that telling me that the connection time out.

    404 Error

    In this example I put in “http://localhost/notarealpage.php” for the URL. This will connect to the local Apache server but that page doesn’t exist so the server will return a 404 file not found error. The server responded but since we are not checking the response code from the server our code just tells us the page is down and that is true but it would be useful to know that it is because the page is missing!

    The page is down!

    If the server responds OK we will get a 200 status code. We should handle any other response appropriately.

    Handle the exceptions

    The first thing we can do is put a try catch block around our code and try catching the HttpException as shown in example section of the documentation for the HttpRequest::send method:

    <?php
    
    $http_req = new HttpRequest("http://www.example.com");
    $http_req->setOptions(array(timeout=>10,useragent=>"MyScript"));
    
    try {
        $http_req->send();
    } catch (HttpException $ex) {
        echo $ex->getMessage();
    }
    
    if (stripos($http_req->getResponseBody(), "example") === false){
        echo "The page is down!";
    } else {
        echo "The page is up!";
    }
    
    ?>
    

    If there is a time out or connection failure the HttpException is caught and we see this:

    Empty or too short HTTP message: ''The page is down!

    Hmm… that is not very informative and the same error is displayed for both a name lookup failure and a connection timeout. We can also try changing:

    echo $ex->getMessage();
    to
    echo $ex;

    Now we get this:

    exception 'HttpInvalidParamException' with message 'Empty or too short HTTP message: ''' in /var/www/httptest.php:16 inner exception 'HttpRequestException' with message 'Couldn’t resolve host name; Couldn’t resolve host 'www.ssomewebsitethatdoesnotexist.com'
    (http://www.ssomewebsitethatdoesnotexist.com/)' in /var/www/httptest.php:5 Stack trace: #0 /var/www/httptest.php(16): HttpRequest->send() #1 {main}The page is down!

    That is painfully ugly but at least get the “Couldn’t resolve host name” message in there so we know what went wrong. Still, we can do better.

    In addition to putting a try-catch around the send() method you probably should surround all of your HttpRequest code with a try-catch that eventually catches “Exception” to be safe.

    The undocumented inner exception

    The HttpException object, which is not really documented all that well as of this writing, has something completely undocumented called an inner exception. The inner exception is a more detailed exception that is nested inside the HttpException object. We can check if an inner exception is set and if so, display that instead:

    <?php
    
    $http_req = new HttpRequest("http://www.example.com");
    $http_req->setOptions(array(timeout=>10,useragent=>"MyScript"));
    
    try {
        $http_req->send();
    } catch (HttpException $ex) {
        if (isset($ex->innerException)){
            echo $ex->innerException->getMessage();
            exit;
        } else {
            echo $ex;
            exit;
        }
    }
    
    if (stripos($http_req->getResponseBody(), "example") === false){
        echo "The page is down!";
    } else {
        echo "The page is up!";
    }
    
    ?>
    

    Now we get just the part we are interested in:

    Couldn’t resolve host name; Couldn’t resolve host 'www.ssomewebsitethatdoesnotexist.com'
    (http://www.ssomewebsitethatdoesnotexist.com/)

    If the lookup is OK but we get a connection timeout we now see this:

    Timeout was reached; connect() timed out! (http://www.somewebsitethatdoesnotexist.com/)

    If no inner exception is detected the HttpException is echoed.

    Check status codes

    Sometimes the server may be responding but we do not get a 200 status. This could because of a redirect, security error, missing page, or a 500 server error. The HttpRequest object provides a getResponseCode() method so we can check what the response was and handle it appropriately. If redirects are followed the last received response is used. For this example we will simply echo out some of the common status codes if we don’t get a 200:

    <?php
    
    $http_req = new HttpRequest("http://www.example.com/blah");
    $http_req->setOptions(array(timeout=>10,useragent=>"MyScript"));
    
    try {
        $http_req->send();
    } catch (HttpException $ex) {
        if (isset($ex->innerException)){
            echo $ex->innerException->getMessage();
            exit;
        } else {
            echo $ex;
            exit;
        }
    }
    
    $response_code = $http_req->getResponseCode();
    
    switch ($response_code){
        case 200:
        break;
        case 301:
        echo "301 Moved Permanently";
        exit;
        case 401:
        echo "401 Unauthorized";
        exit;
        case 404:
        echo "404 File Not Found";
        exit;
        case 500:
        echo "500 Internal Server Error";
        exit;
        default:
        echo "Did not receive a 200 response code from the server";
        exit;
    }
    
    if (stripos($http_req->getResponseBody(), "example") === false){
        echo "The page is down!";
    } else {
        echo "The page is up!";
    }
    
    ?>
    

    This handles a few of the more common response/status codes. To test the code we can put in a valid URL but a bogus page (I.e. http://www.example.com/blah) If everything works right we now see the following response from our diagnostic page:

    404 File Not Found

    Final Notes

    Our little diagnostic page can now handle most of the errors it will likely encounter when it attempts to test our example site, example.com. If we wanted to take this a bit further we could add a database back-end that maintains a list of multiple sites we would like to test. To take things a step further we could execute this PHP script from the command line via a cron job that runs every 5 minutes. We could then have the script send an e-mail when a site was down with the problem indicated in the message. If we wanted to maintain some up-times stats would could log the outages to a database and generate uptime/SLA reports daily, weekly, yearly, etc.

    In reality, I would just use something like IPSentry or Nagios to conserve effort for future generations but it was nice to think about. 😉 Happy coding!

  • How to: Find your php.ini file and enable PHP error reporting

    On some distributions PHP error reporting or display of errors is disabled by default as a security precaution. This is a good idea for production systems because errors may reveal useful information to undesirables. In a development environment on the other hand, it is generally useful to see your errors. 😉 If error display is disabled you may just see a blank browser window/empty page when you expect an error. To enable errors and error display, find the your php.ini file and make sure the following lines are set:

    ;Show all errors, except for notices and coding standards warnings
    error_reporting = E_ALL & ~E_NOTICE

    display_errors = On

    On Ubuntu you can find the php.ini file here:
    /etc/php5/apache2/php.ini

    On other distributions try:
    /etc/php.ini

    On Windows you might find it here:
    c:\windows\php.ini

    If you are running XAMPP it will be in the php folder off the XAMPP root.

    You will need to restart Apache (or IIS as the case may be) so your changes will take effect:

    On Ubuntu:

    sudo /etc/init.d/apache2 restart

    On other distributions you might try:

    sudo /etc/init.d/httpd restart
  • How to use the PECL HTTP (PECL_HTTP) Extension to make HTTP requests from PHP

    PECL HTTP is a feature-rich PHP extension that allows you to make HTTP and HTTPS (SSL) requests from your PHP code and handle the responses. If you are not familiar with PECL, it is a library of extensions to add functionality to PHP. It uses the same package and delivery system as PEAR.

    Many distributions do not install PECL_HTTP by default even if you install PHP. If you try to use one of the PECL_HTTP object or functions (I.e. http_get()) without the extension installed you will likely get something like this:

    Fatal error: Call to undefined function http_get()

    If this error comes up but you think you installed the PECL_HTTP package and it shows up in phpinfo(), then it is possible your PECL_HTTP install failed and did not get cleaned up so phpinfo() still sees it. This may happen if you didn’t install the cURL source library dependency first (see below).

    So let’s pretend we own the site http://www.example.com (See RFC 2606.) We want to build a PHP diagnostic page that will tell us that www.example.com is returning the string “example” somewhere in the page indicating if the page is up or down. First I need to install the PECL_HTTP extension. For details on how to install a PECL extension see my post “How to install a PHP PECL extension/module on Ubuntu“. For now I am going to assume that the php-pear and php5-dev packages have already been installed. These instructions are based on a Ubuntu install:

    • Install the libcurl3-openssl-dev package. The HTTP_PECL extension requires some of the cURL source libraries so we will have to install the cURL library package first:
      sudo apt-get install libcurl3-openssl-dev

      If you don’t install the cURL source library package you will likely see the following error when you attempt to install the PECL_HTTP extension:

      checking for curl/curl.h... not found
      configure: error: could not find curl/curl.h
      ERROR: ‘/tmp/pear/temp/pecl_http/configure --with-http-curl-requests --with-http-zlib-compression=1 --with-http-magic-mime=no --with-http-shared-deps’ failed
    • Install the HTTP_PECL module with the following command:
      sudo pecl install pecl_http

      The installer may ask you about some specific options but unless you really know what you want, you can probably just hit enter one or more times to accept all the defaults. If all goes well, the module should download, build, and install.

    • Once the install is complete, it will probably ask you to add a “extension=http.so” line to your php.ini file. Open up the php.ini file in your favorite text editor and add the line under the section labeled “Dynamic Extensions”. On Ubuntu the php.ini file seems to be located in the /etc/php5/apache2 folder:
      sudo nano /etc/php5/apache2/php.ini
    • Now that the php.ini file has been updated, Apache will need to be restarted so the new extension will be loaded:
      sudo /etc/init.d/apache2 restart

      That should restart Apache on Ubuntu but if that doesn’t work you can try:

      sudo /etc/init.d/httpd restart

    At this point hopefully the PECL_HTTP extension is installed so now we can create a PHP script that will make an HTTP request to http://www.example.com and display the results. For this example I will use the http_get() function. The first argument is a predefined constant of the request method type (GET, POST, etc.) and the second argument is a string containing the URL. I created a file named httptest.php (using “sudo nano /var/www/httptest.php” with the following code and put it in the /var/www folder (The default HTTP root on a Ubuntu server):

    <?php

    echo http_get("http://www.example.com");

    ?>

    or you could use the http_request function instead to do the same thing:

    <?php

    echo http_request(HTTP_METH_GET,"http://www.example.com");

    ?>

    When the page is opened in a web browser (I.e. http://sparky/httptest.php) it returns something like this:

    HTTP/1.1 200 OK Date: Sun, 04 Jan 2009 22:41:54 GMT Server: Apache/2.2.3 (CentOS) Last-Modified: Tue, 15 Nov 2005 13:24:10 GMT ETag: "b80f4-1b6-80bfd280" Accept-Ranges: bytes Content-Length: 438 Connection: close Content-Type: text/html; charset=UTF-8

    You have reached this web page by typing "example.com", "example.net", or "example.org" into your web browser.

    These domain names are reserved for use in documentation and are not available for registration. See RFC 2606, Section 3.

    That’s it. Those are some pretty quick one-liners if we are fine with the default options. This time we’ll do something similar but use the HttpRequest object instead and set a timeout and a different user agent:

    <?php

    $http_req = new HttpRequest("http://www.example.com");
    $http_req->setOptions(array(timeout=>10,useragent=>"MyScript"));
    $http_req->send();
    echo $http_req->getRawResponseMessage();

    ?>

    The output is the same as the previous two commands but this time the server could have taken up to ten seconds to respond before the request timed out. In addition, we sent the user agent string “MyScript” in the host header to the server. If you don’t want the HTTP response headers included in the the output, you use the getResponseBody() method instead:

    <?php

    $http_req = new HttpRequest("http://www.example.com");
    $http_req->setOptions(array(timeout=>10,useragent=>"MyScript"));
    $http_req->send();
    echo $http_req->getResponseBody();

    ?>

    This outputs:

    You have reached this web page by typing "example.com", "example.net", or "example.org" into your web browser.

    These domain names are reserved for use in documentation and are not available for registration. See RFC 2606, Section 3.

    No response headers this time. In fact, it looks as though you typed http://www.example.com in the browser.

    I could set some URL query parameters using the setQueryData() if example.com was a dynamic page that accepts arguments but I am pretty sure it does not.

    For the purpose of our example it doesn’t look like we have gotten very far but PHP is now getting a hold of the response data before we see it so we are halfway there. Now all we need to do is search for the string “example” and return some type of indicator letting us know that the example.com page is up or down:

    <?php
    
    $http_req = new HttpRequest("http://www.example.com");
    $http_req->setOptions(array(timeout=>10,useragent=>"MyScript"));
    $http_req->send();
    
    /*Note: The stripos() function just returns false if it doesn't find an upper or lower case version of the string we are looking for.*/
    if (!stripos($http_req->getResponseBody(), "example")){
        echo "The page is down!";
    } else {
        echo "The page is up!";
    }
    
    ?>
    

    If everything is working correctly we will see:

    The page is up!

    If example.com is broken or doesn’t return the string “example” our test page returns:

    The page is down!

    This is great and all but you may have noticed there is no error handling to speak of which isn’t good. I will talk about HTTP request/PECL_HTTP error handling in a separate post but until then, happy HTTPing!

  • How to install a PHP PECL extension/module on Ubuntu

    PHP PECL extensions provide additional functionality over the base PHP install. You can browse the PHP PECL extensions available at the PECL repository here. The following steps show how to install a PECL extension/module on Ubuntu using the PECL_HTTP extension as an example and assumes that you already have Apache 2 and PHP 5 installed:

    • First, you will need to install PEAR via apt-get to get the necessary package and distribution system that both PEAR and PECL use. From a shell prompt enter:
      sudo apt-get install php-pear

      You will be prompted to confirm the install. Just press “y” and enter. If all goes well you should see it download and install the php-pear package.

      Note: “sudo” is used to provide the super user privileges necessary for the following command. So in this case the command “apt-get install php-pear” is being executed with super user privileges by preceding it with “sudo”. Unless configured otherwise, you will normally be prompted to enter a password when you use sudo. This is usually the same password that you logged in with.
    • Now you will need to install the php5-dev package to get the necessary PHP5 source files to compile additional modules. Enter the following from a shell prompt:
      sudo apt-get install php5-dev

      If you do not install the php5-dev package and try to install a PECL extension using “pear install”, you will get the following error:

      sh: phpize: not found
      ERROR: `phpize’ failed
    • The PECL_HTTP extension requires an additional dependency package to be installed. You can probably skip this for other extensions:
      sudo apt-get install libcurl3-openssl-dev
    • Now we are finally ready to actually install the extension. From a shell prompt enter following but substitute “pecl_http” with the PECL extension name you are installing:
      sudo pecl install pecl_http

      The installer may ask you about some specific options for the extension you are installing. You can probably just hit enter one or more times to accept all the defaults unless you want to set specific options for your implementation. If all goes well, the module should download, build, and install.

    • Once the install is complete, it will probably ask you to add a “extension=” line to your php.ini file. Open up the php.ini file in your favorite text editor and add the line under the section labeled “Dynamic Extensions”. On Ubuntu the php.ini file seems to be located in the /etc/php5/apache2 folder:
      sudo nano /etc/php5/apache2/php.ini

      In this example, the pecl_http extension install asked me to add “extension=http.so”.

    • Now that the php.ini file has been updated, Apache will need to be restarted so the new extension will be loaded:
      sudo /etc/init.d/apache2 restart

      That should restart Apache on Ubuntu but if that doesn’t work you can try:

      sudo /etc/init.d/httpd restart

    If all went well your PECL extension should now be working. You might want to write a PHP test page that will test the basic functionality of the extension to make sure everything is working OK. You can use this later to check that all your required extensions are installed and working when you deploy to a new server. In this example where I installed the PECL_HTTP module, I might write a PHP page that uses the extension’s HttpRequest class to go get a page and return the results.

    That’s it. For the next extension install you can skip the steps to install the php-pear and php5-dev packages.

  • Book Review: Smart and Gets Things Done: Joel Spolsky’s Concise Guide to Finding the Best Technical Talent

    3 out of 5 stars

    In my previous post I reviewed Joel Spolsky’s Joel on Software:… (I will spare you the full title). In this review I will be talking about one of the followup books to that, Smart and Gets Things Done: Joel Spolsky’s Concise Guide to Finding the Best Technical Talent. There are also a couple other Joel books I have not read yet that are worth noting:

    A lot of the content in Smart and Gets Things Done seems to overlap with Joel’s other books. I understand that most of the books are just a rehash of his blog but I guess I was a little disappointed that there was duplication of content between his books. For about $12 however, this book is still a pretty good value. Particularly if you haven’t already have his other books.

    In this book, Joel explains how to get the best programmers but it seems to lean more towards hiring the best college grads. Joel argues that the best programmer’s are so highly productive it is worth the extra pay and effort to bring them in as opposed to a mediocre programmer. I agree with this to some degree. I am not sure I totally agree with some of his techniques for evaluating who the best programmers are though.

    Joel is an advocate of spending some extra dollars on perks for his interviewees and employees. One example is that he has a uniformed limo service pickup interviewees at the airport. At first this sounds ludicrous but the more I thought about it, the more it started to make sense. If you have gone through the labor of filtering all those resumes and conducting all those phone interviews to find the best candidates to interview in person then perhaps it is worth it to give them the treatment to sell the job. I checked and uniformed limo service from JFK to Manhattan runs less than $150. Considering a typical NYC IT salary, that is a drop in the bucket if it will help land a top notch programmer.

    The book mentions purchasing a $900 chair for employees. I am not sure I could sit in a $900 chair without feeling a like a total snob but a $300 one seems to make sense. A comfortable programmer is likely a productive one. Nothing says you don’t care about your employees like an old, broken, stained, $100 office chair. The other thing the book mentions is giving employees dual monitors and top end computers. I think this is good advise and it probably isn’t expensive as you might think. For example, say you spend less than a $1000 to buy a new office chair, second monitor, dual head video card, and an extra 2GB stick of RAM for each employee and you expect those extras to last three years. That is about $27 a month per employee. I think that is a small price to pay for happy employees that feel valued. If that chair is super comfy you might even make up the cost in overtime work because they won’t be in such a hurry to get out of the chair at the end of the day. 😉

    There are a few things in the book that I disagree with and this might be just because I don’t have enough experience to know better yet. The first is that the book says incentive pay doesn’t work. I disagree. I already talked about this in my review of Joel on Software and I won’t delve into it further here.

    Another item in the book I don’t agree with is the concept that you don’t need an idea to build a software company. I suppose you don’t but it probably helps! I don’t have an MBA but I think that it is good business practice to identify a discrepancy or problem and build a product to fulfill it. Good programmers are great and all but I don’t think “best working conditions” + “best programmers” + “best software” always equals profit. You could build the best software but it won’t be profitable if the market is too small (you need the best sales people to pull that off). Perhaps I just misunderstood the first part of chapter 1.

    One of the suggested interview questions to help separate the wheat from the chaff if is a pointer recursion question. I think it is more important to evaluate the skills that the interviewee claims to have. If they put in their resume that they have experience in a specific language then ask them to write something in that language. An outstanding web developer may never have touched pointers before because they simply never had to. Yes, there are leaky abstraction cases but typically those result in looking something up on Google rather than re-writing a module in C. Also, just because someone understands recursion and pointers doesn’t mean that they will be the best programmer for the job. They might have no understand of object oriented languages because all they have used is a procedural language such as C although admittedly that is less likely in this day and age.

    The book goes on to say that pointers in C is more of an aptitude than a skill. Over the last year I have had a crash course in C/C++ and based on my own experience I argue that it is not that only a few people have an aptitude for pointers. The problem is that pointers are often poorly explained in many references and the syntax is a bit deceiving because the * symbol has a completely different meaning depending on if you are declaring a pointer or using it (“this is a pointer” and then “dereference this pointer”). If 90% of a computer science class isn’t getting something (as mentioned in the book) then I would say the professor should consider a different instruction strategy. Fortunately I had a pretty good instructor.

    Despite some of my misgivings I think this book is worth the money especially if you don’t have any of the Joel on Software books already. There are many helpful tips including where to go looking for candidates, how to help employees feel at home in your organization, and how to turn around an existing team that is on the rocks.

  • Book Review: Joel on Software…

    4 out of 5 stars

    The full book title is actually Joel on Software: And on Diverse and Occasionally Related Matters That Will Prove of Interest to Software Developers, Designers, and Managers, and to Those Who, Whether by Good Fortune or Ill Luck, Work with Them in Some Capacity.

    I felt the title was a bit too long for my blog post title so please excuse abbreviated version. 😉 Other than the title and a few other points which I will cover shortly, I think it is a very good book written by someone who obviously has years of experience in the software industry. The author is Joel Spolsky and the content mostly consists of a series of short essays from his blog at http://www.joelonsoftware.com. Although you can read most of the content on his blog I think it is worth owning the printed book.

    There are some technical sections in the book but it mostly focuses on software development and management in general. This is the kind of advise you would get from an experienced mentor. Of particular interest is the Joel Test. This is a list of 12 yes or no questions regarding things your software team should be doing to produce better code. The more items for which you can answer yes, the higher your team’s score. In my experience, such as it is, this is a pretty good test but there are a couple things I feel that are missing in the case of web application development. More on this shortly.

    Joel talks about five worlds of software development:

    1. Shrinkwrap
    2. Internal
    3. Embedded
    4. Games
    5. Throwaway

    I never really thought about it this way but it makes a lot of sense to do so. Depending on the type of development you are doing you will have wildly different priorities. As an example, Joel points out that embedded software often can never be upgraded so quality requirements are much higher than throwaway software for example, that will only be used once to message an input file. An awareness of the business model you are developing for should help sort your priorities.

    There were a few things in the book I disagreed with. One example is that Joel believes that incentive pay is harmful. I think that managed correctly, it is not. One way to handle this is to simply reward employees privately for going above and beyond. In other words, don’t tell employees you are going to reward them for doing X or you will run into the kind of problems that Joel describes. Instead, just thank them with some reward after the fact and explain that this won’t always be the case so there is no expectation or set formula for them to work around. This will make employees feel more appreciated for going the extra mile and they will likely continue to do so even if there are no guarantee they will be rewarded again. This is kind of a pay it forward incentive.

    I think employee ownership in the company is another good incentive. An individual employee may not have much control over the stock price of the company but it does provide the employee some justification to themselves as to why they should go above and beyond. After all, if you are hiring smart people, they will be smart enough to ask themselves why they are putting in that extra overtime to improve the quality of your product. Unless you are a managing a company that feeds starving children in Africa, smart employees will feel better about working overtime on a weekend if they are benefiting in some remote way and not just making some rich stock holders or owners even richer while their own salary remains flat.

    There are a couple items that I think are missing from the Joel Test that are important for the web application world:

    • Is high availability, reliability, scalability, and performance part of your spec?
    • Do you load test?
    • Do you write automated unit and integration tests?

    High Availability, Reliability, Scalability, and Performance

    I have a little over a year of web application development experience but I have been supporting web applications for well over ten years now and if nothing else I have learned that you need to plan for availability, reliability, scalability and performance:

    • High availability ensures that your web application will be available when your users attempt to access your web application.
    • Reliability specifies how often your web application will work correctly. Just because your application is highly available doesn’t necessarily mean it always works right.
    • Scalability planning ensures that if your site is suddenly Slashdotted you can quickly add capacity or if your user base grows you can shard accounts across multiple database servers. A common bottleneck in web applications is data writes and you can get to a point where no single server will have enough write throughput for a high volume web site. Since this is a bottleneck on the database side, adding application servers is useless. A spec and/or design document needs to include a plan for eventually distributing data across multiple database servers for DB write intensive applications. It is not unheard of for a dot com to re-engineer a good portion of their software base in a big hurry to handle growth. Rushed software updates will likely create disgruntled employees and a poor user experience.
    • Performance indicates how quickly a web application will respond to a request. This should include internet lag time and take in consideration where users will be accessing the site from. If you are going to have a large user base in Australia for a example, it might be a good idea to consider implementing a content delivery network with a point of presence in Australia.

    I think it is important to include very specific numbers for each of these items in the spec because each will strongly determine the level of effort and ultimately cost of a project. As you move closer to 100% availability and reliability, costs will likely go up exponentially for both development time and/or hardware. Scalability planning needs be in the spec so design time can be allocated for it on the project plan. Each incremental improvement will likely require more development time and/or hardware.

    Load Testing

    Load testing is critical to assessing performance bottlenecks in web applications. Your application’s performance may be spectacular with one user but what about 5000 users generating 500 requests a second to your database driven web application? It is good to know before it happens so you can plan accordingly and verify you are actually bottlenecking on over utilized hardware and not a “false bottleneck”. I define a false bottleneck as a situation where none of your server hardware is fully utilized and yet you can’t get any more throughput. This can be caused by a number of things such as a propagation delay, uncompressed images and JavaScript using up all the network bandwidth, or even something like a sleep(2) that someone put in the code for debugging and forgot to remove.

    I believe optimizing your code without load test data can be a time sink. If you are optimizing your code without any data you might spot a Shlemiel the Painter’s Algorithm and then spend six hours fixing it resulting in a few micro seconds of execution time saved. That sounds great but if you had done some load testing and monitored your stats, you might have noticed that table scan on your database server costing you over half a second each transaction that could fixed in 10 minutes with a single create/alter index statement. Load testing helps show your actual bottlenecks versus your perceived ones.

    Automated Unit and Integration Tests

    Automated testing is essential for the same reasons as a daily build… to ensure that no one has broken anything. You can use automated unit and integration testing to do a full regression test of your software every day or even prior to every check-in. Daily builds will just identify if anyone has broken the build while automated testing will tell you if everything stills works like it should.

    Unit tests focus on individual functions or units of code while integration tests verify that your application’s components are all working together per the requirements. A unit test, for example, will tell you if your circleArea() function is returning the correct value for given inputs. An integration test will tell you if data submitted via a form is being stored in the database correctly. Unit tests are good at helping you identify broken units of code at a more granular level while integration tests evaluate the overall functionality of your system as a whole. There are unit testing frameworks to facilitate authoring unit tests for all popular programming languages. In many cases unit testing frameworks can also be effectively implemented to do integration testing as well.

    There is some debate over the line between unit testing and integration testing but I honestly don’t care too much. The ideal goal is that you can execute a single command to do a full regression test of your entire solution. This initially increases your development time but will more than make up for itself over long run if your software is going to be around for a while and have many revisions. I have seen a few projects where fixing one bug introduces another or adding a new feature breaks an existing feature. Automated testing will bring these cases to light quickly before you deploy or get too far down the development cycle.

    Conclusion

    Despite a few things that I disagreed with or omissions I give this book a 4 out of 5 stars. Joel obviously has a lot of experience and I learned a lot. Even if you are an experienced developer you might find some valuable insights from this book.

  • My Geek Christmas

    I think we are done with our various Christmas celebrations and my friends and family over-did for me this year (as usual):

    I also received some gift cards:

    I bought a little something for myself. My soldering irons are getting a bit old and I wanted a nice solder station to build my Mousebot with:

  • Senario NRG MicroFly RC Hovering UFO

    I like RC toys, particularly those that fly, so when I saw the MicroFly I knew I had to have one… and another four as Christmas gifts for my family. I usually embrace the opportunity purchase toys for my younger nephews that make noise, launch projectiles or fly around so this was a rather obvious purchase. I am sure my sister will make sure I pay for this dearly some day. Anyway, the Alien Microfly can be had from Amazon for under $25.

    It has no directional capability so you only have one channel, up and down. Although much of it is made of foam it very durable for its weight. I have bounced it off many walls and it still keeps going. The cats have even tried to eat it a few times without success. (they are still trying to decide if it is predator or prey) It take 6 AA batteries for the IR transmitter and the MicroFly UFO charges off the transmitter in about 15-20 minutes. An even 4 or 8 double AA batteries would have been nice since my AA charger takes 4 batteries at a time but it works.

    Jen was nice enough to shoot a little video where I demonstrate my poor piloting skills.

  • Setting up PostgreSQL on Linux and connecting using pgAdmin III

    In my previous post I covered setting up/installing MySQL and connecting via MySQL Administrator from a remote host. In this post I will talk about the PostgreSQL equivalent.

    As a side note, any database system should always be behind a firewall of some kind. Putting up a database server on a public IP using the default port (5432 for PostgreSQL and 3306 for MySQL) is begging for trouble. Most web applications should only require revealing the web server to the world and even then only ports 80 and 443 (for SSL). Ideally database servers should never see the light of a port scanner.

    Install PostgreSQL server and pgAdmin III

    • To install PostgreSQL on a Ubuntu server use:
      sudo apt-get install postgresql
    • If you are going to be using PostgreSQL with PHP then you will need the PHP module for PostgreSQL:
      sudo apt-get install php5-pgsql
    • Download and install the pgAdmin III Client for the OS you will be connecting from.
    • Start pgAdmin III. Click on the plugPostgreSQL Connect Plugin the upper left hand corner to pull up the “New Server Registration” window. Enter any name you like for “Name”, the IP or DNS name of your PostgreSQL Server for “Host”, “postgres” for the “Username” and nothing for the password.

    If you try to connect from a remote host now without configuring your PostgreSQL server bindings you will likely see something similar to the following error:

    Server doesn’t listen

    The server doesn’t accept connections: the connection library reports

    could not connect to server: Connection refused (0x0000274D/10061) Is the server running on host "192.168.1.100" and accepting TCP/IP connections on port 5432?

    The problem is that by default PostgreSQL does not listen on or bind to any IP addresses (not even localhost). Like MySQL, this was probably done so PostgreSQL is locked down by default which is a good security feature.

    Configure PostgreSQL Connection Settings

    • To configure PostgreSQL to listen on all IP addresses on your server, you will need to modify the “#listen_addresses” line in the /etc/postgresql/8.3/main/postgresql.conf file using your favorite text editor (also look for it in /var/lib/postgresql/data or /var/lib/pgsql/data if you are not using Ubuntu). This file is usually locked down so only someone with root can write to it. To work around this use “sudo” before your text editor command:
      sudo nano /etc/postgresql/8.3/main/postgresql.conf
      or
      sudo vi /etc/postgresql/8.3/main/postgresql.conf

      Change:

      #listen_addresses = ‘localhost’
      to
      listen_addresses = ‘*’
    • Save the file and then restart PostgreSQL Server using whichever of the following methods that works on your distro:
      sudo /etc/init.d/postgresql-8.3 restart
      or
      sudo /sbin/service postgresql restart
      or
      sudo /etc/rc.d/init.d/postgresql restart
      or
      /etc/init.d/postgresql restart

    Now pgAdmin III will make a connection but will likely return the following error:

    Access to database denied
    The server doesn’t grant access to the database: the server reports
    FATAL: no pg_hba.conf entry for host "192.168.1.100", user "postgres", database "postgres", SSL off

    It still doesn’t work but we are a bit closer. PostgreSQL checks that users are connecting from allowable IP addresses or IP address ranges. By default, the “postgres” default user can only connect locally so we will need to fix this.

    Grant users remote access

    • There should be a pg_hba.conf file in the same directory as the postgresql.conf file you edited previously. Open this file in your favorite text editor using sudo:
      sudo nano /etc/postgresql/8.3/main/pg_hba.conf

      Path names vary between Linux distributions so this may take a little hunting.

    • In this example we would like to grant all users access from the 192.168.0.0 subnet so we will add the following line:
      host all all 192.168.0.0/16 md5

      Note that the IP subnet is specified in CIDR notation. 192.168.0.0/16 specifies that any host with an IP address that starts with 192.168 will have access.

    • Before we can connect remotely we will need to be able to connect locally to set a password for the “postgres” user. Change the following line:
      local all postgres md5 sameuser

      To:

      local all postgres trust
    • Comment the following two lines with a “#” character:
      #local all all ident sameuser
      #host all all 127.0.0.1/32 md5
    • Save the file and restart PostgreSQL using one of the methods described above.
    • Use the following command to start the PostgreSQL command line client on the server. Note that psql may also be “postgres” or “pgsql” depending on your distribution/installation:
      psql -U postgres -d template1
    • Now set a new password for the “postgres” account using the server command line client:
      template1=# ALTER USER postgres with encrypted password ‘yourpassword’;
    • Now try connecting to your server using pgAdmin III from your remote host. Use “postgres” for the username and whatever password you chose for the password. If all went well you should be able to connect. This will give you a tree hierarchy menu on the left that allows you to drill down to your databases, groups, login roles, etc.

    Usually it is bad practice to do everything with your default account so you might want to create another user. This is particularly the case if you are going to use it for connecting from PHP. In PostgreSQL, the concept of a user translates to a “Login Role”.

    • From the pgAdmin III client, right click on “Login Roles” and choose “New Login Roles…”. You should now see the “New Login Role…” dialog.
    • Enter a “Role name” (I.e. web_user), a password (twice), check applicable role privileges, and click “OK”. In this case, I put a check next to “Can create database objects” so I could use this account to create databases and tables.
    • You should now have new user. You can test this user by disconnecting from the server in pgAdmin III and then reconnecting using the credentials for your newly created account.

    Create your first database and table

    Now you can use pgAdmin III to create your first database and table.

    • With your server tree open in pgAdmin III right click on “Databases” and choose “New Database…”. Enter a name for your database and click “OK”. You should now see your new database nested under “Databases”.
    • Your databases tables are a bit nested in the pgAdmin III hierarchy. To get there, double click your database, “Schemas”, “public” and then you should see “Tables”.
    • Right click on “Tables” and choose “New Tables…”. Enter your table name on the “New Table…” dialog and then click OK to create your first table.

    That’s it for this post. Good luck!

  • Setting up MySQL on Linux and connecting using MySQL Administrator

    There are a few “gotchas” if you are trying to setup MySQL on Linux server, Ubuntu in this case, and connect to it using MySQL Administrator from somewhere else. Here is what worked for me…

    Install MySQL server and MySQL GUI Tools

    • To install MySQL on a Ubuntu server use:
      sudo apt-get install mysql-server
    • If you are going to be using MySQL with PHP then you will need the PHP module for MySQL 5:
      sudo apt-get install php5-mysql
    • Download and install the MySQL GUI Tools Bundle for the OS you will be connecting from.
    • Start MySQL Administrator. Enter any name you like for “Stored Connection:”, the IP or DNS name of your MySQL Server for “Server Host:”, “root” for the “Username:” and the root password for “Password:”. The default MySQL port is 3306.

    If you try to connect to your server using MySQL Administrator from a remote host without configuring your MySQL server bindings you will likely see something similar to the following error:

    Could not connect to the specified instance.

    MySQL Error Number 2003
    Can’t connect to MySQL server on ‘mysqlhost.example.com'(10061)

    The problem is that by default MySQL server is only bound to the localhost/loopback IP of 127.0.0.1. This was probably done so MySQL is locked down by default which is a good security feature.

    Configure MySQL bindings

    • To bind MySQL to the real IP address on your server, you will need to comment out the “bind-address = 127.0.0.1” line in the /etc/mysql/my.cnf file using your favorite text editor. This file is usually locked down so only someone with root can write to it. To work around this use “sudo” before your text editor command:
      sudo nano /etc/mysql/my.cnf
      or
      sudo vi /etc/mysql/my.cnf

      Change:

      bind-address = 127.0.0.1
      to
      #bind-address = 127.0.0.1
    • Save the file and then restart MySQL Server:
      sudo /etc/init.d/mysql restart

    Now MySQL Administrator will make a connection but will likely return the following error:

    Could not connect to the specified instance.
    MySQL Error Number 1045
    Access denied for user ‘root’@’yourclienthost.example.com’ (using password: YES)

    It still doesn’t work but we are a bit closer. MySQL associates IP addresses and/or DNS names with users. By default, root can only connect from the localhost so we will need to fix this.

    Grant users remote access

    • From your MySQL server enter the following to start up the command line MySQL client:
      mysql -u root -p

      When prompted, enter your root password and then you should see an “mysql>” prompt.

    • Now we need to grant root the ability to connect from the host you want to use MySQL Administrator on. You can do this by DNS or by IP address with ‘%’ as a wildcard. I like to do it by IP so I would use ‘192.168.%.%’. This will allow me to connect from any computer with an IP that starts with 192.168 which is the typical default private IP range used by most broadband routers that geeks have in their homes. Here is an example (replace ‘yourpassword’ with your password):
      mysql> GRANT ALL PRIVILEGES ON *.* TO ‘root’@’192.168.%.%’ IDENTIFIED BY ‘yourpassword’ WITH GRANT OPTION;

      The GRANT statement is granting all permission to the ‘root’ user connecting from the IP range or DNS name you have defined. If you wanted to do this by a DNS name you might do something like:

      mysql> GRANT ALL PRIVILEGES ON *.* TO ‘root’@’%.example.com’ IDENTIFIED BY ‘yourpassword’ WITH GRANT OPTION;
    • Now if everything is configured correctly you should be able to connect using MySQL Administrator.

    All this is well and good but usually it is bad practice to do everything with your root account so you might want to create another user. This is particularly the case if you are going to use it for connecting from PHP.

    • Assuming you are still in the MysQL command line client on your server, here is how you would add a user named ‘web_user’ with a password of ‘yourpassword’ with enough privileges to do nearly everything:
      GRANT ALL PRIVILEGES ON *.* TO ‘web_user’@’192.168.%.%’ IDENTIFIED BY ‘yourpassword’;

      Note the IP range. This is set so that you can connect via this user from MySQL Administrator running on your remote host if you like. Depending on what you are going to do with this user, you may want to restrict the permissions further to a specific database or even specific tables. You can also restrict the user to only read. It is best practice to only give your SQL user the minimum permissions needed to the job.

    Create your first database and table

    Now that you are able to connect with MySQL Administrator you can close out of the command client on the server (“\q” -> Enter) and use MySQL Administrator to create your first database and table.

    • In MySQL Administrator click on “Catalogs” in the menu on the left. This should reveal the default internal databases that MySQL uses for itself. To create your own database right click on the empty area where the current databases are showing and choose “Create New Schema”. Type in the name for your new database and press enter and you should now see your database below the others.
    • To create a table, right click on your newly create database/schema/catalog (or whatever it is called) and click “Create New Table”. From there you can enter a table name, columns, etc. On the “Table Options” tab there is a “Storage Engine” option. MySQL supports several different storage engines that are often trade offs between data integrity and performance. The two engines that are probably most well known are MyISAM and InnoDB. MyISAM sacrifices data integrity features like foreign key constraints for performance. InnoDB sacrifices performance for data integrity features such as FK constraints and ACID compliant transactions. You will need to decide which is best for your application.

    Well that’s it for this post. I hope someone finds it useful.

  • Another geek blog…

    I intend to post my web development notes here for my own reference and in hopes that others might find some useful bits.