<?xml version="1.0" encoding="utf-8" ?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Pete's Points | Peter Lyons
  </title>
  <link href="http://peterlyons.com/problog/feed" rel="self">
  </link>
  <link href="http://peterlyons.com/problog/feed">
  </link><updated>2012-01-31T00:06:04.787Z</updated><id>http://peterlyons.com/problog/feed</id>
  <author>
    <name>Peter Lyons
    </name>
  </author>
  <entry>
    <title>Node Summit
    </title>
    <link href="http://peterlyons.com/problog/2012/01/node-summit">
    </link><updated>2012-01-31T00:06:04.787Z</updated><id>http://localhost:9400/problog/2012/01/node-summit</id>
    <content type="html">&lt;p&gt;So I flew out to San Francisco last week for the &lt;a href=&quot;http://nodesummit.com/&quot;&gt;Node Summit&lt;/a&gt; conference. I wasn't sure exactly what to expect. The first event was the &lt;a href=&quot;http://nodeup.com&quot;&gt;NodeUp&lt;/a&gt; live pod cast at the Bottom of the Hill bar. I was in the Mission immediately prior to that and it was a long walk (after 5.5 hours of walking) to get there. The crowd seemed to be almost all developers and it was surprisingly international. I got to meet some folks I admire from the Internet like &lt;a href=&quot;http://metaduck.com/&quot;&gt;Pedro Teixeira&lt;/a&gt;. Tim Caswell and TJ Holowaychuck (my current programming hero) were both there and Ryan Dahl eventually arrived as well. I learned an interesting thing about myself: even if you take a bar and fill it up with ubernerds, I still feel uncomfortable and count the minutes until I can make a quiet exit.&lt;/p&gt;

&lt;p&gt;Tuesday morning I caught a shuttle from a hotel near my room to the event, which was a really nice service to have (although I guess I would have happily walked as well). The conference was a lot bigger and more extravagant than I was anticipating. Many large companies were represented including Microsoft, Ebay, Yahoo, LinkedIn, Sabre, Visa, Google, and more. The discussion panels were pretty hit or miss (mostly miss I guess), with a few being really interesting. I did a lot of networking and hopefully some of these connections will prove valuable in the near future. I went to both of the &lt;a href=&quot;http://nodejitsu.com&quot;&gt;Nodejitsu&lt;/a&gt; workshops, which were packed beyond capacity.&lt;/p&gt;

&lt;p&gt;Wednesday was more of the same but there was a good panel with Ryan Dahl and Brendan Eich on the future of JavaScript. I asked a question to the panel of Platform as a Service companies a question and the moderator, &lt;a href=&quot;http://blog.jolieodell.com/&quot;&gt;Jolie O'Dell&lt;/a&gt; said &quot;and by the way you're nail polish is bitchin'&quot; as I returned to my seat. :-)&lt;/p&gt;

&lt;p&gt;I didn't manage to find any after parties via the twitters, although I probably would have opted to go home and nap anyway. I did get to hang out with the Nodejitsu crew Thursday evening though, which was fun.&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Build a Consumer App With Node.js
    </title>
    <link href="http://peterlyons.com/problog/2012/01/build-a-consumer-app-with-node-js">
    </link><updated>2012-01-21T17:57:09.771Z</updated><id>http://localhost:9400/problog/2012/01/build-a-consumer-app-with-node-js</id>
    <content type="html">&lt;p&gt;It's time to start &lt;a href=&quot;http://venturebeat.com/2012/01/07/building-consumer-apps-with-node/&quot;&gt;building consumer apps with node&lt;/a&gt;&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Troubleshooting responsive layout with :after
    </title>
    <link href="http://peterlyons.com/problog/2012/01/troubleshooting-media-querie">
    </link><updated>2012-01-11T00:34:06.000Z</updated><id>http://localhost:9400/problog/2012/01/troubleshooting-media-querie</id>
    <content type="html">&lt;p&gt;So I'm working on a new design for my site and using the super-great &lt;a href=&quot;http://learnboost.github.com/stylus/&quot;&gt;stylus&lt;/a&gt; CSS preprocessor along with &lt;a href=&quot;https://gist.github.com/1549029&quot;&gt;this great gist for simple responsive layout&lt;/a&gt; (again with stylus and the &lt;a href=&quot;https://github.com/visionmedia/nib&quot;&gt;nib&lt;/a&gt; library).&lt;/p&gt;

&lt;p&gt;Well, I wanted to see which of my CSS3 media queries were in effect.  Here's a simple way to do it with the :before selector.&lt;/p&gt;

&lt;p&gt;First, put some markup in your HTML near the top, like this (using &lt;a href=&quot;https://github.com/visionmedia/jade&quot;&gt;jade template language&lt;/a&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;body
  .content
    header
      p BUGBUG responsive layout max-width:
      h1
        a(href=&quot;/&quot;) Peter Lyons
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then in your .styl stylesheet, use :after to tell which media query is active.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;////////// Responsive layout //////////
@media screen and (max-width: 960px)
  header
    p
      &amp;:after
        content &quot;960&quot;

@media screen and (max-width: 720px)
  header
    p
      &amp;:after
        content &quot;720&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now load that in your browser and resize the window. The CSS will change the text in the header telling you exactly which rules are firing.  Nice!&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Tips for MongoDB Migrations in Rails
    </title>
    <link href="http://peterlyons.com/problog/2011/12/mongodb-migrations">
    </link><updated>2011-12-27T07:04:50.000Z</updated><id>http://localhost:9400/problog/2011/12/mongodb-migrations</id>
    <content type="html">&lt;p&gt;I wanted to share a quick and easy method for testing Rails migrations when using the MongoDB database.  The flexibility of mongo and ruby makes this pretty straightforward. In this example, we'll be renaming a field in our example &quot;books&quot; collection from &quot;isbn&quot; to &quot;book_number&quot;. This is a pretty common type of migration and once you get the hang of this simple case, more complex migrations follow the same pattern. First, lets generate our timestamped migration script boilerplate.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;rails generate migration rename_isbn_to_book_number
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then we'll edit the file that generated under &lt;code&gt;db/migrate&lt;/code&gt;.  We'll split our code up into several sections as follows.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Some class constants shared by all methods&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;self.up&lt;/code&gt; method that does the forward migration&lt;/li&gt;
&lt;li&gt;A helper method to set up some sample data to test forward migration&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;self.down&lt;/code&gt; method that does the rollback&lt;/li&gt;
&lt;li&gt;A helper method to set up some sample data (if needed) to test rollback&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;First, just define some constants that our methods will use.  We store the mongo update options hash &lt;code&gt;MultiUpdate&lt;/code&gt; for convenience since most migration update operations want upsert false (don't create any new documents), multi true (update all matching documents), and safe true.&lt;/p&gt;

&lt;p&gt;Then we also define a constant for the collection name.  For the real migration, the collection is &quot;books&quot;, but for testing, we'll create a new collection called &quot;test&lt;em&gt;books&lt;/em&gt;migration&quot; as we develop our code.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class RenameIsbnToBookNumber &lt; Mongoid::Migration
  MultiUpdate = {:upsert =&gt; false, :multi =&gt; true, :safe=&gt;true}
  Collection = db.collection(&quot;books&quot;) #Final production code
  Collection = db.collection(&quot;test_books_migration&quot;) #Just for testing on the console
  def up
  end

  def down
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;OK, that's our initial boilerplate.  The next step is to take a backup of our development database if there's any data in there we don't want to accidentally wreck.  Then we start coding a little helper method to populate our test collection with fake documents resembling what we expect to see in production, but only focusing on the fields relevant to the migration. Add this method to your migration class.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def mock_data_for_testing_up
  3.times {|number| Collection.insert({&quot;isbn&quot; =&gt; &quot;#{number}&quot;})}
end

def show_collection
  Collection.find({}).each {|_| puts _}
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will create 3 dummy documents we can use for testing.  We are putting this code in a method so it's easy to re-run as we tweak and test our migration code.  For complex migrations, many rounds of tweaking to get all the edge cases might be needed.&lt;/p&gt;

&lt;p&gt;We can now fire up a rails console and run this code by copying and pasting the 2 class constants and the mock&lt;em&gt;data&lt;/em&gt;for&lt;em&gt;testing&lt;/em&gt;up method into the console and then running mock&lt;em&gt;data&lt;/em&gt;for&lt;em&gt;testing&lt;/em&gt;up&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ bundle exec rails console
Loading development environment (Rails 3.1.1)
irb(main):001:0&gt; #Paste the following into the console
MultiUpdate = {:upsert =&gt; false, :multi =&gt; true, :safe=&gt;true}
Collection = db.collection(&quot;test_books_migration&quot;) #Just for testing on the console
def mock_data_for_testing_up
  3.times {|number| Collection.insert({&quot;isbn&quot; =&gt; &quot;#{number}&quot;})}
  Collection.find({}).each {|_| puts _}
end
def show_collection
  Collection.find({}).each {|_| puts _}
end
irb(main):008:0&gt; mock_data_for_testing_up
mock_data_for_testing_up
MONGODB app_development['test_books_migration'].insert([{&quot;isbn&quot;=&gt;&quot;0&quot;, :_id=&gt;BSON::ObjectId('4ef5f43b2a4397a5d7000001')}])
MONGODB app_development['test_books_migration'].insert([{&quot;isbn&quot;=&gt;&quot;1&quot;, :_id=&gt;BSON::ObjectId('4ef5f43b2a4397a5d7000002')}])
MONGODB app_development['test_books_migration'].insert([{&quot;isbn&quot;=&gt;&quot;2&quot;, :_id=&gt;BSON::ObjectId('4ef5f43b2a4397a5d7000003')}])
irb(main):009:0&gt; show_collection
MONGODB app_development['test_books_migration'].find({})
{&quot;_id&quot;=&gt;BSON::ObjectId('4ef5f3812a4397a5bd000001'), &quot;isbn&quot;=&gt;&quot;0&quot;}
{&quot;_id&quot;=&gt;BSON::ObjectId('4ef5f3812a4397a5bd000002'), &quot;isbn&quot;=&gt;&quot;1&quot;}
{&quot;_id&quot;=&gt;BSON::ObjectId('4ef5f3812a4397a5bd000003'), &quot;isbn&quot;=&gt;&quot;2&quot;}
=&gt; nil
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So now we have a separate, well-understood test collection ready to test our simple migration.  Let's code up our &lt;code&gt;up&lt;/code&gt; method. To do our migration.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def up
  #We want to rename the book.isbn field to book.book_number
  Collection.find({&quot;isbn&quot; =&gt; {&quot;$exists&quot; =&gt; 1}}).each do |book|
    update_op = {
      &quot;$unset&quot; =&gt; {&quot;isbn&quot; =&gt; 1},
      &quot;$set&quot; =&gt; {&quot;book_number&quot; =&gt; book[&quot;isbn&quot;]}
    }
  Collection.update({&quot;_id&quot; =&gt; book[&quot;_id&quot;]}, update_op, MultiUpdate)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can paste that into the console and run it to test our migration.  We can verify the results with &lt;code&gt;show_colletion&lt;/code&gt;.  If we want to test other records for the rollback, we can create a &lt;code&gt;mock_data_for_testing_down&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;This should give you a really quick way to experiment and get your migration code working.  Mongo has some advanced query and modify capabilities that can do amazing things, and an easy way to do some trial and error is handy.  If you make a mess of your test data, you can use &lt;code&gt;Collection.drop&lt;/code&gt; to get a clean slate. Here's the final migration code for reference. &lt;strong&gt;Don't forget&lt;/strong&gt; to remove the test collection constant and drop the test collection from your database when your ready to start running your code for real with &lt;code&gt;rake db:migrate&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class RenameIsbnToBookNumber &lt; Mongoid::Migration
  MultiUpdate = {:upsert =&gt; false, :multi =&gt; true, :safe=&gt;true}
  Collection = db.collection(&quot;books&quot;) #Final production code

  def up
    #We want to rename the book.isbn field to book.book_number
    Collection.find({&quot;isbn&quot; =&gt; {&quot;$exists&quot; =&gt; 1}}).each do |book|
      update_op = {
        &quot;$unset&quot; =&gt; {&quot;isbn&quot; =&gt; 1},
        &quot;$set&quot; =&gt; {&quot;book_number&quot; =&gt; book[&quot;isbn&quot;]}
      }
      Collection.update({&quot;_id&quot; =&gt; book[&quot;_id&quot;]}, update_op, MultiUpdate)
    end
  end

  def down
    #We want to rename the book.book_number field to book.isbn
    Collection.find({&quot;book_number&quot; =&gt; {&quot;$exists&quot; =&gt; 1}}).each do |book|
      update_op = {
        &quot;$unset&quot; =&gt; {&quot;book_number&quot; =&gt; 1},
        &quot;$set&quot; =&gt; {&quot;isbn&quot; =&gt; book[&quot;book_number&quot;]}
      }
      Collection.update({&quot;_id&quot; =&gt; book[&quot;_id&quot;]}, update_op, MultiUpdate)
    end
  end

  #These methods are not called by the migration.  Just for manual testing
  #by copy/pasting into the console
  #To test (by copy/pasting from here to the console)
  #1. Set the MultiUpdate constant. Adjust &quot;Collection&quot; to be a test collection
  #2. Copy/paste the 2 methods below
  #2. Run mock_data_for_testing_up
  #3. Run the body of self.up
  #3b. Optionally run the body of self.up again to make sure it is idempotent
  def mock_data_for_testing_up
    3.times {|number| Collection.insert({&quot;isbn&quot; =&gt; &quot;#{number}&quot;})}
  end

  def show_collection
    Collection.find({}).each {|_| puts _}
  end
end
&lt;/code&gt;&lt;/pre&gt;
    </content>
  </entry>
  <entry>
    <title>No Need for npm -g
    </title>
    <link href="http://peterlyons.com/problog/2011/12/no-need-for-npm-g">
    </link><updated>2011-12-22T21:14:45.000Z</updated><id>http://localhost:9400/problog/2011/12/no-need-for-npm-g</id>
    <content type="html">&lt;p&gt;So &lt;a href=&quot;http://npmjs.org/&quot;&gt;npm&lt;/a&gt; has this &quot;-g&quot; switch to install &quot;global&quot; packages that bundle command line executable scripts.  I've been on a strict project isolation kick lately after dealing with rbenv in the ruby world, and I just don't see any need for &lt;code&gt;npm -g&lt;/code&gt;.  I want each project to have its own version of node, coffeescript, mocha, or whatever else I need.  Here's my principles for a harmonious multi-project system.&lt;/p&gt;

&lt;h2&gt;1. Install things under your project root&lt;/h2&gt;

&lt;p&gt;Node goes in &lt;code&gt;project/node&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Install npm modules without &lt;code&gt;-g&lt;/code&gt;.  &lt;code&gt;coffee&lt;/code&gt; becomes &lt;code&gt;project/node_modules/.bin/coffee&lt;/code&gt;. &lt;code&gt;mocha&lt;/code&gt; becomes &lt;code&gt;project/node_modules/.bin/mocha&lt;/code&gt;. And so on.&lt;/p&gt;

&lt;h2&gt;2. Set your PATH&lt;/h2&gt;

&lt;p&gt;Add &lt;code&gt;./node/bin:./node_modules/.bin&lt;/code&gt; and to your &lt;code&gt;PATH&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Done&lt;/h2&gt;

&lt;p&gt;Here's an example.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ mkdir project1 project2
$ cd project1
$ npm install coffee-script@1.0.1
coffee-script@1.0.1 ./node_modules/coffee-script 
$ which coffee
./node_modules/.bin/coffee
$ coffee --version
CoffeeScript version 1.0.1
$ cd ../project2
$ npm install coffee-script@1.2.0
coffee-script@1.2.0 ./node_modules/coffee-script 
$ which coffee
./node_modules/.bin/coffee
$ coffee --version
CoffeeScript version 1.2.0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Same principle works for &lt;code&gt;node&lt;/code&gt; and any scripts you get from npm modules.&lt;/p&gt;

&lt;p&gt;While we're talking about &lt;code&gt;PATH&lt;/code&gt;, here's how I set my &lt;code&gt;PATH&lt;/code&gt; in my &lt;code&gt;~/.zshrc&lt;/code&gt;.  It's nice because I can throw a bunch of crap in there that may or may not exist on any given computer and only directories that exist get into my &lt;code&gt;PATH&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;########## PATH ##########
PATH=
addPath() {
  if [ -d &quot;${1}&quot; ]; then
    export PATH=$PATH:&quot;${1}&quot;
  fi
}
addPath ./node_modules/.bin
addPath ./node/bin
#Repeat addPath lines for each directory...
export PATH
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Security Caveat About Relative Paths in PATH&lt;/h2&gt;

&lt;p&gt;Having relative directory paths in your &lt;code&gt;PATH&lt;/code&gt; is arguably a &lt;a href=&quot;http://developer.apple.com/library/mac/#documentation/opensource/conceptual/shellscripting/ShellScriptSecurity/ShellScriptSecurity.html&quot;&gt;security vulnerability&lt;/a&gt;. &lt;a href=&quot;https://www.securecoding.cert.org/confluence/pages/worddav/preview.action?pageId=3524&amp;fileName=Environment+Variables+v3.pdf&quot;&gt;See also slide 20 here&lt;/a&gt;.  I'm not personally too concerned about this one for my personal interactive login shell.  However, this practice is probably not suitable for shell scripts that are run as programs.  Also note that in a production deployment you should launch your &lt;code&gt;node&lt;/code&gt; or &lt;code&gt;coffee&lt;/code&gt; executable via an absolute path when coding your &lt;a href=&quot;http://upstart.ubuntu.com/&quot;&gt;upstart&lt;/a&gt; or SysV init script.&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>The destiny of complex languages
    </title>
    <link href="http://peterlyons.com/problog/2011/12/complex-language">
    </link><updated>2011-12-04T01:33:01.000Z</updated><id>http://localhost:9400/problog/2011/12/complex-language</id>
    <content type="html">&lt;p&gt;I had an interesting thought about programming languages and complexity yesterday.  I think of languages like Python and CoffeeScript (and even C &lt;sup&gt;&lt;a href=&quot;#endnote1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; aim to be small and consistent as a language.  Things like Perl and Ruby are much more complex.  I have been studying both Ruby and CoffeeScript for a while  (I think about a year for Ruby and maybe 6 months for CoffeeScript guessing from unreliable memory), and I feel like my JavaScript/CoffeeScript knowledge is solid and with Ruby I'm mired in intermediate status. However, the fact is I write lots of code in each of these languages.  So what does that indicate?&lt;/p&gt;

&lt;p&gt;It seems to me that the more complex your language, the larger portion of the total extant codebase written in that language will have been authored by programmers that do not completely understand the language.  I would venture that if you took all existing Perl code, about 80% of it was written by programmers or sysadmins with cursory knowledge of the language.  And within the Perl community, finding truly competent programmers with solid mastery of the language is definitely the exception, not the rule.  I think the combination of ruby's largeness and complexity coupled with the huge popularity of rails means the same is true for Ruby.  But I don't think there's anything about Perl or Ruby that makes it this way other than just it's the nature of having complexity in your language.&lt;/p&gt;

&lt;p&gt;I'm solidly in the camp of tiny, simple languages with programs authored by programmers with mastery, and this post has been interesting food for thought for me on this topic.&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;&lt;aside id=&quot;endnote1&quot;&gt;
1: End Note on C&lt;/aside&gt;&lt;/p&gt;

&lt;p&gt;While C as a language is usually considered small and simple, the POSIX interfaces you need are so old and devilishly tricky that I wouldn't trust C code written by beginner or intermediate C programmers.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Hands off my window title!
    </title>
    <link href="http://peterlyons.com/problog/2011/11/hands-off-my-window-title">
    </link><updated>2011-11-29T12:23:46.000Z</updated><id>http://localhost:9400/problog/2011/11/hands-off-my-window-title</id>
    <content type="html">&lt;p&gt;So I recently ssh'ed into a shiny new Ubuntu 11.10 server on EC2 and noticed some new things.  Firstly, Ubuntu seems to have enabled the &lt;a href=&quot;https://help.ubuntu.com/11.10/serverguide/C/byobu.html&quot;&gt;byobu&lt;/a&gt; terminal multiplexer configuration by default.  This looks potentially handy, but just like &lt;a href=&quot;https://github.com/robbyrussell/oh-my-zsh&quot;&gt;Oh my zsh&lt;/a&gt; I don't feel motivated to futz with it just now.  It's easy to disable byobu with just a quick &lt;code&gt;byobu-disable&lt;/code&gt;, which will, somewhat surprisingly, fully exit your shell, log you out and close your ssh session.  But next time you'll get a normal shell instead of a byobu/screen session.&lt;/p&gt;

&lt;p&gt;Now one thing I noticed and finally got motivated with to &quot;research&quot; (and by that I mean ask smarter people on twitter) and fix was that when I connected via ssh, the window title in my iTerm2 tab was dynamically changed from what I had carefully set it to (&quot;asset pipeline&quot;) to the supremely unhelpful &quot;ubuntu@ip-10-11-12-13&quot;.  In some non-cloud situations where the server has a meaningful hostname, this might be handy, but distinguishing dozens of servers by their internal EC2 IP is not appealing to me.  Initial research suggested my &lt;code&gt;PROMPT&lt;/code&gt; or &lt;code&gt;PROMPT_COMMAND&lt;/code&gt; environment variables, but neither was set.  I read through &lt;a href=&quot;http://www.ibm.com/developerworks/linux/library/l-tip-prompt/&quot;&gt;this DeveloperWorks article&lt;/a&gt; and found the responsible code in the &lt;code&gt;~/.bashrc&lt;/code&gt; file.  Here's the offending excerpt.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# If this is an xterm set the title to user@host:dir
case &quot;$TERM&quot; in
xterm*|rxvt*)
    PS1=&quot;\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1&quot;
    ;;
*)
    ;;
esac
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So you can comment out that line that changes &lt;code&gt;PS1&lt;/code&gt; if you prefer your own manually-set window title.&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Linkzie gets a stack upgrade
    </title>
    <link href="http://peterlyons.com/problog/2011/11/linkzie-gets-a-stack-upgrade">
    </link><updated>2011-11-24T08:17:08.000Z</updated><id>http://localhost:9400/problog/2011/11/linkzie-gets-a-stack-upgrade</id>
    <content type="html">&lt;p&gt;So &lt;a href=&quot;https://linkzie.com&quot;&gt;Linkzie&lt;/a&gt; has had a nice series of updates to the underlying software that runs it.  It's now pretty much bleeding edge latest and greatest.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ubuntu 11.10&lt;/li&gt;
&lt;li&gt;rbenv&lt;/li&gt;
&lt;li&gt;Ruby 1.9.3-p0&lt;/li&gt;
&lt;li&gt;Rails 3.1.3&lt;/li&gt;
&lt;li&gt;Unicorn 4.1.1&lt;/li&gt;
&lt;li&gt;jQuery 1.7&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It was an interesting process.  I just went through a pretty challenging upgrade from Ruby 1.8.7 to 1.9.3 at work and it was pretty destabilizing and chaotic.  So for this one I went slow and steady, changing one thing at a time, and keeping the app running and tests passing.  First I left ruby at 1.8.7 and switched from the Ubuntu ruby to rbenv without changing anything else.  Then I upgraded some of the supporting gems like Capistrano without messing with the key gems (rails).  Then I upgraded to Rails 3.1.1 and got that working.  Then I did a carte blanche bundle update and pulled everything else up to latest.  Only then did I switch to ruby 1.9.3.&lt;/p&gt;

&lt;p&gt;Ruby 1.9.3 still has bugs in the debugger, which I find a bit shocking, but I'm limping along with &quot;pry&quot; as a poor substitute.  I'm a bit shocked by the issues with Ruby 1.9.2 and 1.9.3 given how old the project is.  I don't remember any actual bugs going from Python 2.4 all the way up to 2.7, just the normal code evolution stuff.&lt;/p&gt;

&lt;p&gt;In any case, if Linkzie had any users, in theory it might be loading and responding faster for them.  I learned a lot in the process and got my staging server VM to very closely mirror production, which is good.&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Thoughts on launching and scaling quickly
    </title>
    <link href="http://peterlyons.com/problog/2011/10/thoughts-on-launching-and-scaling-quickly">
    </link><updated>2011-10-24T22:10:40.000Z</updated><id>http://localhost:9400/problog/2011/10/thoughts-on-launching-and-scaling-quickly</id>
    <content type="html">&lt;p&gt;I have &lt;a href=&quot;http://dojo4.com/blog/thoughts-on-launching-and-scaling-quickly&quot;&gt;new blog post&lt;/a&gt; over on the &lt;a href=&quot;http://dojo4.com&quot;&gt;Dojo4&lt;/a&gt; web site.&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>New Linkzie Release: Nice UI improvements
    </title>
    <link href="http://peterlyons.com/problog/2011/07/linkzie-ui-improvements">
    </link><updated>2011-07-30T21:09:10.000Z</updated><id>http://localhost:9400/problog/2011/07/linkzie-ui-improvements</id>
    <content type="html">&lt;p&gt;I just pushed out a new release of &lt;a href=&quot;&quot;&gt;https://linkzie.com&lt;/a&gt;.  There is no longer any need to have an &quot;Organize your links&quot; button.  You can always organize you categories by dragging them around by their name.  You can always organize your links by dragging them by a little crosshairs icon that is displayed to the right of each link when you mouse over it.  The commands to edit or delete items are now in a little pop-up context menu to the right of each link and they are more intuitive to use.&lt;/p&gt;

&lt;p&gt;I think this UI makes Linkzie very easy to use and the code is much simpler now and the UI should respond more snappily.  Try it out and let me know what you think!&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Web Framework Woes
    </title>
    <link href="http://peterlyons.com/problog/2011/07/web-framework-woes">
    </link><updated>2011-07-05T23:10:46.000Z</updated><id>http://localhost:9400/problog/2011/07/web-framework-woes</id>
    <content type="html">&lt;p&gt;
So my attitude for most of this year has been one of reserving judgement in favor of direct experience.  As part of this, I have been trying all the latest &quot;Kool-Aid&quot; web development technologies even if at first glance they didn't sit right with me.  So I started building the &lt;a href=&quot;http://othenticate.com&quot;&gt;Othenticate&lt;/a&gt; infrastructure on a shiny stack of mongodb, node.js, express, mongoose, jade, and stylus.  For the most part, I love this stack.  However, after I while I have come to the conclusion that for a micropreneur project such as mine, traditional RDBMS (PostgreSQL) is going to be more appropriate than dealing with the trade-offs that NoSQL is making.  I won't need to scale to millions of objects, and I want ACID, a schema, and mature tools at the data layer.  So I've decided I want to use PostgreSQL instead of MongoDB.  However, along with this came the realization that it seems that there is no mature Object-Relational Mapper (ORM) on the node stack for PostgreSQL.  There also does not seem to be a mature schema migration framework.  Basically all of the other elements of the node stack are fantastic to work with, but these two might just be deal breakers for me.
&lt;/p&gt;
&lt;p&gt;
So I started thinking along the lines of another stack I might use if I wanted a good PostgreSQL ORM and schema migrator.  There seem to be two obvious candidates: Django and Rails, and maybe some smaller ones and probably the option of cobbling together a hybrid stack of python components.  In contemplating learning another web development framework (Django) or doing another Rails project, I started doing some analysis.  Below are some of my thoughts on these stacks.
&lt;/p&gt;
&lt;p&gt;
First, Node.js and friends. I've been coding in node.js/express for a while and for the most part finding it fantastic.  The frameworks are cleanly designed and easy to understand and work with.  The environment is in general pretty straightforward (compared to rails).  Node starts up instantly and has fantastic debugging support that work seamlessly with chrome's debugger via the node-inspector module.  I can graphically debug the client side code in the browser, the server side code, my jasmine tests, even my server side node.js command line unit tests can be debugged in chrome.  I LOVE the fact that I can re-use my model classes across the browser and server. The only real pain points are the lack of ORM, schema migrator, and the hassle of async callbacks (which is mild but it's still a hassle).  Jade for templating with stylus for CSS is almost perfect.
&lt;/p&gt;
&lt;p&gt;
I've also been working in rails for a while now.  Overall  the node/express environment feels like a better match for me.  Ruby has been influenced by Perl and Perl is utter anathema to me.  The fact that  Rails is written in Ruby is pretty much a kiss of death for me.  However, I do love ActiveRecord and the rails schema migration subsystem.  But that's about it.  Rails templating has decent choices although Jade is the clear king in my mind.  I don't care much for the Rails controller model and routing mechanism either.  But perhaps the paramount rails stopping point for me is just the way the community operates.  It's a mess of gems and fads and hipsterism and uncontrolled software erosion.  The documentation can't keep up with it so you end up in a sea of partially-clueful blog posts.  My overall reaction to this just ends up being &quot;this doesn't feel right&quot;.
&lt;/p&gt;
&lt;p&gt;
So I've started to give serious consideration to Django.  I do like python quite a lot. I was hoping Ruby would be the next major language I work in extensively, but I don't think I'm going to go down that path much farther. Working with CoffeeScript has made python feel much less elegant to me, but I can deal with it.  I hear good things about the South subsystem in Django and the docs look good.  I really don't want to recode my whole current effort though.  So these days I'm torn.  Here's a summary of my reactions in bullets.
&lt;/p&gt;
&lt;h3&gt;Node.js/Express/Etc - the good&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Love CoffeeScript&lt;/li&gt;
&lt;li&gt;Express/Connect are elegant. They are libraries you use, not a framework you must inject your code into&lt;/li&gt;
&lt;li&gt;Jade and Stylus are rapidly approaching Nirvana status&lt;/li&gt;
&lt;li&gt;Debugging support is top-notch&lt;/li&gt;
&lt;li&gt;The functional programming sits pretty well with me&lt;/li&gt;
&lt;li&gt;High concurrency through solid design is pretty sweet&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Node.js/Express/Etc - the bad&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;No good ORM for PostgreSQL&lt;/li&gt;
&lt;li&gt;No good schema migrator for PostgreSQL&lt;/li&gt;
&lt;li&gt;Event model means stack traces are often useless&lt;/li&gt;
&lt;li&gt;No intelligent reloading of changed code on the fly&lt;/li&gt;
&lt;li&gt;The async stuff, while manageable, is clearly a hinderance compared to synchronous code.  It does force you to be a little more considered than you might otherwise be, though&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Rails - the good&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;ActiveRecord is awesome&lt;/li&gt;
  &lt;li&gt;Good schema migrator&lt;/li&gt;
  &lt;li&gt;Lots of handy gems&lt;/li&gt;
  &lt;li&gt;Culture of thorough testing&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Rails - the bad&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Ruby is a kitchen sink language&lt;/li&gt;
  &lt;li&gt;Not quite optimized for modern web apps&lt;/li&gt;
  &lt;li&gt;Docs and blog culture don't suite me&lt;/li&gt;
  &lt;li&gt;A big freaking mess with ruby versions, RVM, gems, bundles&lt;/li&gt;
  &lt;li&gt;Not javascript means code duplication&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Django - the predicted good&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;South/ORM/Schema are supposedly very good&lt;/li&gt;
  &lt;li&gt;Python&lt;/li&gt;
  &lt;li&gt;Docs look good&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Django - the predicted bad&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;The python community has a long history of web frameworks that don't catch on.  Django seems to be the winner, but I am still skeptical&lt;/li&gt;
  &lt;li&gt;Not JavaScript means code duplication&lt;/li&gt;
  &lt;li&gt;Seems like there might not be any intelligent autoreloading of code during development&lt;/li&gt;
 &lt;/ul&gt;

    </content>
  </entry>
  <entry>
    <title>Linkzie 0.7.1 released
    </title>
    <link href="http://peterlyons.com/problog/2011/07/linkzie-0-7-1-released">
    </link><updated>2011-07-05T07:04:28.000Z</updated><id>http://localhost:9400/problog/2011/07/linkzie-0-7-1-released</id>
    <content type="html">&lt;p&gt;
So after a fairly lengthy period of inattention to my &lt;a href=&quot;https://linkzie.com&quot;&gt;Linkzie&lt;/a&gt; bookmark manager, I got frustrated on my current project and turned my focus to Linkzie today for some updates.  There's a new menu bar shamelessly stolen from the new google bar.  A brand new &quot;beach house&quot; color theme has been applied.  The &quot;organize&quot; UI has been changed significantly.  Lots of other minor UX tweaks have been made. For example, you can now click anywhere on the entire row for a link as opposed to before where you had to click on the actual link text itself. Perhaps the biggest improvement is session expiration detection.  Since linkzie is designed to sit open in a browser tab for long periods of time, it was possible for a user's server side session to expire with the window open.  The UI would let them attempt to make changes, only to fail with a login required situation when trying to save their changes.  This resulted in lost work and an error message popping up.  Now linkzie will poll the server periodically which will likely just keep your session alive longer, but if it does expire, the UI is updated to reflect that.  You can still use your links, but all editing operations are disabled and you are notified to sign in again if you need to make any edits.
&lt;/p&gt;
&lt;p&gt;Have a look and let me know what you think!&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Commander: Utility Automation
    </title>
    <link href="http://peterlyons.com/problog/2011/07/commander">
    </link><updated>2011-07-02T04:24:54.000Z</updated><id>http://localhost:9400/problog/2011/07/commander</id>
    <content type="html">&lt;p&gt;
Check out my new little automation utility belt app for the keyboard-centric power users.  It is hosted on a &lt;a href=&quot;https://github.com/focusaurus/commander&quot;&gt;github project here&lt;/a&gt;.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>MicroConf
    </title>
    <link href="http://peterlyons.com/problog/2011/06/microconf">
    </link><updated>2011-06-05T09:11:46.000Z</updated><id>http://localhost:9400/problog/2011/06/microconf</id>
    <content type="html">&lt;p&gt;
Just a quick note that I'm off to &lt;a href=&quot;http://www.microconf.com&quot;&gt;MicroConf&lt;/a&gt; in Las Vegas tomorrow.  A buddy of mine from Denver Hack Nite is also going.  It should be a really fun time and hopefully I'll learn a lot.  I've been coding away like a madman (an excruciating 4-6 hours a day!) preparing a little proof-of-concept for the startup idea I'm working on.  Can't wait to have it scutinized and hopefully not completely blasted to bits.  I'll be blogging and &lt;a href=&quot;https://twitter.com/#!/focusaurus&quot;&gt;tweeting&lt;/a&gt; if there's anything interesting and I'm not too busy concentrating.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Leveling Up: Career Advancement for Software Developers
    </title>
    <link href="http://peterlyons.com/problog/2011/05/leveling-up">
    </link><updated>2011-05-06T00:56:18.000Z</updated><id>http://localhost:9400/problog/2011/05/leveling-up</id>
    <content type="html">&lt;p&gt;
I have written a piece to help software developers accelerate their career.  It lives on its own page, &lt;a href=&quot;/leveling_up&quot;&gt;over here&lt;/a&gt;, but please share your comments on this blog post. There are some other comments &lt;a href=&quot;http://news.ycombinator.com/item?id=2518519&quot;&gt;on Hacker News&lt;/a&gt; as well.
&lt;/p&gt;

    </content>
  </entry>
  <entry>
    <title>Great Design: Fat Brain Toys Order Confirmation
    </title>
    <link href="http://peterlyons.com/problog/2011/05/great-design-fat-brain-toys-order-confirmation">
    </link><updated>2011-05-02T23:04:19.000Z</updated><id>http://localhost:9400/problog/2011/05/great-design-fat-brain-toys-order-confirmation</id>
    <content type="html">&lt;p&gt;
&lt;a href=&quot;http://www.fatbraintoys.com/&quot;&gt;Fat Brain Toys&lt;/a&gt; has the best-looking order confirmation page I have seen. Click the image for a larger version.
&lt;/p&gt;
&lt;a href=&quot;/problog/images/2011/fat_brain_confirmation_redacted.png&quot;&gt;&lt;img src=&quot;/problog/images/2011/fat_brain_confirmation_redacted_small.png&quot; alt=&quot;Fat Brain Toys order confirmation&quot;&gt;&lt;/a&gt;
    </content>
  </entry>
  <entry>
    <title>Good node.js things are happenning
    </title>
    <link href="http://peterlyons.com/problog/2011/04/good-node-js-things">
    </link><updated>2011-04-13T10:38:39.000Z</updated><id>http://localhost:9400/problog/2011/04/good-node-js-things</id>
    <content type="html">&lt;p&gt;
I hope to post some details on this soon, but just a note that my ongoing node.js hacking is turning up some real gems.  I migrated my application level tests from Zombie to Phantom with great results.  phantom.js is pretty rad.  Also got remote graphical debugging in the browser working and learned how to properly drop in a REPL for quick and dirty investigations, which is cool.  Maybe on Thursday I'll have time to post some of the details, but at this point my node.js stack is pretty fierce.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Node docs vs. Rails docs
    </title>
    <link href="http://peterlyons.com/problog/2011/04/node-docs-vs-rails-docs">
    </link><updated>2011-04-05T21:15:51.000Z</updated><id>http://localhost:9400/problog/2011/04/node-docs-vs-rails-docs</id>
    <content type="html">&lt;p&gt;
I've been working with &lt;a href=&quot;http://nodejs.org&quot;&gt;Node.js&lt;/a&gt; a lot recently and enjoying it.  However, I'm not sure entirely WHY I am enjoying it.  I'm writing it in &lt;a href=&quot;http://jashkenas.github.com/coffee-script/&quot;&gt;CoffeeScript&lt;/a&gt;, which is pleasant enough, but it doesn't change the fact that javascript is lacking is basic language/library features like a Set object or hashes with non-string keys and so forth.  The other day I realized part of my good feelings about node are from my experience with the docs, which normally goes as follows.
&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Each project has a single authoritative home page
    &lt;ul&gt;&lt;li&gt;often the main github repo or a unique domain that just links to github&lt;/li&gt;&lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;The docs are usually a 1-pager that explains all you need to get started&lt;/li&gt;
  &lt;li&gt;The examples are clear enough&lt;/li&gt;
  &lt;li&gt;That's all it takes. You can get up and running with that.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
I really enjoy that aspect of this.  Case studies include &lt;a href=&quot;http://documentcloud.github.com/underscore/&quot;&gt;underscore&lt;/a&gt;, &lt;a href=&quot;http://documentcloud.github.com/backbone/&quot;&gt;backbone&lt;/a&gt;, &lt;a href=&quot;https://github.com/visionmedia/jade&quot;&gt;jade&lt;/a&gt;, &lt;a href=&quot;http://jashkenas.github.com/coffee-script/&quot;&gt;coffeescript&lt;/a&gt;, and &lt;a href=&quot;http://expressjs.com/guide.html&quot;&gt;express&lt;/a&gt;.  Also, the docs are the right length (compared to rails).  They aren't one-off blog posts on how to do X, nor are they a book-length comprehensive tutorial that builds an app from scratch.  They are both full API references and guides coupled with enough tiny snippets to get you going.
&lt;/p&gt;
&lt;p&gt;
Now rails on the other hand, and this might be my own fault, seems to be problematic here.  For one, the gems you need often don't have enough documentation to get up and running on their github project.  You are forced out into the harsh unpleasant jungle of umpteen out of date, contradictory or incorrect blog posts.  One nice thing about having the main docs on github (in node land) is that all the info there is guaranteed to be current and the outdated stuff is not there (but it is still in the git history if you need it).  The problem with information rot on rails related blog posts and screencasts is really annoying.  Rails DOES have nice docs on the other end of the spectrum, the comprehensive gigantic tutorial, but for whatever reason these days I never read those end to end and I usually just get myself into trouble with impatient skimming.  If I'm going to spend hours and hours reading something, I probably still prefer to get a physical book than read a gigantic web based tutorial.
&lt;/p&gt;
&lt;p&gt;
This is just one taste of the different experience working with these two technologies.  I still think I'll  continue to view rails as basically the top web development framework out there (unless I try Django, maybe, but I learned rails first to force me to learn ruby), but there are definitely aspects of the rails ecosystem that rub me the wrong way.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Baby steps in node.js
    </title>
    <link href="http://peterlyons.com/problog/2011/03/baby-steps-in-node-js">
    </link><updated>2011-03-24T00:51:22.000Z</updated><id>http://localhost:9400/problog/2011/03/baby-steps-in-node-js</id>
    <content type="html">&lt;p&gt;
So I've been working on some toy projects in &lt;a href=&quot;http://nodejs.org/&quot;&gt;node.js&lt;/a&gt; for a few weeks.  Node is pretty hot in the tech media these days (see &lt;a href=&quot;http://www.theregister.co.uk/2011/03/01/the_rise_and_rise_of_node_dot_js/&quot;&gt;this article&lt;/a&gt;, among many others), and I've been having fun learning it and opening my brain to the world of async and callbacks.  This post will just note a handful of the interesting things I've found thus far.
&lt;/p&gt;
&lt;p&gt;
OK, so one of the first things I got enticed by was &lt;a href=&quot;http://jashkenas.github.com/coffee-script/&quot;&gt;CoffeeScript&lt;/a&gt; by Jeremy Ashkenas.  It's a pretty little javascript dialect and does a good job of taking the &lt;a href=&quot;http://oreilly.com/catalog/9780596517748&quot;&gt;JavaScript: The Good Parts&lt;/a&gt; book, smashing it together with python indentation aesthetics, and making a language out of it.  It all just compiles to javascript though, so there's no deep magic here.  Most node.js libraries and utilities can directly work with .coffee source files without needing a manual intermediate step of running &lt;code&gt;coffee --compile&lt;/code&gt; to get from .coffee to .js source code, which is key for me.  There is also &lt;code&gt;coffee --compile --watch mydir&lt;/code&gt; which will use FSevents to watch all .coffee files under the mydir directory and regenerate the corresponding javascript instantly every time you save them.  Currently, the only thing in my stack that actually needs the .js versions are my jasmine in-browser tests.
&lt;/p&gt;
&lt;p&gt;
Next I stumbled upon the CSS compiler &lt;a href=&quot;http://learnboost.github.com/stylus/&quot;&gt;stylus&lt;/a&gt;, which is fantastic.  I also looked at &lt;a href=&quot;http://sass-lang.com/&quot;&gt;SASS/SCSS&lt;/a&gt;, but there's no good and complete node.js implementation at this time as well as &lt;a href=&quot;http://lesscss.org/&quot;&gt;less CSS&lt;/a&gt;.  All of these are great and beyond adequate.  I ended up choosing stylus because it had a utility to convert .css files to .styl files automatically, makes all of the extra CSS syntax optional, meaning it's easy to start with .css and gradually refactor to .styl, the command line utility is nice, and it works great with node.js and express.js. Again, any one of these will do just fine and improve greatly upon CSS.  It seems at this point the CSS problem is pretty much solved and I don't have to spend much energy on it, which is great.
&lt;/p&gt;
&lt;p&gt;
Next of course we need some templates.  This is where most of the variation and choice seems to exist.  I looked at several.  I started with &lt;a href=&quot;https://github.com/mauricemach/coffeekup#readme&quot;&gt;CoffeeKup&lt;/a&gt; because I was enamored with CoffeeScript and wanted to have my entire codebase in CoffeeScript.  I eventually started to find all those &quot;-&quot; characters cumbersome though, as well as one bug that slowed my development.  So I switched to &lt;a href=&quot;https://github.com/visionmedia/jade&quot;&gt;Jade&lt;/a&gt;, which improves upon &lt;a href=&quot;http://haml-lang.com/&quot;&gt;HAML&lt;/a&gt;.  Jade is so far pretty good and very minimal.  It's not perfect, but it's really pretty and I'm liking it.  I also thought about &lt;a href=&quot;https://github.com/caolan/weld&quot;&gt;Weld&lt;/a&gt;, which looks very cool but I haven't tried it yet.  I &lt;a href=&quot;http://www.yuiblog.com/blog/2010/09/29/video-glass-node/&quot;&gt;watched this video on using server side YUI&lt;/a&gt; and honestly this looks like the shizzle.  I've done some experimenting with jsdom and jquery but haven't gotten huge traction yet.  I really like the idea of rendering the basic document and then after the fact doing some jquery-style changes before sending to the client.
&lt;/p&gt;
&lt;p&gt;
On the middleware side I'm using &lt;a href=&quot;http://expressjs.com/guide.html&quot;&gt;Express&lt;/a&gt; which seems to be widely adopted.  It works great with no fuss and no muss (so far, but my apps are still microscopic).  For testing, it's &lt;a href=&quot;https://github.com/mhevery/jasmine-node&quot;&gt;jasmine-node&lt;/a&gt; on the server and &lt;a href=&quot;http://pivotal.github.com/jasmine/&quot;&gt;jasmine&lt;/a&gt; in the browser.  The &lt;a href=&quot;https://github.com/noblesamurai/jasbin&quot;&gt;jasbin&lt;/a&gt; command line utility is handy for this.  I've been using &lt;a href=&quot;http://zombie.labnotes.org/&quot;&gt;zombie.js&lt;/a&gt; for application level testing with good success.
&lt;/p&gt;
&lt;p&gt;
Today I did my first real async work on the server side.  I used &lt;a href=&quot;https://github.com/caolan/async&quot;&gt;async&lt;/a&gt; to help with this successfully.  The results are great, but not stellar.  The code is still a bit foreign in its layout but overall I'm grokking the async world sufficiently to make forward progress. &lt;a href=&quot;http://documentcloud.github.com/underscore/&quot;&gt;underscore.js&lt;/a&gt; has some handy utilities, but this is where coffeescript/javascript falls short.  Consider taking a list of photo objects and extracting a list of captions from that in these languages:
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
#Javascript + underscore.js
captions = _.pluck(photos, &quot;caption&quot;)
&lt;/pre&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
#CoffeeScript
captions = photo.caption for photo in photos
&lt;/pre&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
#python
captions = [photo.caption for photo in photos]
&lt;/pre&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
#Ruby, better than javascript but not as clean as python
#The block mechanism is more generically powerful though
captions = photos.collect {|photo| photo.caption}
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
I've written my business logic in &lt;a href=&quot;http://documentcloud.github.com/backbone/&quot;&gt;backbone.js&lt;/a&gt; and so far so good.  After some head scratching and namespace tweaking, I am able to run the exact same model code on the browser and in node on the server, which is sort of the holy grail that this whole effort was seeking.  Hurray for only coding data validation and error messages once.  So far so good, but again I'm still at the baby steps stage of this.
&lt;/p&gt;
&lt;p&gt;
Overall my attitude about the node.js ecosystem is highly optimistic.  I hope in the next year or two it gets the kind of mainstream support that rails has.  Expect some more posts with more tech details sometime soon as my experiments move into the more complex guts of the applications.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Show HN: My bookmarking app
    </title>
    <link href="http://peterlyons.com/problog/2011/03/show-hn-my-bookmarking-app">
    </link><updated>2011-03-17T10:21:11.000Z</updated><id>http://localhost:9400/problog/2011/03/show-hn-my-bookmarking-app</id>
    <content type="html">&lt;p&gt;
So the &lt;a href=&quot;http://ycombinator.com/newsguidelines.html&quot;&gt;Hacker News Guidelines&lt;/a&gt; tell me when submitting something I should write a blog post about it and link to that.  OK, here it is.  I'd like to get feedback from the HN community about my simple but effective bookmarking web application: &lt;a href=&quot;https://linkzie.com&quot;&gt;Linkzie&lt;/a&gt;.  I built it mostly during nights/weekends while holding down a day job doing enterprise software.  I know there are a lot of apps out there for this problem, especially in the recent months following the delicious.com leaked &quot;sunset&quot; presentation.  I missed that boat to announce the app, but anyway here it is.  I think the interface is very clean and straightforward and the functionality is simple.  There's nothing here that can't be explained in a handful of words.  You put in your URLs, it lets you lay them out on one big page.  There's drag and drop organization and in-place editing.  You can try the app using sample data right on the home page without needing to sign in (click no the &quot;Next&quot; link in the tour to see the edit mode).
&lt;/p&gt;
&lt;p&gt;
The app was written mostly as an &quot;organic&quot; project since I have been maintaining a home page of links using a little python script to generate static HTML since 2003 or thereabouts.  I wanted to learn modern web development (spent 2004-2010 in back end enterprise software) with rails and jquery.  My initial thought was this space was completely saturated but the success of Pinboard has made me rethink that assumption.  Linkzie doesn't have any whiz-bang fancy features currently, but maybe the spacial layout and clean design will appeal to some users.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>My Lifestyle Business Hero
    </title>
    <link href="http://peterlyons.com/problog/2011/03/music-teachers-helper">
    </link><updated>2011-03-10T23:56:12.000Z</updated><id>http://localhost:9400/problog/2011/03/music-teachers-helper</id>
    <content type="html">&lt;p&gt;
Brandon Pierce of &lt;a href=&quot;http://musicteachershelper.com&quot;&gt;Music Teacher's Helper&lt;/a&gt; has what looks to me like the ultimate lifestyle business.  &lt;a href=&quot;http://www.fourhourworkweek.com/blog/2011/03/04/engineering-a-%E2%80%9Cmuse%E2%80%9D-%E2%80%93-volume-3-case-studies-of-successful-cash-flow-businesses/&quot;&gt;check out the case study on Tim Ferriss's blog&lt;/a&gt;.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Startup Weekend Boulder: Bridge My Path
    </title>
    <link href="http://peterlyons.com/problog/2011/02/swboulder-bridgemypath">
    </link><updated>2011-03-01T03:20:21.000Z</updated><id>http://localhost:9400/problog/2011/02/swboulder-bridgemypath</id>
    <content type="html">&lt;p&gt;
I spent all weekend at &lt;a href=&quot;http://boulder.startupweekend.org/&quot;&gt;Startup Weekend Boulder&lt;/a&gt;.  This is an event where people pitch their business ideas, vote on the top ideas, and then form teams and try to get the ideas from zero to launch in 54 intense hours.  It was my first time participating in Startup Weekend.  Overall, it was a really fun experience and I met a lot of really smart and fun people.  Friday was pitch night where somewhere just shy of 40 people gave 1-minute pitches.  There was good variety here from already established projects to things conceived just moments earlier.  There were gaming ideas, social networking ideas, education, finance, retail, even a medical marijuana related business and an infrared steak griller.  I pitched one of the ideas I have sitting around on my big long &lt;a href=&quot;https://workflowy.com&quot;&gt;WorkFlowy&lt;/a&gt; business idea lists: ThreeShopper, a curated shopping site to end the paradox of choice and narrow things down to exactly three choices.  My idea got zero votes. :-)  I think there were around 60 total attendees including folks from Nova Scotia and South Africa.
&lt;/p&gt;
&lt;p&gt;
After the top 7 or so pitches were elected we each had a chance to choose a project and join that team.  I joined up with &lt;a href=&quot;http://about.me/marshallhayes&quot;&gt;Marshall Hayes&lt;/a&gt; who was working on his mentoring networking idea.  We ended up gathering a very large team.  It was nine people even after a few joined and then switched to other projects.  Folks were enthusiastic about promoting the concept of mentorship but I felt like the idea needed refinement.  So we spent a lot of time just debating whether it was going to be consumer focused or licensed to universities for alumni networks amongst other variations.  Saturday morning as these discussions continued and the idea seemed to be heading towards a profile-based &quot;linkedin for mentoring&quot;, which was not at all appealing to me, I suggested we pivot the focus and instead of focusing on the mentors themselves, focus on a skill path and connect people along the same skill progression, facilitating mentorship from those ahead of you, connection with peers at the same place as you, and also helping those following you on the learning path.  The team was receptive to the pivot, and we started refining that idea.  Eventually we ended up with &lt;a href=&quot;http://bridgemypath.com&quot;&gt;Bridge My Path&lt;/a&gt; as the company name and got to work on wireframing, graphic design, market research, copy writing, etc.
&lt;/p&gt;
&lt;p&gt;
In the next day and a half we built the home page and refined a lot of the idea, as well as creating the demo pitch for Sunday evening.  Our MVP was our graphic designer &lt;a href=&quot;http://warbweb.com&quot;&gt;Carly Gloge&lt;/a&gt; from WarbWeb.  She created a beautiful site and graphics. &lt;a href=&quot;http://www.techstars.org/mentors/nglaros/&quot;&gt;Nicole Glaros&lt;/a&gt; complemented her during the pitches and said we had the best design of all the teams.  I think the aspiration of startup weekend is to actually get a functioning product up and working end to end, but given the lack of initial clarity in the idea and my years working in enterprise software, I am very skeptical of the utility of this goal.  So we ended up just building a static HTML site to launch the concept.  I put together most of the HTML/CSS using coffeekup templates and rendering them via node.js/express, then just posting the static HTML files.
&lt;/p&gt;
&lt;p&gt;
Sunday evening teams had 5 minutes to pitch their product.  Wow. What a difference a day and a half make!  Everything was much more refined and lots of teams had at least a nice home page designed and live.  Some teams had working mobile apps.  I don't recall any of the web apps having much to show in terms of code written over the weekend (SnapGames had some pre-existing code).  The panel of judges commented liberally on each idea and gave a lot of good feedback.  &lt;a href=&quot;http://www.suzanbond.com/2011/02/in-the-thick-of-things-startup-weekend-boulder/&quot;&gt;Suzan Bond&lt;/a&gt; was there blogging and tweeting the event as well as conducting some video interviews (which I can't easily locate on her site just yet). After the event, I took a long overdue nap, grabbed some ribs at the Rib House, and then hung out at the Spark Palace after party.
&lt;/p&gt;
&lt;p&gt;
Again, this was a really fun and valuable event.  In general I like high-commitment events (like Burning Man), so the fact that all the participants sprung for a $75 ticket and committed to locking themselves in the bunker all weekend makes a great dynamic.  A few lamers did pitch their idea and then jet when they didn't get selected, but what are you going to do?  If you like those early days of product ideas, I definitely recommend giving it a try.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Linkzie gets a bookmarklet
    </title>
    <link href="http://peterlyons.com/problog/2011/02/linkzie-gets-a-bookmarklet">
    </link><updated>2011-02-20T10:17:43.000Z</updated><id>http://localhost:9400/problog/2011/02/linkzie-gets-a-bookmarklet</id>
    <content type="html">&lt;p&gt;
I have added a bookmarklet feature to &lt;a href=&quot;https://linkzie.com&quot;&gt;Linkzie&lt;/a&gt;.  In the &quot;organize your links&quot; area you will see a little link there you can drag to your browser's bookmarks bar.  Then if you are looking at a web page and want to save it into Linkzie, just click the bookmarklet.  You will be able to set the name and category for the link and then be taken right back to the original page you were looking at.  This should make it easier to get lots of links into Linkzie.  Enjoy!
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>House of Genius
    </title>
    <link href="http://peterlyons.com/problog/2011/02/house-of-genius">
    </link><updated>2011-02-19T02:16:31.000Z</updated><id>http://localhost:9400/problog/2011/02/house-of-genius</id>
    <content type="html">&lt;p&gt;
I got to participate in a cool new brainstorming event called &lt;a href=&quot;http://houseofgenius.org&quot;&gt;House of Genius&lt;/a&gt; last night.  I won't say too much about it since the founders seem to prefer if the participants don't know exactly what to expect.  But it was populated by a great group of smart and interesting people from many different age groups and backgrounds.  It was in a super-swank enormous apartment downtown and was overall very unusual, engaging, and fun.  It's funny, last week's Denver Hack Nite meetup was also in an inappropriately-fancy mansion.  Maybe all my tech meetups will gradually start shifting from coffee shops and garages to luxury homes with helipads....
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Going all in on Google Calendar and Android
    </title>
    <link href="http://peterlyons.com/problog/2011/02/google-calendar">
    </link><updated>2011-02-18T22:00:53.000Z</updated><id>http://localhost:9400/problog/2011/02/google-calendar</id>
    <content type="html">&lt;p&gt;
So I bought an &lt;a href=&quot;http://www.virginmobileusa.com/cell-phones/lg-optimus-v-phone.jsp&quot;&gt;LG Optimus V&lt;/a&gt; Android phone this weekend.  I said if I finished the redesigns for peterlyons.com and linkzie.com I would treat myself.  I've been a fairly die-hard PalmOS fan since the early days of the PalmPilot Personal.  I think I got my first palm while still in high school probably in 1997 or so. I learned the Palm Graffiti input method.  I soaked it right up. I continued on with a Kyocera cell phone/PDA based on PalmOS which I loved passionately and then onto the Treo for work and finally the Centro which I bought in March 2008 and have been using for the past 3 years. Throughout this whole time I maintained the same contact database and calendar.  I had phone numbers in there of high school friends from before 10-digit dialing with area code was required.  I had 4-digit on-campus extensions for random people I met in college filed under their first name only.  It was amusing to go and look through the archives.  I was pretty anal-retentive about putting everything in there.   Guy who conducted my Region Band in high school: in there.  That girl who cut my hair for $5 one time in college: in there.  The number for the King 101 CS lab: in there. The same goes for Calendar.  I think during one of the numerous computer data migrations I did over the years, I abandoned the early years of calendar data, but I still have data going back to as early as September 1999.  I can tell you that I had a saxophone quartet rehearsal at 6:30pm on Wednesday September 22, 1999.  Some 7000+ calendar records have all been migrated into my google calendar. And now they're all synced to my phone. w00t!
&lt;/p&gt;
&lt;p&gt;
So anyway for the contact list I had to go through and trim down the people I hadn't been in contact with for a decade and then I hired someone on odesk.com to figure out how to migrate it into my gmail contacts list.  The whole keeping track of people's email addresses and phone numbers is pretty much moot at this point since tracking folks down by name online is usually straightforward, but we all have those handful of folks that still have little or no online presence.  So at this point keeping my contact book maintained basically makes sure I can easily call the people in my immediate network and perhaps more importantly the caller ID works properly when they call.  But clearly we're close to the point where any manual address book maintenance will be a thing of the past.
&lt;/p&gt;
&lt;p&gt;
So far the Optimus V seems like an awesome deal, especially for mobile-phobes like me who are late late adopters.  The phone costs $150 and you get a prepaid $25 plan with no contract that includes unlimited data and TXT plus 300 voice minutes.  That's saving me $15/month over my already pretty cheap AT&amp;T plan which has no data at all.  The savings on the plan will pay for the phone in 10 months.  Plus now that I'm already done with my AT&amp;T contract I don't have to start a new one.  We'll see how the coverage is: Virgin Mobile uses a subset of the Sprint towers, I'm told.   So far no issues and AT&amp;T is not great in this area anyway.
&lt;/p&gt;
&lt;img src=&quot;http://pinoytutorial.com/techtorial/wp-content/uploads/2011/02/lg_optimus_v.jpg&quot; alt=&quot;LG Optimus V&quot;&gt;
&lt;p&gt;
Perhaps the best part is how much functionality I get for my $150.  I get a full GPS device with updated maps. No thanks, Garmin, I won't be paying you $75 to update the maps on my Nuvi.  I get a new Sonos controller.  No thanks, Sonos, I won't be buying a $300 dedicated extra controller.  I get another Rhapsody/Pandora streaming music endpoint.  Nope, don't need another Ibiza Rhapsody MP3 player for $160.  Plus now I can develop and test Android apps, which I just might do. Plus since I can get a 32 GB microSD card for $100, I can _finally_ have an MP3 player that can hold pretty much all of my music.  I bought and loved a Creative Nomad Xen Xtra around 2005 probably.  It's the size of an old cassette tape player, and on the rare occasions when I bring it out into public, people think I'm a hipster listening to 80s cassettes or something.  But it was cheap and good and it took years and years for Apple and other mainstream mp3 players to hit 64GB. If there was a 64GB microSD card I could fit all my ripped MP3s from my CD collection (30GB or so) plus a ton of Rhapsody stuff and still have room for some photos and videos.  It seems microSD is topping out at 32GB though, so next year I'll probably buy another cheap Android phone with microSDHXC and then achieve portable device Nirvana.
&lt;/p&gt;
&lt;p&gt; 
On Tuesday I spent all day in Boulder working at Coffee shops in between my three (3!) meetups in one day.  Thus I had to move my car about three or four times during the day to park for free and not get a ticket.  What this meant is that when my last meetup concluded around 8:30pm I had forgotten which of the locations was the most recent one and spent about 20 minutes walking a grid in the dark looking for my car.  Yesterday I downloaded the parked car locator app. Never again will I have to wonder around! (Assuming I remember to actually tag my car's location during the parking process).
&lt;/p&gt;
&lt;p&gt;
Any other Optimus-V users out there?  Let me hear from ya!
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>New web designs
    </title>
    <link href="http://peterlyons.com/problog/2011/02/new-web-designs">
    </link><updated>2011-02-16T02:35:08.000Z</updated><id>http://localhost:9400/problog/2011/02/new-web-designs</id>
    <content type="html">&lt;p&gt;
As you may notice, the &lt;a href=&quot;/&quot;&gt;peterlyons.com&lt;/a&gt; site has been redesigned.  I worked with &lt;a href=&quot;http://stephaniehenderson.com&quot;&gt;Stephanie Henderson&lt;/a&gt; to get a new look now that I am an independent professional.  I'm pleased with the results, and the blogs are more seamlessly integrated now.
&lt;/p&gt;
&lt;p&gt;
I have also released a new version of &lt;a href=&quot;https://linkzie.com&quot;&gt;Linkzie&lt;/a&gt; today featuring a brand new web design. The original layout of categories was based on rows and that has been changed to columns.  Check it out!
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Going all in on gmail
    </title>
    <link href="http://peterlyons.com/problog/2011/02/going-all-in-on-gmail">
    </link><updated>2011-02-10T13:13:50.000Z</updated><id>http://localhost:9400/problog/2011/02/going-all-in-on-gmail</id>
    <content type="html">&lt;p&gt;
So somewhere in college around 2000 I started running my own IMAP mail server and storing my own mail.  Around 2007 I got tired of fighting the spam battle and switched to gmail.  I migrated some of my current messages into gmail using IMAP, but I didn't want to dump everything in there.  Today I realized I no longer have convenient access to those messages sitting in mbox format files on the linux laptop I rarely use anymore, so I hooked Thunderbird up to gmail and dragged my mail archives into gmail.  That was about 12,000 messages between 2000 and 2007.  Now it's all in one happy, searchable, spam-free place.  As long as Google doesn't spontaneously disappear gmail, I should be in good shape.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>node.js and command line jQuery
    </title>
    <link href="http://peterlyons.com/problog/2011/02/node-js-and-command-line-jquery">
    </link><updated>2011-02-09T03:41:10.000Z</updated><id>http://localhost:9400/problog/2011/02/node-js-and-command-line-jquery</id>
    <content type="html">&lt;p&gt;
node.js combined with the &lt;a href=&quot;https://github.com/visionmedia/query&quot;&gt;new query utility&lt;/a&gt; which makes jQuery-like functionality available from the command line.  This type of thing used to take a 30-line script. Brilliant!
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
for N in {1..10}; do curl --silent http://news.ycombinator.com | query td.title a get &quot;${N}&quot;; done


YC Partner Harjeet Taggar Gives Insights [Interview]
Update your timezone defs: Russia abolishes winter time (DST)
Want to be a leader? Wash the Dishes When Nobody Else Will. 
How to be 100% sure your startup idea is good
Introducing the Google Translate app for iPhone
In China, alpha males carry designer purses
How To Get Your First 1,000 Users
How Much Money I Made From Side Projects In 2010
Facebook Buys Old Sun Campus in Menlo Park
Government investigation: No evidence Toyota electronic throttles malfunctioned
&lt;/pre&gt;
&lt;/div&gt;
    </content>
  </entry>
  <entry>
    <title>The Perfect Exercise Headphones
    </title>
    <link href="http://peterlyons.com/problog/2011/02/exercise-headphones">
    </link><updated>2011-02-07T07:00:11.000Z</updated><id>http://localhost:9400/problog/2011/02/exercise-headphones</id>
    <content type="html">&lt;p&gt;
I believe I have now found a pair of headphones to wear while exercising and traveling that is optimal.  I've gone through a lot of headphones over the years as I'm sure many of you have.  I've tried both the extreme low end of the price spectrum (less than $10) and the costs-more-than-your-mp3-player range (I owned some $180 Shure E2C earbuds back when they were $180 instead of $90).  Of course I have learned some lessons the hard way. The following list starts out with weightier arguments and then decays into personal preference
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The $20 ballpark is about right where when they are inevitably lost or broken, you shake it off and move on, but they are still able to sound OK
    &lt;ul&gt;&lt;li&gt;As an aside, my $180 Shure headphones started making static interference two weeks after the warranty expired.&lt;/li&gt;
    &lt;li&gt;If you want a nicer pair for hi-fi listening at home, by all means go for it and invest in some nice cans, just don't bring them when traveling&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Sound isolating earbuds are the way to go for travel and exercise.  I know traditional full-ear headphones are back in popularity with the snowboarders, but in general I want something that will pack up to essentially zero size and block out the exterior noise. I don't wear these while skiing where taking them on and off frequently might be required.&lt;/li&gt;
&lt;li&gt;It is better if there is a right angle between the plug and the cord.  This is a sturdier design less likely to cause static or drop a stereo channel with wear and tear.&lt;/li&gt;
&lt;li&gt;The combination of in-ear earbud with behind-the ear wire for stability and security is the best. You don't want your earpiece falling out and dangling there while you are working on your downward dog.&lt;/li&gt;
&lt;li&gt;The cord should include a small clip you can slide up from the neck to the earbuds to zip up the &quot;Y&quot; section of the cable for storage to prevent tangling&lt;/li&gt;
&lt;li&gt;The cord should have a woven fabric sheath instead of plastic or rubber. This makes it easier to untangle.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
So to this end I think the &lt;a href=&quot;http://www.amazon.com/MEElectronics-M6-BK-Sport-Sound-Isolating-Headphones/dp/B0038W0K2K/ref=sr_1_2?ie=UTF8&amp;s=electronics&amp;qid=1297036394&amp;sr=1-2&quot;&gt;MEElectronics M6-BK Sport Sound-Isolating In-Ear Headphones&lt;/a&gt; are pretty much perfect.  The only item they lack from the above list is the woven fabric sheath.  The behind the ear wire is minimal but effective and they come with both single and triple flange earpieces.
&lt;/p&gt;
&lt;img src=&quot;http://ecx.images-amazon.com/images/I/41c5QzNEcOL.jpg&quot; alt=&quot;MEElectronics M6 Headphones&quot;&gt;
    </content>
  </entry>
  <entry>
    <title>And the bots show up
    </title>
    <link href="http://peterlyons.com/problog/2011/02/and-the-bots-show-up">
    </link><updated>2011-02-06T02:00:35.000Z</updated><id>http://localhost:9400/problog/2011/02/and-the-bots-show-up</id>
    <content type="html">&lt;p&gt;
I set up a staging server for a web app I'm working on yesterday.  This morning I find this in my rails log.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
ActionController::RoutingError (No route matches &quot;/scripts/setup.php&quot;):
ActionController::RoutingError (No route matches &quot;/sql/scripts/setup.php&quot;):
ActionController::RoutingError (No route matches &quot;/web/scripts/setup.php&quot;):
ActionController::RoutingError (No route matches &quot;/scripts/setup.php&quot;):
ActionController::RoutingError (No route matches &quot;/admin/scripts/setup.php&quot;):
ActionController::RoutingError (No route matches &quot;/PMA/scripts/setup.php&quot;):
ActionController::RoutingError (No route matches &quot;/phpmyadmin/scripts/setup.php&quot;):
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
Thus the shotgun bot scanning has commenced.  It's funny how organic and viral (viral in the bad sense) the web is given it's all just computer programs.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>dayJob.quit()
    </title>
    <link href="http://peterlyons.com/problog/2011/01/dayjob-quit">
    </link><updated>2011-01-28T00:01:39.000Z</updated><id>http://localhost:9400/problog/2011/01/dayjob-quit</id>
    <content type="html">&lt;p&gt;
I've quit my job writing data center automation software for HP.  I'm striking out on my own doing independent web application development.  I'll be building some information products and then hopefully a subscription based web application targeted at a small niche audience.  I had been working on the same product for 6.5 years and I am ready to switch gears from the world of enterprise software to consumer-facing web applications.
&lt;/p&gt;
&lt;p&gt;
I've been learning ruby, rails, and jquery in my free time for the past six months or so.  It's been pretty enjoyable (especially jQuery), and there is a stark contrast between the slow grind of enterprise software and the lightness and speed of web development.  I'll have more time and spare brain cycles for blogging and hopefully some more open source contributions, to which I look forward.  &lt;a href=&quot;/problog/feed/&quot;&gt;Subscribe to this blog's feed&lt;/a&gt; to see how things pan out for me in the coming weeks and months.  My working plan is to give self-employment a good long try before considering outside work.  I plan to work at it at least until Labor Day, which is a full 7 months away.  Wish me luck!
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Wiki migration from MoinMoin to gitit
    </title>
    <link href="http://peterlyons.com/problog/2011/01/moinmoin_to_giti">
    </link><updated>2011-01-10T05:08:29.000Z</updated><id>http://localhost:9400/problog/2011/01/moinmoin_to_giti</id>
    <content type="html">&lt;p&gt;
So I've been hosting my own &lt;a href=&quot;http://moinmo.in/&quot;&gt;MoinMoin&lt;/a&gt; wiki for a while.  I use it pretty heavily to manage my own little projects, packing lists, and whatnot.  I used MoinMoin initially because it is written in python and we were using it at work.  It has served me reasonably well, but I decide to shut it down and migrate all my data over to &lt;a href=&quot;http://gitit.johnmacfarlane.net/&quot;&gt;gitit&lt;/a&gt; instead.
&lt;/p&gt;
&lt;p&gt;
My reasoning for the migration includes the following factors.
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
MoinMoin seems as far as I can tell to be impossible if not difficult to run under nginx, and in this world of low-RAM VPSes where I keep all my online stuff, nginx beats apache soundly in this regard, so I've moved everything over to nginx
&lt;/li&gt;
&lt;li&gt;
I never really liked the notion of WikiWords to create links.  I wasted a lot of mental cycles worrying about how to prevent MoinMoin from making RedHat a hyperlink unintentionally, etc. Thus I wanted to move to a different markup, such as &lt;a href=&quot;http://daringfireball.net/projects/markdown/&quot;&gt;markdown&lt;/a&gt;, which seems to be pretty widely used, especially in github projects for README files.
&lt;/li&gt;
&lt;li&gt;
Using git as the data store is just perfect and brilliant.  I'm only now realizing the magic of git's &quot;no single master repo&quot; distributed nature and now I can confidently add stuff to my wiki from multiple machines via either a web browser or a text editor and know that everything will eventually be merged up perfectly. This is one less thing for me to worry about manually rsyncing around the next time I move to a different VPS host.
&lt;/li&gt;
&lt;li&gt;
MoinMoin has several glaring warning signs about programmers running wild with no product sense. For example, they don't have configuration files.  They have python code you edit.  The ruby community is another big fan of this misguided approach. Folks, asking programmers to edit code in a language they know to achieve complicated configuration (a la routes.rb) is fine.  Asking sysadmins to configure basic shit like which TCP port your program should listen on is not.  Make a configuration file, preferably in dead simple key=value format.
&lt;/li&gt;
&lt;li&gt;Another glaring warning sign is how MoinMoin handles its default content (help pages, etc), of which there is quite a lot.  It just dumps them into your wiki right next to your own home-grown content with no distinction.  There is no copy-on-write mechanism and no easy way to extract a list of pages you actually care about.  I ended up combining some globbing telling me which pages had at least 2 revisions with a manual scan of page names to fish out my data from the sea of default MoinMoin pages.  In hindsight, the smarter thing would have been to install a blank MoinMoin wiki, extract the set of page names, and then do a difference with the set of page names of my existing wiki, but my approach of just &lt;code&gt;ls */revisions/00000002&lt;/code&gt; worked almost as well, missing only a handful of pages, which a manual scan caught.
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Sadly, gitit is not provided as an out of the box Ubuntu x64 package, which I REALLY prefer not to mess with.  But alas, as with most webby stuff, I have to accept my fate and deal with manual installation, configuration, maintenance, and upgrade.  Other than that fact, gitit has been working well enough so far. I had to write my own init script for it, but that is just annoying as opposed to a serious drawback.
&lt;/p&gt;
&lt;p&gt;
The cool part about this migration was I was able to for the most part script it.  I wrote a python script that would take the VERY limited set of MoinMoin syntax that I use (primarily headers, lists, and links) and convert it to markdown.  Sadly its seems MoinMoin is one of the few markup formats that pandoc cannot use as input. :-( I also coded my script to start with revision 00000001 (used by MoinMoin) and commit each distinct MoinMoin page revision into git sequentially.  Now in my migrated gitit repo, I have the full MoinMoin edit history for every page, which is pretty sweet.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Unit tests are like Ewoks
    </title>
    <link href="http://peterlyons.com/problog/2010/12/unit-tests-are-like-ewoks">
    </link><updated>2010-12-02T03:24:43.000Z</updated><id>http://localhost:9400/problog/2010/12/unit-tests-are-like-ewoks</id>
    <content type="html">&lt;p&gt;Unit tests are like Ewoks.  They are your friends. They will help you.  They will help you fight formidable enemies.  They are cute and cuddly and adorable.&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Managing complexity and chaos
    </title>
    <link href="http://peterlyons.com/problog/2010/11/managing-complexity-and-chaos">
    </link><updated>2010-11-23T04:02:23.000Z</updated><id>http://localhost:9400/problog/2010/11/managing-complexity-and-chaos</id>
    <content type="html">&lt;p&gt;
I read somewhere (sorry I've forgotten where by now) that as an engineering, our #1 omnipresent mandate is &quot;manage complexity&quot;.  I interpret &quot;manage&quot; to mean &quot;reduce&quot; or &quot;prevent&quot; or &quot;minimize&quot; or &quot;mitigate&quot; in this context.  When I read this, it resonated very strongly with me.
&lt;/p&gt;
&lt;p&gt;
I was recently asked for feedback on one of the managers I work with and I thought in many was, in the manager role, the mantra is &quot;manage chaos&quot;.  I think that fits nicely.  When everything is clear, well-understood, prioritized, under control, the team hums along efficiently.  As a team gets overwhelmed or sidetracked or confused, chaos ensues, and it seems to me the role of management to prevent/reduce/minimize/mitigate that chaos.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>OS X and CD burning non-smartness
    </title>
    <link href="http://peterlyons.com/problog/2010/10/os-x-and-cd-burning-non-smartness">
    </link><updated>2010-10-19T19:46:59.000Z</updated><id>http://localhost:9400/problog/2010/10/os-x-and-cd-burning-non-smartness</id>
    <content type="html">&lt;p&gt;
So since making the linux to Mac transition, I have this mindset of things on the Mac are supposed to &quot;just work&quot; and be fantastic and marvelous and flawless.  This preconception was shattered again when I tried to burn some CDs recently.  I downloaded and ISO and tried to burn it with Finder.  In Ubuntu, if you try to burn a single .iso file to disc, it prompts you that you probably want to just make a disc from that CD image, and thus does the right thing.  Not so on the Mac. It happily spit out a data disc containing a single .iso file. Duh.  Then I went to burn a music CD using some .wav files a friend sent.  Again, on Ubuntu, if you ask it to burn a CD of just .wav music files, it suggests that you burn an audio CD automatically.  Mac, nope. Data CD with .wav files.  Facepalm.  In case you're curious, yes I did figure out that you need to use Disk Utility on the Mac to burn the .iso file and iTunes for the music CD.  I thought this was supposed to be seamless?
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>jQuery and Ball World
    </title>
    <link href="http://peterlyons.com/problog/2010/10/jquery-and-ball-world">
    </link><updated>2010-10-14T07:54:43.000Z</updated><id>http://localhost:9400/problog/2010/10/jquery-and-ball-world</id>
    <content type="html">&lt;p&gt;
So in my first computer science course, one of the earliest labs with &lt;a href=&quot;http://www.bandgap.cs.rice.edu/personal/adrice_swong/public/default.aspx&quot;&gt;Stephen Wong&lt;/a&gt; was called Ball World.  Professor Wong had set up some fill-in-the-blanks java code for us that brought up a basic frame.  We wrote code to add some ball sprites and we had to define their velocity and have code to detect collisions and compute new velocities after balls collided with each other or the boundaries.  I still remember the first time you get everything fired up and see two balls collide and bounce off each other realistically.  Keep in mind this is somewhere pretty earlier in my first semester ever programming.  It was pretty powerful and pretty exciting.
&lt;/p&gt;
&lt;p&gt;
Now here I am with ten years or so professional programming experience.  I'm teaching myself &lt;a href=&quot;http://jquery.com/&quot;&gt;jQuery&lt;/a&gt; and javascript.  Well, I guess I should put &quot;teaching&quot; in quotes because I'm basically just doing it since there's no learning required.  I've had about twenty different things I wanted to do so far on my practice project, and each of them involves a 3-word google query like &quot;jquery sort drag&quot;, a very tiny snippet of code, averaging about 2 or 3 lines, and it working perfectly the first time.  It's amazing.  I've got all kinds of HTML5 Ajaxy Web 2.0 goodness going and it works great and it's easy.  AND there's a graphical debugger and rapid development tools.  It's pretty darn sweet.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Home LAN fails and wins
    </title>
    <link href="http://peterlyons.com/problog/2010/10/home-lan-fails-and-wins">
    </link><updated>2010-10-11T04:16:55.000Z</updated><id>http://localhost:9400/problog/2010/10/home-lan-fails-and-wins</id>
    <content type="html">&lt;p&gt;
So my trusty Linksys wireless router up and died on me a few months ago.  Of course, in a geek's world, lack of Wifi at home is a stop-the-line emergency, so of course I just immediately drove to a local brick and mortar store to get a replacement.  I bought a Belkin as it was the cheapest.  It seems to not have the ability to remember MAC to IP mappings for any significant length of time, which meant all my laptops and VMs were constantly changing IPs.  I even ended up writing a little script to update &lt;code&gt;/etc/issue&lt;/code&gt; on my VMs so I could see their current IP without needing to log in.  Well, after a while I was finally frustrated with this enough to go see if the &lt;a href=&quot;http://www.dd-wrt.com&quot;&gt;DD-WRT&lt;/a&gt; replacement firmware was available.  It wasn't, so I thought I'd get a different model so I could enjoy the DHCP with static IP mappings goodness.  I clicked my handy bookmark for local craigslist and bam, there's a guy in my town selling a Buffalo WHR-G126 for $25 with the latest dd-wrt already installed.  w00t!  After a conversation over a few emails, calls, and texts, we rendezvoused in town just an hour or so later and completed our transaction.
&lt;/p&gt;
&lt;p&gt;
So now my network is so nice and lovely.  All my devices use DHCP, but the router remembers the devices that &quot;live here&quot; and gives them each a static IP address and entry in DNS.  Hurray for dd-wrt and craigslist.
&lt;/p&gt;
&lt;p&gt;
In case you might find this useful, here's a script to find the current IP address of a machine and put it into &lt;code&gt;/etc/issue&lt;/code&gt; so you can see it on the login screen without actually logging in.  
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First, edit &lt;code&gt;/etc/issue&lt;/code&gt; with a placeholder line (as root) like this: &lt;code&gt;echo &quot;IP Address: &quot;  /etc/issue&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Second, edit &lt;code&gt;/etc/rc.local&lt;/code&gt; and append this little bit of code.
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
#plyons. Display the IP at the login screen so we can SSH in
#without loggin in on the console
getip() {
    IP=`ifconfig eth0 | egrep &quot; inet addr:&quot; | cut -d : -f 2 | cut -d &quot; &quot; -f 1`
}
getip
if [ -z &quot;${IP}&quot; ]; then
    sleep 10 #Wait for network to initialize
    getip
fi
perl -pi -e &quot;s/IP Address: .*/IP Address: ${IP}/&quot; /etc/issue
&lt;/pre&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Now this has a lot of assumptions (one NIC called eth0, etc) and is in no way a generic solution, but for most VMs, it will probably get the job done as is or with a small tweak. 
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Cease and desist your password restrictions
    </title>
    <link href="http://peterlyons.com/problog/2010/09/password-restrictions">
    </link><updated>2010-09-09T22:45:38.000Z</updated><id>http://localhost:9400/problog/2010/09/password-restrictions</id>
    <content type="html">&lt;p&gt;
I just tried to create an account at Bed Bath and Beyond.  They said my password could only contain letters and numbers.  Many sites impose various restrictions on the password, including
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Password must be exactly some length&lt;/li&gt;
&lt;li&gt;Password must be greater than some minimum length&lt;/li&gt;
&lt;li&gt;Password must be smaller than some maximum length&lt;/li&gt;
&lt;li&gt;Password must contain some minimum number of &quot;complex&quot; characters&lt;/li&gt;
&lt;li&gt;Password must NOT contain complex characters&lt;/li&gt;
&lt;li&gt;Password is actually a 4-digit PIN&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
What this creates is A) major frustration B) a problem with remembering dozens of totally different passwords and C) inability to have a simple &quot;low importance&quot; password for your dozens of accounts you aren't super concerned about.
&lt;/p&gt;
&lt;p&gt;
Stop this madness.  Stop your silly password restrictions.  They are incompatible, frustrating, and probably not helping.  See the articles below for reference, but there are many others out there if you do a web search.
&lt;/p&gt;
&lt;p&gt;
References
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.nytimes.com/2010/09/05/business/05digi.html&quot;&gt; A Strong Password Isn’t the Strongest Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.wired.com/politics/security/commentary/securitymatters/2007/01/72458&quot;&gt;Secure Passwords Keep You Safer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
    </content>
  </entry>
  <entry>
    <title>A response to &quot;Handling Bugs in an Agile Context&quot;
    </title>
    <link href="http://peterlyons.com/problog/2010/09/agile-bugs">
    </link><updated>2010-09-08T21:07:27.000Z</updated><id>http://localhost:9400/problog/2010/09/agile-bugs</id>
    <content type="html">&lt;p&gt;
&lt;a href=&quot;http://testobsessed.com/2009/03/13/handling-bugs-in-an-agile-context/&quot;&gt;Handling Bugs in an Agile Context&lt;/a&gt; is a blog post I came across via &lt;a href=&quot;http://news.ycombinator.com&quot;&gt;Hacker News&lt;/a&gt; this morning.  As is often my experience when reading material on Agile from my perspective of an enterprise software developer, the experience was one of frustration and disbelief.
&lt;/p&gt;
&lt;p&gt;
So let me quote the portions of the article I find untenable in a enterprise software realm.
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;
Let’s start with the Product Owner. Not all Agile teams use this term. So where my definition says “Product Owner,” substitute in the title or name of the person who, in your organization, is responsible for defining what the software should do. This person might be a Business Analyst, a Product Manager, or some other Business Stakeholder.
&lt;/p&gt;
&lt;p&gt;
This person is not anyone on the implementation team. Yes, the testers or programmers may have opinions about what’s a bug and what’s not. The implementation team can advise the Product Owner. But the Product Owner decides.
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
Most of this matches my experience to some degree.  Yes we have product owners that are primarily business people.  We actually have about two levels of these, we have what we call &quot;Product Managers&quot; who set the more abstract direction for the product overall, and another role called &quot;Functional Architect&quot; that is a mostly technical person that deals with more detailed issues.  One way to think about this is the Product Manager decides mostly WHAT the product will do, and the Functional Architect refines that and specifies HOW the product will behave in detail.  Note the Functional Architect doesn't define how the IMPLEMENTATION will be done, just the more detailed behavior.
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;
This person is also not the end user or customer. When end users or customers encounter problems in the field, we listen to them. The Product Owner takes their opinions and preferences and needs into account. But the Product Owner is the person who ultimately decides if the customer has found something that violates valid expectations of the behavior of the system.
&lt;/p&gt;
&lt;p&gt;
Yes, that does put a lot of responsibility on the shoulders of the Product Owner, but that’s where the responsibility belongs. Defining what the software should and should not do is a business decision, not a technical decision.
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
Well, that would be great if it worked that way, but it doesn't.  The product owner doesn't have enough technical skill or understanding of the details to make decisions on specific bugs.  I'm talking about deep, subtle bugs.  Think error handling, file encoding, network performance tuning, etc.  If a customer complains that our NFS server should by default be configured for a block size of 32768, I'm sorry but the product owner is just not equipped to make a decision on that.  Yes, the technical team could explain the situation to him or her in enough detail for understanding, but the decision would be a &quot;no brainer&quot; dictated by how the team framed the explanation.  There are trade-off decisions where the tech lead for the feature needs to make a trade off between two things that are both desirable, like high throughput and low latency.  And it's not like we get these once in a blue moon. They happen many times in every sprint.  We live them day in and day out, and it's the job of the implementation team to independently make good decisions on them.  It's a bit demeaning to the implementation team to suggest that they are mere instruments of the product owner's omniscient will.  It's a TEAM.  EVERYONE takes into account many factors in making decisions on designs, implementations, and bugs every day. The team needs authority and autonomy to make the set of decisions that it is appropriate for them to own, and decisions need to escalate as appropriate for their scope and impact.  In my experience, 95% of all bugs are correctly resolved by the team members without any input from the product owner.
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;
Before we declare a story “Done,” if we find something that would violate the Product Owner’s expectations, we fix it. We don’t argue about it, we don’t debate or triage, we just fix it. This is what it means to have a zero tolerance for bugs. This is how we keep the code base clean and malleable and maintainable. That’s how we avoid accumulating technical debt. We do not tolerate broken windows in our code. And we make sure that there are one or more automated tests that would cover that same case so the problem won’t creep back in. Ever.
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
Well, that's cute but again, sometimes it's not possible to do that.  Here's a real world example.  I built a feature that installed some ZIP packages onto some servers.  We tested it.  It passed and worked.  We moved on to the next sprint.  Later on, we found out that if the ZIP file's install path contained non-ascii characters, we ran into problems and it failed.  OK, so that's a bug.  So you are saying &quot;we fix it&quot;.  You say &quot;We don’t argue about it, we don’t debate or triage, we just fix it&quot;. Well, in this case, after several days of me trying to &quot;just fix it&quot;, I informed the team that in order to correctly fix this bug I would have to re-implement the entire user story taking a very different approach.  This would take several days and since it was a de-facto rewrite, all of the tests would need to be re-run.  So how does your advice apply here?  It doesn't. We needed to debate and discuss and plan and adjust the backlog and otherwise deal with this reality.  I wish I could go to the fire departments that are currently battling the giant wildfire in the next town over and say &quot;Don't debate. Just extinguish it&quot;.  But it's not a helpful thing to say.
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;
And since we just fix them as we find them, we don’t need a name for these things. We don’t need to prioritize them. We don’t need to track them in a bug tracking system. We just take care of them right away.
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
Sorry, but this is the delusional rubbish the agile community puts out that makes it OK for me to dismiss you as utterly and hopelessly clueless.  Have you really never heard of a bug that is time consuming or difficult to fix?  Have you never seen a bug that needs multiple completely different fixes tried before one that really works is identified?  Are you accepting applications for citizenship in your universe?  It sounds nice.
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;
Usually the motivation for wanting to keep a record of things we won’t fix is to cover our backsides so that when the Product Owner comes back and says “Hey! Why didn’t you catch this?” we can point to the bug database and say “We did too catch it and you said not to fix it. Neener neener neener.” If an Agile team needs to keep CYA records, they have problems that bug tracking won’t fix.
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
Well, we have a lot of bugs.  My product has been around for almost a decade now.  There's value in tracking them.  For one, it gives management some concrete numbers to understand that they have technical debt, the product has issues, and we're accumulating more.  For two, people hit the bugs. They hit them over and over again.  It's a waste to have the issue re-triaged, re-explained every time.  We track them so there's one place that explains what this bug is, how it is reproduced, why we haven't fixed it, and how you can work around it.  Whether we track this in a bug tracker or a wiki or a KB or whatever doesn't seem as important to me as acknowledging the fact that there are issues that might affect users continually for quite a while (maybe for the rest of the life of the product), and not tracking that data makes the problem worse.
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;
Further, there is a high cost to such record keeping.
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
Arguably.  But there is also a cost to just abandoning them and leaving the new team members and customers to encounter them over and over again and refile the same bug.  Yes, sometimes these things last a long time and there's no cost effective way to solve them.  For example, in early versions of my product we installed Windows OSes via a DOS boot environment.  There was no other viable alternative at the time.  DOS has crippling network issues that we couldn't solve, so under some circumstances copying a full OS over the network on DOS would result in DOS hanging.  We can't fix this.  We just waited for Microsoft to announce WinPE, but that took several years.  Instead we documented that if you had an Intel NIC and encountered this issue, there was an obscure workaround you had to do.  This seems like a perfectly valid use of a long-term bug database to me.
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;
And if we’re not doing things right, we may find out that there are an overwhelming number of the little critters escaping. That’s when we know that we have a real problem with our process. 
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
OR, maybe we have a legacy code base with technical debt. Maybe the folks who wrote that code don't work here anymore and this is the only way we find the problems with it.  This doesn't necessarily mean we're not doing Agile correctly.
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;
Stop the bugs at the source instead of trying to corral and manage the little critters.
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
Thanks. Tell that to my several million lines of legacy code in a half dozen languages that runs on 72 different operating system versions.  Done &quot;stopping them at the source&quot; yet?  I'll wait.  Still not done?  Hmm, what's the problem?
&lt;/p&gt;
&lt;p&gt;
Anyway, that concludes my response to this blog post.  The general theme I see in the Agile world is the practitioners are mostly working from a mindset of small web development projects with a new code base.  Building enterprise software has realities that create real struggles for us, and we're looking for help, but the pundits out there generally dismiss our struggles without really understanding them and acknowledging their reality.  And that's what I find so frustrating.  The truth is most of these agile folks could probably provide us with real useful insights, but they first need to come to terms that we have some real problems that need to be considered even if they don't fit within their idealized vision for agile utopia.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Sonos pricing insanity continued
    </title>
    <link href="http://peterlyons.com/problog/2010/09/sonos-pricing-insanity">
    </link><updated>2010-09-07T07:17:01.000Z</updated><id>http://localhost:9400/problog/2010/09/sonos-pricing-insanity</id>
    <content type="html">&lt;p&gt;
So I'm a fan of &lt;a href=&quot;http://www.sonos.com&quot;&gt;Sonos&lt;/a&gt; music systems, but they've always been expensive.  Almost prohibitively so.  But now I can either spend &lt;a href=&quot;http://sonos.com/products/controllers/cr200/default.aspx?rdr=true&amp;LangType=1033&quot;&gt;$349&lt;/a&gt; for an additional controller that controls my sonos and does precisely fuck-all else, or I can buy an iPod Touch on &lt;a href=&quot;http://boulder.craigslist.org&quot;&gt;craigslist&lt;/a&gt; for $125 and then download the free Sonos controller application, plus do all the other iPod Touch stuff.  I'm not sure who Sonos thinks is going to buy this thing anymore other than the utterly rich and utterly clueless.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>No one really cares about web accessibility anyway...
    </title>
    <link href="http://peterlyons.com/problog/2010/09/web_accessibility">
    </link><updated>2010-09-07T05:12:19.000Z</updated><id>http://localhost:9400/problog/2010/09/web_accessibility</id>
    <content type="html">&lt;p&gt;
So if a picture is worth a thousand words, shouldn't it be:
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
&lt;img src=&quot;/some/image.jpg&quot;&gt;
    &lt;alt&gt;
        one two three four five six seven....
    &lt;/alt&gt;
&lt;/img&gt;
&lt;/pre&gt;
&lt;/div&gt;

    </content>
  </entry>
  <entry>
    <title>No squids for me, Bruce
    </title>
    <link href="http://peterlyons.com/problog/2010/09/no-squids-for-me-bruce">
    </link><updated>2010-09-05T05:16:30.000Z</updated><id>http://localhost:9400/problog/2010/09/no-squids-for-me-bruce</id>
    <content type="html">&lt;p&gt;
&lt;a href=&quot;http://www.schneier.com/&quot;&gt;Bruce Schneier&lt;/a&gt; is a leading author in the technology security and cryptography fields.  He is exceedingly rational and pragmatic.  I consider his insights on dealing with terrorism to be bar none the best available.  His blog often has great insights.  However, for reasons I can't understand, he always posts a random post on Fridays about squids.  Yes, the sea creature.  I don't know why, but it stopped being cute long ago.
&lt;/p&gt;
&lt;p&gt;
To that end, I've created a &lt;a href=&quot;http://pipes.yahoo.com/peterlyons/schneiernosquids&quot;&gt;Yahoo Pipe Feed of the Schneier on Security blog with the Friday Squid Blogging filtered out&lt;/a&gt;.  Feel free to subscribe and enjoy.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>UNIX is broken
    </title>
    <link href="http://peterlyons.com/problog/2010/09/unix-is-broken">
    </link><updated>2010-09-04T19:40:03.000Z</updated><id>http://localhost:9400/problog/2010/09/unix-is-broken</id>
    <content type="html">&lt;p&gt;
Reading &lt;a href=&quot;http://factor-language.blogspot.com/2010/09/two-things-every-unix-developer-should.html&quot;&gt;this blog post&lt;/a&gt; just makes me cringe.  This is why I  am so persistently hesitant to bother with C and low level systems programming.  They are broken. They are impossible to get right.  If even after numerous decades, the &lt;em&gt;cat&lt;/em&gt; program still has bugs in basic system interaction, it's not the programmers, it's the system. Thanks, but no. I'll code in a high level language that lets me focus on delivering functionality to my users, not performing sacrificial rituals to the Gods of K&amp;R, signal processing, and bitmasks.
&lt;/p&gt;
&lt;p&gt;
At work we have certain code bases that no matter how long we tweak them and how many dozens of bugs we fix, they just never reach a point of stability and reliability.  You need to just abandon them and rethink the problem from scratch.  Come at it with a different approach and a brand new code base and some analysis that actually addresses all of the edge cases.
&lt;/p&gt;
&lt;p&gt;
Interestingly, I think the mobile space (iOS, Android) have potential to finally provide a next generation operating system where applications written by average, normal programmers usually function perfectly.  Contrast this to most existing operating systems where most programs can be easily made to crash or misbehave in the course of normal use. We've got to get beyond this notion that in order to make a well-behaved application, the developer needs encyclopedic knowledge of bizzarre archaic minutia of the underlying OS.
&lt;/p&gt;
&lt;p&gt;
Incidentally, that post is written by &lt;a href=&quot;http://factorcode.org/slava/&quot;&gt;Slava Pestov&lt;/a&gt;, the creator of the jEdit text editor that has been my primary editor for many years.  This guy is good.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>On flash eating my key browser keyboard commands
    </title>
    <link href="http://peterlyons.com/problog/2010/08/on-flash-eating-my-key-browser-keyboard-commands">
    </link><updated>2010-08-30T19:48:19.000Z</updated><id>http://localhost:9400/problog/2010/08/on-flash-eating-my-key-browser-keyboard-commands</id>
    <content type="html">&lt;p&gt;
Dear Universe,
&lt;/p&gt;
&lt;p&gt;
   Please manifest a way for me to specify that my key browser keyboard shortcuts will work even when I'm watching a flash video.  Specifically I always want &quot;close window&quot;, &quot;new tab&quot;, and &quot;cycle active window&quot; to work.  I hate that after watching a video I have to click on the surrounding non-flash web page to get my keyboard commands working again. Please post a comment on this blog post when you are done manifesting this.
&lt;/p&gt;
&lt;p&gt;
Thanks,
&lt;/p&gt;
&lt;p&gt;
Pete
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>How to Install Ubuntu 10.04 over a LAN
    </title>
    <link href="http://peterlyons.com/problog/2010/07/install-ubuntu-lan">
    </link><updated>2010-07-17T04:40:24.000Z</updated><id>http://localhost:9400/problog/2010/07/install-ubuntu-lan</id>
    <content type="html">&lt;h1&gt;Overview and Requirements&lt;/h1&gt;
&lt;p&gt;
So I've volunteered to help &lt;a href=&quot;http://www.bococo.org/&quot;&gt;Boulder Community Computers&lt;/a&gt; set up some automated Ubuntu OS installations over their local area network.  Being as this is a large part of what I do professionally, this is something easy for me to do that should help them be more efficient.  Their network layout is going to look something like this I think:
&lt;/p&gt;
&lt;pre&gt;
Internet
   ^
   |
Office LAN (private addresses)&lt;-&gt; Various workstations (DHCP from router)
   ^
   |
Multihomed Netboot Server
   ^
   |
OS Build Network (other private addresses)
   ^
   |
   &lt;-&gt;Bare metal target machines needing  an OS (DHCP from Netboot Server)
&lt;/pre&gt;
&lt;p&gt;
OK, so let's talk about some of the goals/requirements:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I want the build network to be separate from the office network. This will keep things simple, avoid DHCP collisions, prevent users from accidentally provisioning their workstations, and keep the intense network traffic of OS installation off the office LAN&lt;/li&gt;
&lt;li&gt;I want to mainly download cached objects from the Netboot server to the targets.  I want to avoid downloading the OS over the Internet connection repeatedly.&lt;/li&gt;
&lt;li&gt;I don't want a lot of ongoing maintenance tasks&lt;/li&gt;
&lt;li&gt;I would like to be able to do a fully automatic hands-off installation&lt;/li&gt;
&lt;li&gt;I would also like to be able to boot into a live CD to test compatibility&lt;/li&gt;
&lt;li&gt;I would like to be able to do an interactive custom install&lt;/li&gt;
&lt;li&gt;I do want internet access from the OS Build Network (mostly for NTP, but it's just handy in general)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
So overall, Ubuntu can meet all of these goals easily. Here are the details.
&lt;/p&gt;
&lt;h1&gt;Setting up the Multihomed Network Install Server&lt;/h1&gt;
&lt;p&gt;
So we want an Ubuntu server machine with two network interfaces (multihomed) to act as our network install server. This machine will provide DHCP/PXE, TFTP, NFS, HTTP, and HTTP proxy services to the target machines.  It will also act as their default gateway, routing their traffic from the OS Build Network out to the Office LAN and then out onto the Internet and back. Here's how we get this server installed and configured.
&lt;/p&gt;
&lt;h2&gt;Install a basic Ubuntu 10.04 Server amd64 host&lt;/h2&gt;
&lt;p&gt;
I used a VirtualBox VM for this while testing this setup, and we will probably use that in their &quot;production&quot; environment as well. If you use a physical computer, make sure it has 2 network connections. You can select &quot;OpenSSH Server&quot; during the install or &lt;a href=&quot;https://help.ubuntu.com/community/InstallingSoftware&quot;&gt;install&lt;/a&gt; openssh-server later. Once the OS is up and running, we need to configure both of the network interfaces.  We'll be using RFC 1918 private addresses for both. In this case we have a 10.0.0.0/8 network as eth0 which is the Office LAN and a 192.168.0.0/16 network as eth1 which is the OS Build Network. The Office LAN is your typical home or office type network where a router is serving private IP addresses over DHCP and providing Internet access. In VirtualBox I set the first interface to be a bridged interface eth0 and the second interface to an internal only interface eth1.  To do this we edit &lt;code&gt;/etc/network/interfaces&lt;/code&gt; as root as follows.
&lt;/p&gt;
&lt;h3&gt;A quick note about code samples and long lines&lt;/h3&gt;
&lt;p&gt;
Several of the code samples in this article require very long lines. These don't display well on the web, but they are displayed using a CSS overflow horizontal scrollbar. Hopefully they are easy enough to read, copy, and paste.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
auto eth0
iface eth0 inet dhcp

# The internal build network for OS installation
auto eth1
iface eth1 inet static
address 192.168.8.1
netmask 255.255.255.0
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
After that edit, activate the config with (as root) &lt;code&gt; ifdown eth1; ifup eth1&lt;/code&gt;.  Verify it's working by pinging both of your IP addresses (the 10.X.X.X one and the 192.168.X.X one).
&lt;/p&gt;
&lt;h2&gt;Install and Configure PXE Boot Services&lt;/h2&gt;
&lt;p&gt;
&lt;a href=&quot;https://help.ubuntu.com/community/InstallingSoftware&quot;&gt;Install&lt;/a&gt; &lt;code&gt;dnsmasq&lt;/code&gt; via &lt;code&gt;apt-get install dnsmasq&lt;/code&gt;. Then as root do a &lt;code&gt;mkdir /var/lib/tftpboot&lt;/code&gt;. Edit &lt;code&gt;/etc/dnsmasq.conf&lt;/code&gt; to look as follows.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
interface=eth1
dhcp-range=192.168.8.100,192.168.8.254,12h
dhcp-boot=pxelinux.0
enable-tftp
tftp-root=/var/lib/tftpboot/
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
Restart it with &lt;code&gt;service dnsmasq restart&lt;/code&gt;
&lt;/p&gt;

&lt;p&gt;
The next big step is to make the Debian Installer available as a PXE boot image.  We're basically following &lt;a href=&quot;http://www.debuntu.org/how-to-unattended-ubuntu-network-install-pxelinux-p4&quot;&gt;this article&lt;/a&gt; here, except we're doing Ubuntu Lucid 10.04 instead of Feisty. Do the following.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
cd /var/lib/tftpboot
sudo wget http://archive.ubuntu.com/ubuntu/dists/lucid/main/installer-i386/current/images/netboot/netboot.tar.gz
sudo tar -xzvf netboot.tar.gz
sudo rm netboot.tar.gz
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
OK, at this point we should be ready to boot our first test target machine into the Debian Installer network boot service OS.  PXE boot a machine on the OS Build Network (again, I used a VirtualBox VM).  You may have to fit a key like F12 to instruct the machine to boot from a network card.  I have to hit F12 then &quot;l&quot; to boot from the LAN on my VirtualBox VM. If all is well, you should see a lovely Ubuntu installer menu like this (you won't have the menu items containing &quot;10.04&quot; yet, but we'll add them later).
&lt;/p&gt;
&lt;img src=&quot;/problog/images/ubuntu_installer_pxe_menu.png&quot; alt=&quot;Ubuntu Installer PXE Menu&quot;&gt;
&lt;p&gt;
So now we can use this to install Ubuntu onto the target. Well, almost. We don't have Internet access from our OS Build Network working yet, so let's get that going.
&lt;/p&gt;
&lt;h1&gt;Setting up Routing to the Internet&lt;/h1&gt;
&lt;p&gt;
We want to allow our target machines to connect to the Internet, which means we will configure our Net Boot server as a very basic router and NAT firewall. This is often called Internet Connection Sharing when used for this simple purpose. We're basically following &lt;a href=&quot;https://help.ubuntu.com/community/Internet/ConnectionSharing&quot;&gt;Internet Connection Sharing Setup&lt;/a&gt; docs from the Ubuntu community wiki. As root, add &lt;code&gt;net.ipv4.ip_forward=1&lt;/code&gt; to &lt;code&gt;/etc/sysctl.conf&lt;/code&gt;. Then run these commands.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
sudo iptables -A FORWARD -i eth0 -o eth1 -s 192.168.0.0/16 -m conntrack  --ctstate NEW -j ACCEPT
sudo iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A POSTROUTING -t nat -j MASQUERADE 
sudo iptables-save | sudo tee /etc/iptables.sav
sudo sh -c &quot;echo 1 &gt; /proc/sys/net/ipv4/ip_forward&quot;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
Now add &lt;code&gt;iptables-restore &lt; /etc/iptables.sav&lt;/code&gt; to &lt;code&gt;/etc/rc.local&lt;/code&gt; as root just above the &lt;code&gt;exit 0&lt;/code&gt; line.
This will get us going again when we reboot.
&lt;/p&gt;
&lt;p&gt;
Now you should be able to PXE boot a target, select &quot;Install&quot; from the menu and do an Ubuntu installation. However, it's going to be a normal install over the Internet, which is fine for doing one or two boxes, but since we want to crank these out en masse, we'll want to cache the bits and grab them locally.
&lt;/p&gt;
&lt;h1&gt;Caching the Ubuntu Packages with apt-cacher-ng&lt;/h1&gt;
&lt;p&gt;
I tried both squid and apt-cacher for a solution to locally cache the bulk of the Ubuntu binary package payload, but neither worked very well. apt-cacher-ng seems to do exactly what we want and nothing more with no configuration.  &lt;a href=&quot;https://help.ubuntu.com/community/InstallingSoftware&quot;&gt;install&lt;/a&gt; &lt;code&gt;apt-cacher-ng&lt;/code&gt;. This &quot;just works&quot; with no setup.  After it's installed, if you PXE boot a target, select &quot;Install&quot; and go through the interactive install, when prompted for a proxy, enter &lt;code&gt;http://192.168.8.1:3142&lt;/code&gt;. This is the OS Build Network IP of the Net Boot server and the apt-cacher-ng proxy port.  Now all your packages will be cached on the OS Boot Server.  The first install will download them from the Internet. Subsequent installs will get them from the local cache and thus be much much faster and more efficient.
&lt;/p&gt;
&lt;h1&gt;Hands-off Automatic OS Installation&lt;/h1&gt;
&lt;p&gt;
OK, we now have network based OS installs working, but since we're planning on doing many of these, we want to fully automate this.  To do that, we'll use a preseed file to configure the Debian Installer to not ask any questions. Configure a file at &lt;code&gt;/var/lib/tftpboot/bococo.seed&lt;/code&gt; with the the content linked here: &lt;a href=&quot;/problog/images/bococo.seed&quot;&gt;bococo.seed&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
Now we'll add a new item to the PXE menu to trigger a hands-off automated OS install. Edit &lt;code&gt;/var/lib/tftpboot/ubuntu-installer/i386/boot-screens/text.cfg&lt;/code&gt; and add this entry.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
label install-10.04-hands-off 
        menu label ^Install 10.04 Hands Off
        kernel ubuntu-installer/i386/linux
        append vga=normal initrd=ubuntu-installer/i386/initrd.gz locale=en_US.UTF-8 debian-installer/keymap=us auto hostname=bococo preseed/url=http://192.168.8.1/bococo.seed -- quiet
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
In order to get that file over HTTP, we'll server it up with the nginx web server. &lt;a href=&quot;https://help.ubuntu.com/community/InstallingSoftware&quot;&gt;install&lt;/a&gt; nginx via &lt;code&gt;apt-get install nginx&lt;/code&gt;.  Then we can edit &lt;code&gt;/etc/nginx/sites-enabled/default&lt;/code&gt; and change &lt;code&gt;location /&lt;/code&gt; block to set the new document root to the tftpboot directory so it looks like this.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
        location / {
                root   /var/lib/tftpboot;
                index  index.html index.htm;
        }
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
Restart nginx via &lt;code&gt;service nginx restart&lt;/code&gt;. Now we should be able to PXE boot the target and select the &quot;Install 10.04 Hands Off&quot; option and the entire thing should happen automatically.
&lt;/p&gt;
&lt;h1&gt;Network Booting into the Live Image&lt;/h1&gt;
&lt;p&gt;
One final bit of utility functionality we want to provide is a network bootable live Ubuntu image.  This will be handy for testing hardware compatibility, performance, etc. First we'll download the primary Ubuntu Desktop CD image and make it available over NFS. Do the following as root.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
apt-get install nfs-kernel-server
cd /var/opt
wget 'http://ubuntu.cs.utah.edu/releases/lucid/ubuntu-10.04-desktop-i386.iso'
mkdir /var/lib/tftpboot/ubuntu-10.04-desktop-i386
echo /var/opt/ubuntu-10.04-desktop-i386.iso /var/lib/tftpboot/ubuntu-10.04-desktop-i386 auto ro,loop 0 0 &gt;&gt; /etc/fstab
mount /var/lib/tftpboot/ubuntu-10.04-desktop-i386
echo &quot;/var/lib/tftpboot/ubuntu-10.04-desktop-i386 *(ro,sync,no_subtree_check)&quot; &gt;&gt; /etc/exports
service nfs-kernel-server restart
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
Now add another entry to your PXE menu for the live Session. I also like to move the &lt;code&gt;menu default&lt;/code&gt; option to the live session, so my final &lt;code&gt;/var/lib/tftpboot/ubuntu-installer/i386/boot-screens/text.cfg&lt;/code&gt; looks like this:
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
default install-10.04-hands-off
label live
        menu label ^Live Session 10.04
        menu default
        kernel ubuntu-10.04-desktop-i386/casper/vmlinuz
        append initrd=ubuntu-10.04-desktop-i386/casper/initrd.lz boot=casper netboot=nfs nfsroot=192.168.8.1:/var/lib/tftpboot/ubuntu-10.04-desktop-i386          -- quiet
label install
        menu label ^Install
        kernel ubuntu-installer/i386/linux
        append vga=normal initrd=ubuntu-installer/i386/initrd.gz -- quiet 
label install-10.04-hands-off
        menu label ^Install 10.04 Hands Off
        kernel ubuntu-installer/i386/linux
        append vga=normal initrd=ubuntu-installer/i386/initrd.gz locale=en_US.UTF-8 debian-installer/keymap=us auto hostname=bococo preseed/url=http://192.168.8.1/bococo.seed -- quiet
label cli
        menu label ^Command-line install
        kernel ubuntu-installer/i386/linux
        append tasks=standard pkgsel/language-pack-patterns= pkgsel/install-language-support=falseac vga=normal initrd=ubuntu-installer/i386/initrd.gz -- quiet
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
And that's it!  We can now netbook into a live session, an interactive install, or a fully automated install.
&lt;/p&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.debuntu.org/how-to-unattended-ubuntu-network-install-pxelinux-p4&quot;&gt;How-To: Unattended Ubuntu Deployment over Network -- page 4 -- PXELinux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://help.ubuntu.com/community/Internet/ConnectionSharing&quot;&gt;Ubuntu Internet Connection Sharing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.debian.org/releases/etch/i386/ch04s07.html.en&quot;&gt;Debian Installer section on Automatic Installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://help.ubuntu.com/10.04/installation-guide/example-preseed.txt&quot;&gt;Sample preseed file for Ubuntu 10.04&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.ubuntugeek.com/apt-cacher-ng-http-download-proxy-for-software-packages.html&quot;&gt;Info on apt-cacher-ng&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Acknowledgements&lt;/h2&gt;
&lt;p&gt;
Thanks to the authors of the above reference web pages and Nick Flores for collaborating on this.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Windows Server 2008 Setup Annoyances
    </title>
    <link href="http://peterlyons.com/problog/2010/07/windows-setup-annoyances">
    </link><updated>2010-07-10T07:12:54.000Z</updated><id>http://localhost:9400/problog/2010/07/windows-setup-annoyances</id>
    <content type="html">&lt;p&gt;
So I do a lot of work with automated unattended intallations of Operating Systems, including Windows.  Here's some of my primary complaints about the new Windows setup program in Windows Server 2008.
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;No good way to validate the unnatend.xml file.  Now Microsoft does provide tools to help generate these files, and hopefully any file you generate with those (graphical, Windows-only) tools should be at least well-formed and semantically valid.  However, there's no way to do a deeper validation that a given XML file is compatible with a particular target machine.
&lt;/li&gt;
&lt;li&gt;
The unattend.xml encodes the processor architecture all over the place for no reason.  I have to do a search and replace of &lt;code&gt;processorArchitecture=&quot;x86&quot;&lt;/code&gt; with &lt;code&gt;processorArchitecture=&quot;amd64&quot;&lt;/code&gt; in every &lt;code&gt;component&lt;/code&gt; tag.  There's pretty much zero information in most unattend.xml files that's CPU architecture specific anyway.  This is a real nuisance.  I should be able to use the exact same file on x86 and amd64 without issues.
&lt;/li&gt;
&lt;li&gt;
Setup doesn't do the simple but important hardware compatibility validation that would make users' lives easier. For example, neither winpe nor windows setup with complain if the target system has insufficient RAM.  WinPE will just behave very oddly and things won't work.  There's no checking for sufficient disk space ahead of time or that the disk layout is feasible.  There's no checking for suitable network or storage drivers.  When you don't have viable storage drivers, you just reboot out of WinPE into a lovely Blue Screen of Death &lt;code&gt;0x7b&lt;/code&gt; stop error. Hurray!  Similarly, no one at MS seems to care if you don't have a working NIC driver.  The OS has the word &quot;Server&quot; in it.  You need a network driver or your OS is in a useless void.
&lt;/li&gt;
&lt;li&gt;
The fact that windows setup reboots into an environment with zero networking and zero third party applications allowed is just a mind-boggling recipe for end user frustration.  We have to resort to brute time out calculation to even know whether the Windows install worked or not.  We can't provide a good user experience to people looking to do UNATTENDED installs.
&lt;/li&gt;
&lt;li&gt;
Another one in the &quot;we have no idea what unattended means&quot; even though our configuration file is called &quot;unattend.xml&quot; department: STOP PRESENTING GUI DIALOGS.  There are many issues that unattend.xml supposedly allows a &lt;code&gt;showGui=&quot;never&quot;&lt;/code&gt; but in my experience they either have no effect (GUI displays and halts the install anyway) or they just flat out break the install.  Microsoft just doesn't &quot;get it&quot; here.  Any automated install isn't a fully attended graphical install rejiggered to try not to pop up a GUI.  It's an entirely different use case.  Get it through your thick skull: NO ONE IS LOOKING AT THE CONSOLE. THESE ARE SERVERS. THE INSTALL IS BEING DRIVER AUTOMATICALLY BY SOFTWARE. NEVER EVER SHOW A GUI.
&lt;/li&gt;
&lt;li&gt;
Same category of cluelessness. Windows setup doesn't return a non-zero exit code on failure. Duh.  I'm aware of some other mechanisms like &lt;code&gt;setupcomplete.cmd&lt;/code&gt; that MS claims to provide for this, but from what I can tell after several attempts, they simply don't work.
&lt;/li&gt;
&lt;li&gt;
The log files and error messages are just a mess.  And I'm not even talking about obscure edge case failures.  Simple things like an invalid product key or computer name can create weird mysterious failures and behavior.
&lt;/li&gt;
&lt;/ol&gt;
    </content>
  </entry>
  <entry>
    <title>Remove your SCM system from your job postings
    </title>
    <link href="http://peterlyons.com/problog/2010/07/no-scm-in-job-postings">
    </link><updated>2010-07-07T04:54:58.000Z</updated><id>http://localhost:9400/problog/2010/07/no-scm-in-job-postings</id>
    <content type="html">&lt;p&gt;
Most job postings will list one or more source code management (SCM) tools in their laundry list of required skills and buzzwords.  This seems to both be unnecessary and also to miss the point. As an employer, your concern shouldn't be whether or not your candidate is intimately familiar with subversion, git, perforce, starteam, mercurial, CVS, sourcesafe, or whatever system your code happens to reside within at this point in time.  Why not?  Because it just doesn't matter and it doesn't help you distinguish good candidates from bad. This in my mind seems akin to asking potential postal employees &quot;Do you have experience driving from the right hand side of a boxy little truck?&quot;.  It's just not something that's going to be a stumbling block.  If you can drive, you'll get the hang of the postal delivery truck soon enough.  It's the same with SCM.  If you have worked successfully on a few sizeable projects in one or two of them, you'll be fine.  I've never heard this story: &quot;Oh yeah, we hired this woman Sandra and she wrote this fantastic code for us, but she just could not figure out how to commit it to subversion, so we had to let her go.&quot;  It's just not going to be the issue.  Well, what is going to be the issue?
&lt;/p&gt;
&lt;p&gt;
In this context, it's more important to see if the candidate understands branch and release organization and management as generic principles.  When and how should the code be branched?  What steps do you take when it's time to ship a release?  When a bug needs to get fixed against an old version of the product, how does that work with SCM?  These types of questions are OK sanity checks to spend 2 minutes on in an interview, but they don't need to be on your job posting.
&lt;/p&gt;
&lt;p&gt;
Now, here's what I see missing that seems in my mind much more likely to actually cause you problems down the road.  Does your candidate understand software packaging, distribution, installation, and upgrade?  Do they understand the principles of package management systems like RPM or DEB, and the complexities around dependency management, upgrade, and rollback?  Do they understand how to package and ship (or not) third party libraries? These questions may be more or less relevant based on the deployment model you use (web vs. bundled vs. embedded, etc).  However, for lots of projects, they are key and lots of companies are clearly in the dark here.
&lt;/p&gt;
&lt;p&gt;
So the summary: don't bother listing a specific SCM as a requirement in your job posting. Instead, briefly interview your candidates on general SCM methods and principles.  And in addition to that, given your deployment model(s) find out how much your candidate knows about packaging and installation.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>On Idempotence, intention, and unix commands
    </title>
    <link href="http://peterlyons.com/problog/2010/05/on-idempotence-intention-and-unix-commands">
    </link><updated>2010-05-21T09:42:05.000Z</updated><id>http://localhost:9400/problog/2010/05/on-idempotence-intention-and-unix-commands</id>
    <content type="html">&lt;p&gt;
&lt;a href=&quot;http://en.wikipedia.org/wiki/Idempotence#In_computing&quot;&gt;Idempotence&lt;/a&gt; means that running a command or function several times produces the same result as running it only once.  This is an very important design principle that is a blessing when used appropriately and a scourge when not used where warranted.
&lt;/p&gt;
&lt;p&gt;
For analogy, imagine you ask a housemate (or butler if that's how you roll) to empty the dishwasher.  They dutifully go over there, open the dishwasher door, and find it's already empty.  How do they react?  Do they come back to you shouting in confusion &quot;You fool! How can I empty the dishwasher if there's nothing in it! Oh woe is me. What am I to do?&quot;?  Or do they just think to themselves &quot;score!&quot; and go on a coffee break, leaving you to go about your business trusting that the dishwasher is now empty?
&lt;/p&gt;
&lt;p&gt;
Another analogy is from the military's notion of &quot;management by intent&quot; wherein a commander might order his troops to &quot;have camp fully operational by noon&quot; as opposed to dictating specific tactics that must be taken in order to achieve the intended outcome. This way, the troops can rely on their own abilities to achieve the intent and are empowered to respond to changing or unexpected circumstances independently.
&lt;/p&gt;
&lt;p&gt;
Now, when it comes to computer programs, UNIX has a mixed bag of utilities that understand this and some that don't.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
mkdir /tmp/2;echo $?;mkdir /tmp/2;echo $?
0
mkdir: cannot create directory `/tmp/2': File exists
1

rm /tmp/foo;echo $?;rm /tmp/foo;echo $?
0
rm: cannot remove `/tmp/foo': No such file or directory
1

&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
So the bad examples include &lt;code&gt;mkdir, rmdir, rm, ln, and perhaps kill (debatable)&lt;/code&gt;. Think about how much simpler using a command line and writing shell scripts would be if these were idempotent and instead of panicking in horror when the user does not know the current state of the filesystem, just allowed the user to describe the desired end state.  I would love to have idempotent and recursive by default commands like &lt;code&gt;mkdir -p&lt;/code&gt; or &lt;code&gt;rm -rf&lt;/code&gt; in combination with a transactional filesystem with built in undo capabilities.
&lt;/p&gt;
&lt;p&gt;
Good idempotent examples include &lt;code&gt;touch, tar, zip, cp, chmod&lt;/code&gt;.
&lt;/p&gt;
&lt;p&gt;
So the point about design and usability here is &lt;b&gt;it's good to ask oneself &quot;What is the user's intent here?&quot;&lt;/b&gt;, and try to do everything in your power to work in concert with that intention.  A strong and painful negative example from my career has to do with the fact that the Solaris &lt;code&gt;patchadd&lt;/code&gt; program is not idempotent and it doesn't return exit codes according to the user's intent.  So when I run &lt;code&gt;patchadd 123456-01&lt;/code&gt;, really my intention is &quot;I want this system to be OK with regard to patch 123456-01&quot;.  &lt;code&gt;patchadd&lt;/code&gt; will return a non-zero exit code if the patch is already installed or the patch is not applicable to the server or if a newer revision is already installed.  As a user of &lt;code&gt;patchadd&lt;/code&gt;, I don't care.  It's all success to me, and nor do I want to be bothered with implementation details within patchadd such as not installing a patch if a newer revision is already installed. I think many shell scripts would be a lot smaller and clearer and simpler without always having to wrap &lt;code&gt;mkdir&lt;/code&gt; in an &lt;code&gt;if [ ! -d /blah/dir ]&lt;/code&gt; clause to avoid spurious error output.
&lt;/p&gt;
&lt;p&gt;
A few other links on this topic:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://devhawk.net/2007/11/09/The+Importance+Of+Idempotence.aspx&quot;&gt;The Importance of Idempotence (devhawk)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.allapplabs.com/glossary/idempotent.htm&quot;&gt;Java Glossary entry on Idempotent&lt;/a&gt;. I like this quote &quot;Elevator call buttons are also idempotent, though many people think they are not.&quot;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://stackoverflow.com/questions/1077412/what-is-an-idempotent-operation&quot;&gt;Stack Overflow: What is an idempotent operation?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
    </content>
  </entry>
  <entry>
    <title>Bleeding Edge and Rotting Core
    </title>
    <link href="http://peterlyons.com/problog/2010/03/bleeding-edge-and-rotting-core">
    </link><updated>2010-03-19T08:53:53.000Z</updated><id>http://localhost:9400/problog/2010/03/bleeding-edge-and-rotting-core</id>
    <content type="html">&lt;p&gt;
I just wanted to post some thoughts on the topic of selecting software components with regard to the maturity thereof.  I think overall the programmer community is by default gung-ho about the bleeding edge.  We like the shiny new toys with the bells and whistles.  Once something's been around enough to have its weaknesses well understsood, we find it very frustrating to have to continue to work with it.  I'm not going to offer any specific recommendations, just some things to keep in mind.  The general gist though is that it takes some hard-earned pragmatism and real production experience to understand the value of using older releases of components.
&lt;/p&gt;
&lt;p&gt;
First, let's define some terms.  We're familiar with what is known as the bleeding edge.  The new hotness. The stuff straight off the presses instilled with the glimmering light of state of the art knowledge.  There's probably always been a lot of this, but there seems to have been a flurry in the past five years of so of interest in ruby, rails, erlang, clojure, scala, dozens of python app and web frameworks, etc.  On the other hand, we have the old guard, which I'd like to call the rotting core.  Generally we shy away from this, but there are times when it is absolutely the correct choice in certain situations.
&lt;/p&gt;
&lt;p&gt;
So, let's look at some pros and cons.
&lt;/p&gt;
&lt;p&gt;
Bleeding edge pros:
&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;The freshest and (usually) best designs and thinking are made available&lt;/li&gt;
    &lt;li&gt;Almost always more succinct and expressive&lt;/li&gt;
    &lt;li&gt;Often more coherent, clean, and consistent&lt;/li&gt;
    &lt;li&gt;Embodies improvements based on lessons learned from past failings and shortcomings&lt;/li&gt;
    &lt;li&gt;Development tools and processes are sometimes more productive&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Bleeding edge cons:
&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Development tools are usually immature and inferior
        &lt;ul&gt;
        &lt;li&gt;IDE support is likely to lag behind&lt;/li&gt;
        &lt;li&gt;Debugger may lag behind as may remote graphical debugging&lt;/li&gt;
        &lt;li&gt;Performance profilers might not be there&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;Deployment issues may not have been well addressed yet&lt;/li&gt;
    &lt;li&gt;Updates will come more frequently causing churn&lt;/li&gt;
    &lt;li&gt;Software has not had as broad testing in production and is therefore likely to have more &quot;surprises&quot;. Sometimes these can be showstoppers.&lt;/li&gt;
    &lt;li&gt;Community size will be smaller&lt;/li&gt;
    &lt;li&gt;Depth of knowledge in the community will be shallower&lt;/li&gt;
    &lt;li&gt;Standard library may be undergoing more flux&lt;/li&gt;
&lt;/ul&gt;
 
Rotting core pros:
&lt;ul&gt;
    &lt;li&gt;Stable, known quantity. It may have warts and bugs, but at least we're aware of most of them by now&lt;/li&gt;
    &lt;li&gt;Development tools generally have solid support including remote graphical debugging, mature performance profilers, etc&lt;/li&gt;
    &lt;li&gt;Community size will be larger&lt;/li&gt;
    &lt;li&gt;Community depth of knowledge will be much deeper&lt;/li&gt;
    &lt;li&gt;Updates are rare and only for occasional major issues or security patches&lt;/li&gt;
    &lt;li&gt;standard library will be well known and stable&lt;/li&gt;
&lt;/ul&gt;
Rotting core cons:
&lt;ul&gt;
    &lt;li&gt;Less exciting to developers.  Yesterday's designs and paradigms.&lt;/li&gt;
    &lt;li&gt;Often tedious compared to the bleeding edge&lt;/li&gt;
    &lt;li&gt;Support issues. Standard answer may always be &quot;update to the latest version&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
And now, let's back this up with some examples and anecdotes.  I think when it comes to rotting core technologies, you have both the &quot;oldie but a goodie&quot; category and the &quot;oldie and a baddie&quot; one.  Currently my project has a component written against the now ancient Python 1.5.2 runtime, and we have hundreds of thousands of copies of that component installed at customer sites.  It is running on something around seventy different OSes.  Now, at the time when that component was originally written, this was close to the bleeding edge.  We've still not entirely upgraded it because it's an oldie and a goodie.  We've patched it a bunch and run it under huge loads and huge scales. We know what it can do, and we know what it can't do.  We even had famous python educator Mark Lutz (Programming Python) come in to train us and give us quizzical looks when we explain that half of what he is saying doesn't apply to us since it wasn't available in python 1.5.2.  Over the years, I've come to see the merits of this and even though its frustrating, the business reality is that every year that stuff continues to run without issue is bettering the return on the initial R&amp;D investment.  It ain't broke, so we're not in a hurry to fix it.
&lt;/p&gt;
&lt;p&gt;
Of course, on the other side, you've got things like Java 1.2, which I also worked with.  Python has come a long way since 1.5.2, but really it's still basically the same deal, and the design was good from the start. Java has probably come even farther, but the design was a mess from the beginning and they've since seen the error of their ways and made some great improvements.  I would put that one in the &quot;oldie but a baddie&quot; category and do what it takes to upgrade.
&lt;/p&gt;
&lt;p&gt;
I remember chatting with a stranger on a plane after we each noticed that we were both programmers and were both actively programming on the plane. This was a few years ago and Ruby was still pretty much bleeding edge.  He looked at me with desperation and asked me if I knew anything about debugging deadlocks, threading issues, and core dumps since his production ruby app was regularly hitting issues and his team was basically at a point where they didn't have the knowledge or tools to solve them, and it was jeopardizing their whole project.  Sadly I couldn't offer any help, but I could certainly sympathize.
&lt;/p&gt;
&lt;p&gt;
I also have a friend who used to work at a DNS registry run by someone very much of the &quot;rotting core&quot; philosophy.  They ran Solaris 8 and ancient versions of lots of core C/unix utilities (bind et al), and to actually run versions that old took significant effort on their part, but it made sense for that project. They are running a piece of the Internet backbone. It's not bleeding edge stuff.  It just needs stability, stability, stability, and those are the tools they needed to meet their business goals.
&lt;/p&gt;
&lt;p&gt;
So next time you join a new project and start to reflexively freak out when they explain their software stack, supress your urge for a minute and get some information about the choices they have made and the reasoning and circumstances that got them where they are.  You might be surprised at the difficult but pragmmatic choices that were made and hopefully you can admire and appreciate the character of those who made them.
&lt;/p&gt;
&lt;p&gt;
And finally, think about the value of being able to look across a broad set of available components and correctly determine where components are in a &quot;sweet spot&quot; of their lifecycle, ripe to be chosen and deployed at length.  That is a deep wisdom that is a long time coming.
&lt;/p&gt;

    </content>
  </entry>
  <entry>
    <title>MoinMoin Columns Macro
    </title>
    <link href="http://peterlyons.com/problog/2010/03/moinmoin-columns">
    </link><updated>2010-03-08T00:43:31.000Z</updated><id>http://localhost:9400/problog/2010/03/moinmoin-columns</id>
    <content type="html">&lt;p&gt;
I just updated the &quot;Columns&quot; macro for the &lt;a href=&quot;http://moinmo.in/&quot;&gt;MoinMoin&lt;/a&gt; wiki.  This allows you to lay out a wiki page in two to ten columns.  This makes it easier to get lots of info on one page in certain situations and I've used it to great benefit on my personal wiki where I organize my stuff.
&lt;/p&gt;
&lt;p&gt;
Here's the &lt;a href=&quot;http://moinmo.in/MacroMarket/Columns#peterlyonsupdate&quot;&gt;MacroMarket page where my update has been posted for discussion&lt;/a&gt;.  The original author may not like it, so it might not become the canonical fork, but that's how it goes with open source.
&lt;/p&gt;
&lt;p&gt;
Here's what it looks like with four columns:
&lt;/p&gt;
&lt;img src=&quot;/problog/images/moinmoin_columns.png&quot; alt=&quot;MoinMoin wiki page with Columns macro&quot;&gt; 
    </content>
  </entry>
  <entry>
    <title>Optional Syntax Should Be Illegal
    </title>
    <link href="http://peterlyons.com/problog/2010/02/optional-syntax-should-be-illegal">
    </link><updated>2010-02-14T03:33:31.000Z</updated><id>http://localhost:9400/problog/2010/02/optional-syntax-should-be-illegal</id>
    <content type="html">&lt;p&gt;
Why in the world do some programming languages include optional syntax?  To a true type A engineer, this is incomprehensible and unacceptable.  For example, in Adobe's ActionScript, statements may optionally be terminated with a semicolon.  Usually this is not required, except in a few situations you need it.  Evil.  The statement that our number one job as software engineers is to manage complexitity really resonates with me, and willy nilly allowing of optional syntax just destroys consistency, predictability, and simplicity for no reason whatsoever.  Optional syntax seems to me a bad language design smell that indicates the language authors need to rethink a bit and find something that works always and should be required.
&lt;/p&gt;
&lt;p&gt;
Part of the impetus for this post is my annoyance when my thinking cycles are wasted on unimportant details in a source code file.  I'd rather have a strict format so that whenever I am reading or writing in a language, there will be a strong and deep consistency.  I don't have to spend time deciding whether I'm going to use some optional syntax or which of the several ways to express the same thing I'm going to use. Similarly, when I come upon someone else's code and it's a mixture of two optional approaches, I feel compelled to go and make it consistent, which is another time waster.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Environment Variables Considered Harmful
    </title>
    <link href="http://peterlyons.com/problog/2010/02/environment-variables-considered-harmful">
    </link><updated>2010-02-14T03:30:14.000Z</updated><id>http://localhost:9400/problog/2010/02/environment-variables-considered-harmful</id>
    <content type="html">&lt;p&gt;Many projects reference environment variables at either build time, install time, or run time to handle configuration that can't be made to work across all of the target environments.  It is better to use plain text simple configuration files for the reasons that follow.  First, let's quickly review common usage of environment variables.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Directory path to supporting tools and libraries (JAVA&lt;em&gt;HOME, LD&lt;/em&gt;LIBRARY&lt;em&gt;PATH, CATALINA&lt;/em&gt;HOME, etc)&lt;/li&gt;
&lt;li&gt;Customization of build time locations (BUILD&lt;em&gt;DIR, OUTPUT&lt;/em&gt;DIR, DIST_DIR, etc)&lt;/li&gt;
&lt;li&gt;Customization of compiler options and other build time configurations (STATIC_LINK, etc)&lt;/li&gt;
&lt;li&gt;Settings that apply OS-wide and to several programs (http&lt;em&gt;proxy, etc). In theory this would almost make sense.  You set your http&lt;/em&gt;proxy environment variable in one place, and any program that makes HTTP requests respects that setting.  In practice, these settings are more realistically effective higher up in your desktop environment, and AFAIK in the whole GNU/Linux/UNIX ecosystem, there are only a small handful of cross-program environment variables that are actually used commonly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So what's the problem with environment variables?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The are ephemeral, nebulous, stored in memory within your shell and process tree&lt;/li&gt;
&lt;li&gt;How and where they are set is inconsistent across shells (~/.bash_profile, ~/.zshrc, etc)&lt;/li&gt;
&lt;li&gt;The syntax to specify them is needlessly different across different shells (csh vs. bash vs. cmd.exe, etc)&lt;/li&gt;
&lt;li&gt;How to fully unset them varies per shell and is often unclear&lt;/li&gt;
&lt;li&gt;There is widespread confusion on the distinction between shell variables and environment variables, how to set each, and how each interacts with subprocesses&lt;/li&gt;
&lt;li&gt;They are often tied to a user account due to where they are specified above, and can vary between login shell verses non-login shell. They can therefore often vary when a program runs via init compared to run from an interactive root login shell.  This can be difficult to detect and troubleshoot&lt;/li&gt;
&lt;li&gt;They are rife with &lt;a href=&quot;https://www.securecoding.cert.org/confluence/pages/worddav/preview.action?pageId=3524&amp;fileName=Environment+Variables+v3.pdf&quot;&gt;major security concerns&lt;/a&gt; and a common attack vector&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these reasons combined mean that in general environment variables are losers in our goal of managing complexity and making simple, easy to use software that is cross platform.  So what's the solution?  The solution, as it so often is, is simple plain text configuration files.  At the end of the day, environment variables end up set in a shell script as KEY=VALUE type pairs, and that's where they belong in a configuration file on the filesystem. How does this make things better?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One consistent place to set your application's configuration&lt;/li&gt;
&lt;li&gt;Same syntax regardless of shell, programming language or OS&lt;/li&gt;
&lt;li&gt;Files on disk are concrete and reliable. You can email it to someone for help with troubleshooting and be confident about its content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So go forth and configure with simple plain text configuration files.  And there will be much rejoicing.&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Business hours
    </title>
    <link href="http://peterlyons.com/problog/2009/09/business-hours">
    </link><updated>2009-09-22T10:38:56.000Z</updated><id>http://localhost:9400/problog/2009/09/business-hours</id>
    <content type="html">&lt;p&gt;NOTICE:  To all businesses with a single physical location and a web site.  You will put your address, phone number, and business hours on your home page.  There will not be a &quot;Contact&quot; page. There will not be an &quot;About&quot; page.  END OF NOTICE
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>The wheel of not waiting
    </title>
    <link href="http://peterlyons.com/problog/2009/09/the-wheel-of-not-waiting">
    </link><updated>2009-09-18T08:01:00.000Z</updated><id>http://localhost:9400/problog/2009/09/the-wheel-of-not-waiting</id>
    <content type="html">&lt;p&gt;
So flash videos are everywhere now.  Generally as they load they show some spinning wheel type graphic.  The problem is as an end user, I have no visual differentiation between a video that is loading slowly and a video sitting there waiting for TCP from an overloaded server that is simply never going to work, and certainly not in a timeframe smaller than my attention budget.  Show me whether or not you are getting any data, and I might be willing to wait, but if you have me watching your spinner until your TCP connection times out, you are just frustrating me.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>How to disable wpautop in WordPress blogs
    </title>
    <link href="http://peterlyons.com/problog/2009/05/disable-wpautop">
    </link><updated>2009-05-23T19:53:19.000Z</updated><id>http://localhost:9400/problog/2009/05/disable-wpautop</id>
    <content type="html">&lt;p&gt;
So, when creating a &lt;a href=&quot;http://wordpress.org&quot;&gt;WordPress&lt;/a&gt; blog, even if you are editing in HTML mode, WordPress includes a feature called &quot;wpautop&quot; that will replace any pair of line feed characters in your post markup with a &lt;p&gt; tag.  This is helpful I think in general  for people who blog mostly paragraphs with some links and images.  However, if you blog with more complex markup, this can invalidate your HTML.  I run my HTML through the &lt;a href=&quot;http://validator.w3.org&quot;&gt;W3C HTML Validator&lt;/a&gt; to check it and wpautop can cause validation to fail.  I hunted around online for an easy way to disable this and didn't see one, so I made the changes described below.
&lt;/p&gt;
&lt;p&gt;
One thing to keep in mind is that if you HAVE been relying on wpautop and you have not been including your own explicit &lt;p&gt; tags, disabling wpautop will cause all your paragraphs to run together and thus your layout will be broken.  To prepare for this, pre-edit all your posts so they have the paragraph tags and remove extra blank lines from them.  You can check how they look in that state since when there are no blank lines wpautop won't do anything.  Once they look good like that, you can disable wpautop.
&lt;/p&gt;
&lt;p&gt;
In your WordPress installation, edit the file &lt;code&gt;wp-includes/formatting.php&lt;/code&gt;.  Search for &quot;function wpautop&quot; and insert the following two lines at the beginning of the function to disable it.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
function wpautop($pee, $br = 1) {
        //plyons disabling this. 20090516
        return $pee;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
Of course, this change will be undone when you upgrade to a newer WordPress release, so it's just a convenient hack.  Once you have your posts with proper paragraph tags and no extra line feeds, wpautop should not change your markup and therefore you shouldn't have a problem when it is re-enabled after a WordPress upgrade.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Maritz: 1 - Very Dissatisfied
    </title>
    <link href="http://peterlyons.com/problog/2009/05/maritz-1-very-dissatisfied">
    </link><updated>2009-05-17T04:27:56.000Z</updated><id>http://localhost:9400/problog/2009/05/maritz-1-very-dissatisfied</id>
    <content type="html">&lt;p&gt;
I bought a new car this fall and a few months later I got a follow-up survey in the mail from Maritz Research.  Having a few pieces of feedback to give, such as the orange readout on the Bose sound system being invisible through Sunglasses,  I endeavored to fill it out.  Holy SAT Test, Batman!  The survey is nine jam-packed, small-font pages long. There are 76 officially numbered questions, but many questions involve dozens of individual line-items. See the example below where question 58 asks you to rate 67 individual aspects of the vehicle! Sixty frigging seven!  I gave up in frustration long before getting there.
&lt;/p&gt;

&lt;p&gt;
This represents a complete failure to do your job as a market research company.  This is their business.  Did they exert any effort to make the customer do less work?  No.  Does the survey include dozens and dozens of line items that completely do not apply to my vehicle because it's not a pick-up truck, and so forth? Yes.  Did they select only the really meaningful things for me to rate?  No.  For example, I am asked to supply my satisfaction level from 5 &quot;Completely Satisfied&quot; to 1 &quot;Very Dissatisfied&quot; on the topic &quot;absence of engine stalling&quot;.  Give me a break.  You need to survey you customers to find out A) your cars stall and B) customers find that unsatisfactory.  Please. Do they have any section for free-form comments, unprompted feedback, or even brief descriptions? No.  Do they have a special &quot;green traffic light&quot; insert stapled in reading &quot;Your Opinion Counts! Pleas Proceed...&quot;?  Yes. Apparently my opinion doesn't count enough for them to create a survey with less complexity than an income tax form.  I'm hunting around for the section to fill in my &lt;a href=&quot;http://www.imdb.com/title/tt0365825/quotes&quot;&gt;non-farm income&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
Please rate your satisfaction with this Maritz Research survey:
&lt;/p&gt;
&lt;input type=&quot;radio&quot; name=&quot;maritz_sat&quot; value=&quot;5&quot; disabled=&quot;disabled&quot;&gt;5 - Completely Satisfied&lt;br&gt;
&lt;input type=&quot;radio&quot; name=&quot;maritz_sat&quot; value=&quot;4&quot; disabled=&quot;disabled&quot;&gt;4 - Very Satisfied&lt;br&gt;
&lt;input type=&quot;radio&quot; name=&quot;maritz_sat&quot; value=&quot;3&quot; disabled=&quot;disabled&quot;&gt;3 - Satisfied&lt;br&gt;
&lt;input type=&quot;radio&quot; name=&quot;maritz_sat&quot; value=&quot;2&quot; disabled=&quot;disabled&quot;&gt;2 - Somewhat Dissatisfied&lt;br&gt;
&lt;input type=&quot;radio&quot; name=&quot;maritz_sat&quot; value=&quot;1&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot;&gt;1 - Very Dissatisfied&lt;br&gt;

&lt;br&gt;

&lt;img src=&quot;/problog/images/maritz_survey_fail_web.jpg&quot; alt=&quot;Maritz Survey Fail&quot;&gt;
    </content>
  </entry>
  <entry>
    <title>How to zip a directory in python
    </title>
    <link href="http://peterlyons.com/problog/2009/04/zip-dir-python">
    </link><updated>2009-04-27T10:04:03.000Z</updated><id>http://localhost:9400/problog/2009/04/zip-dir-python</id>
    <content type="html">I came across this problem at work and also over on &lt;a href=&quot;http://stackoverflow.com/questions/458436/adding-folders-to-a-zip-file-using-python/792199#792199&quot;&gt;this www.stackoverflow.com thread&lt;/a&gt;.  You have a directory and you want to recursively zip it up.  Simple, right?  The equivalent of the unix command &quot;zip myDir.zip myDir&quot;.  Should be like 5 lines of code?  Python even has a built in zipfile module, sweet!  Well, as is often the case (see urllib and friends), python's &quot;batteries included&quot; slogan is more like &quot;enough batteries for 36 seconds included&quot;.  Anyway, it's more like 23 lines of functional code, which is still pretty good, but I would have expected the zipfile module to have this included and not have to use os.walk() to do this.

&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
#!/usr/bin/python
# -*- coding: utf-8 -*-
&quot;&quot;&quot;This is a sample function for zipping an entire directory into a zipfile&quot;&quot;&quot;

#This seems to work OK creating zip files on both windows and linux. The output
#files seem to extract properly on windows (built-in Compressed Folders feature,
#WinZip, and 7-Zip) and linux. However, empty directories in a zip file appear
#to be a thorny issue. The solution below seems to work but the output of
#&quot;zipinfo&quot; on linux is concerning. Also the directory permissions are not set
#correctly for empty directories in the zip archive. This appears to require
#some more in depth research.

#I got some info from:
#http://www.velocityreviews.com/forums/t318840-add-empty-directory-using-zipfile.html
#http://mail.python.org/pipermail/python-list/2006-January/535240.html
import os
import zipfile

def zipdir(dirPath=None, zipFilePath=None, includeDirInZip=True):
    &quot;&quot;&quot;Create a zip archive from a directory.
    
    Note that this function is designed to put files in the zip archive with
    either no parent directory or just one parent directory, so it will trim any
    leading directories in the filesystem paths and not include them inside the
    zip archive paths. This is generally the case when you want to just take a
    directory and make it into a zip file that can be extracted in different
    locations. 
    
    Keyword arguments:
    
    dirPath -- string path to the directory to archive. This is the only
    required argument. It can be absolute or relative, but only one or zero
    leading directories will be included in the zip archive.

    zipFilePath -- string path to the output zip file. This can be an absolute
    or relative path. If the zip file already exists, it will be updated. If
    not, it will be created. If you want to replace it from scratch, delete it
    prior to calling this function. (default is computed as dirPath + &quot;.zip&quot;)

    includeDirInZip -- boolean indicating whether the top level directory should
    be included in the archive or omitted. (default True)

&quot;&quot;&quot;
    if not zipFilePath:
        zipFilePath = dirPath + &quot;.zip&quot;
    if not os.path.isdir(dirPath):
        raise OSError(&quot;dirPath argument must point to a directory. &quot;
            &quot;'%s' does not.&quot; % dirPath)
    parentDir, dirToZip = os.path.split(dirPath)
    #Little nested function to prepare the proper archive path
    def trimPath(path):
        archivePath = path.replace(parentDir, &quot;&quot;, 1)
        if parentDir:
            archivePath = archivePath.replace(os.path.sep, &quot;&quot;, 1)
        if not includeDirInZip:
            archivePath = archivePath.replace(dirToZip + os.path.sep, &quot;&quot;, 1)
        return os.path.normcase(archivePath)
        
    outFile = zipfile.ZipFile(zipFilePath, &quot;w&quot;,
        compression=zipfile.ZIP_DEFLATED)
    for (archiveDirPath, dirNames, fileNames) in os.walk(dirPath):
        for fileName in fileNames:
            filePath = os.path.join(archiveDirPath, fileName)
            outFile.write(filePath, trimPath(filePath))
        #Make sure we get empty directories as well
        if not fileNames and not dirNames:
            zipInfo = zipfile.ZipInfo(trimPath(archiveDirPath) + &quot;/&quot;)
            #some web sites suggest doing
            #zipInfo.external_attr = 16
            #or
            #zipInfo.external_attr = 48
            #Here to allow for inserting an empty directory.  Still TBD/TODO.
            outFile.writestr(zipInfo, &quot;&quot;)
    outFile.close()
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Here's some samples of how you use this:
&lt;/p&gt;

&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
zipdir(&quot;foo&quot;) #Just give it a dir and get a .zip file
zipdir(&quot;foo&quot;, &quot;foo2.zip&quot;) #Get a .zip file with a specific file name
zipdir(&quot;foo&quot;, &quot;foo3nodir.zip&quot;, False) #Omit the top level directory
zipdir(&quot;../test1/foo&quot;, &quot;foo4nopardirs.zip&quot;, False) #exclude some leading dirs
zipdir(&quot;../test1/foo&quot;, &quot;foo5pardir.zip&quot;) #Include some leading dirs
&lt;/pre&gt;
&lt;/div&gt;
    </content>
  </entry>
  <entry>
    <title>Thoughts on Scrum
    </title>
    <link href="http://peterlyons.com/problog/2009/04/thoughts-on-scrum">
    </link><updated>2009-04-17T08:15:07.000Z</updated><id>http://localhost:9400/problog/2009/04/thoughts-on-scrum</id>
    <content type="html">&lt;p&gt;
So I had a chance to work in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Scrum_(development)&quot;&gt;Scrum&lt;/a&gt; methodology for about six months recently. I thought I'd write up some of my thoughts on the experience and the process. Please note that first, this is my first experience working in the Scrum process. I am sure as I use it more these opinions will change. Second, these are strictly my personal thoughts and opinions and in no way represent the experience of my team or anyone else.  Third, my project had a specific set of tasks and particulars.  On a different project, with more sprints or shorter tasks or other relevant variations, I might have drawn (and may well draw in the future) different conclusions.
&lt;/p&gt;
&lt;p&gt;
Overall, agile and Scrum are a vast improvement over the &lt;a href=&quot;http://en.wikipedia.org/wiki/Waterfall_model&quot;&gt;waterfall model&lt;/a&gt; traditionally used at large companies. Some of the specifics benefits:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Far less time wasted doing detailed design and estimates for huge amounts of work that will take a long time to build, or very often might not get built at all&lt;/li&gt;

&lt;li&gt;Overall a very steady and metered workload. We did hit a few days of mad dash toward the end of one or two sprints preparing for the demo, but much more stable than the wild variances and unpredictability of waterfall&lt;/li&gt;

&lt;li&gt;The focus on build and test automation really does enable better agility in the code and prevent regressions&lt;/li&gt;

&lt;li&gt;Having something demoable every month is just all around good for all parties involved. I think perhaps this is the single most important piece of the methodology. Ignore this and I bet a substantial amount of the benefit of Scrum would be lost.&lt;/li&gt;

&lt;li&gt;Similar to demoable, the notion of &quot;potentially shippable product&quot; really rings true for me and forces you to deal with the issues that often lurk in the shadows in the waterfall model only to jump out at the last minute: installation, upgrade, documentation, fit and finish, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
So the remainder of this will mostly comment on things I found to be problematic or confusing, but I want it to be clear that I am a huge proponent of agile and Scrum and this is not meant to be an argument that the status quo is better. Quite the contrary.
&lt;/p&gt;

&lt;h3&gt;The Backlog and User Stories&lt;/h3&gt;
&lt;p&gt;
I want to opine a bit about this notion of &quot;User Stories&quot;. Conceptually, this feels like a perfectly healthy way to constantly remind the team to be focused on the important things that will be valuable to the end users. I think this works best for user interfaces and web development where the ratio of engineering effort to visible, tangible change in the end user interface or experience is high. However, I think in many other areas of software development, such as embedded systems, and in our case complex enterprise software, one user story can generate a boatload of tasks and there end up being a lot of backlog items that are just engineering internal stepping stones to functionality. In our team, we were able to bridge this gap and refer to the work items in the backlog just as general &quot;backlog items&quot;, but I still felt a bit guilty adding more raw engineering items to the backlog even though it would be a bit of a stretch to tie them closely to a user story. I think we managed to get beyond this eventually and I don't think we strayed off into the woods of unimportant engineering diversions. However, in many if not most backlog items, we were not able to apply the &quot;As a $USER_ROLE, I want to $ACTION so I can $BENEFIT&quot; user story template.
&lt;/p&gt;
&lt;p&gt;
I was just browsing around &lt;a href=&quot;http://www.openagile.com&quot;&gt;www.openagile.com&lt;/a&gt; and noticed they call the backlog the &quot;Work Queue&quot;. I like this name better because it more easily allows the notion of &quot;anything that requires work&quot; can go in there and I dislike the word &quot;backlog&quot; because it connotes a buildup of work debt that for me has negative motivational effects.
&lt;/p&gt;
&lt;p&gt;
Secondly, I found some problems trying to implement this idea that a user story should fit on a &quot;story card&quot; the size of an index card and be fleshed out through face to face conversation. The software I develop at work tends to have a high complexity level and attention to detail. It's more complex than web development. Our work items often can't be clearly expressed with something straightforward like &quot;As a shopper, I want to see the total cost of the items in my shopping cart in the page header&quot;. The problem with discussion is A) our team was in three cities with no more than two people in any single location B) we wanted something in writing that could be referred to over and over again throughout and after the sprint and C) no one could remember the details otherwise. Due to C), if you asked the product owner (1/2 me) to clarify a user story through conversation on sprint day 2, 12, and 16, you were liable to get three variations. Our stories ended up being usually at least a few paragraphs worth of detail plus a smattering of acceptance tests, and I think that is OK and worked better for us.
&lt;/p&gt;
&lt;p&gt;
Also, it turns out that populating and maintaining the product backlog is actually a large amount of work. Keeping the backlog items detailed enough, in the right order, with good acceptance tests takes an awful lot of time. Normally the product owner is not on the scrum team.  However, at my company we're not adopting that aspect at this time (for several reasons), and in this project myself and one other scrum team member acted as the product owner role. Basically after working hard to get the sprint review demo up and working, my co-product-owner and I would have about 2 hours Friday afternoon while we were completely fried and 1 hour Monday morning to try to whip the backlog into shape for the next sprint planning meeting Monday morning.  In retrospect, we should have allocated about two full days per sprint just for care and feeding of the product backlog. For the early sprints, this number might be larger - like a week, and then as the effort congeals, the amount of time the product owner needs to allocate to backlog care and feeding will probably grow shorter, unless a major change of requirements comes down the pike.
&lt;/p&gt;
&lt;p&gt;
One final point about the backlog or work queue. After several sprints, we ended up having a fairly enormous work queue with hundreds of items. This became really unwieldy to deal with as a flat list ordered by priority. We ended up wanting to leave the items in the sprint ordered by priority, and about a sprint or two's worth of work items ordered by priority in the backlog, but for all the stuff further down the road, it was much easier to group those into folders based on functionality. I guess each product owner's mileage may vary here, but my point is do whatever it is you need to do as a product owner to be able to comprehend and manipulate your work queue with facility.
&lt;/p&gt;
&lt;h4&gt;User Stories and Product Backlog summary and suggestions:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Use the terms &quot;Work Queue&quot; and &quot;Work Item&quot; instead&lt;/li&gt;

&lt;li&gt;Don't feel obligated to use the &quot;As an X, I want to Y, so I can Z&quot; template if it is unnatural (But do stick with it when it is appropriate)&lt;/li&gt;

&lt;li&gt;Put how ever much detail is needed into your Work Items&lt;/li&gt;

&lt;li&gt;Allocate sufficient Product Owner time to keep the work queue healthy&lt;/li&gt;

&lt;li&gt;Do what's needed to organize large work queues for easy manipulation by the product owner&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Sprint Planning, Estimation, and Time Tracking&lt;/h3&gt;
&lt;p&gt;
In terms of planning and estimating, even with the shorter four week sprints, there are still some difficulties built into the Scrum process in my experience. We used story points, a somewhat abstract relative unit of measurement designed to express relative difficulties between two stories, not necessarily any absolute measure of time, effort, or difficulty. For me personally, this just does not feel natural or come easily. First, I don't think about estimates in relative comparison normally. I don't think task A is easy, and task B is four times as hard as task A. As a general rule, I think most people don't think accurately in terms of multiplicative values. Conceptualizing the idea that 13 story points is 6.5 times more effort than 2 story points doesn't come naturally for me. Given any two stories, I can tell you which one is harder, and I could probably use that basic operation to sort a list of tasks by difficulty, but computing the relative sizes of two along the Fibonacci scale is awkward for me. Secondly, the seemingly arbitrary and perhaps a bit weird use of the Fibonacci sequence just sticks out a bit to me as obtuse. Here's my suggested improvements. When it comes to story points, I really believe you need three and only three values: small, medium, large. Fibonacci gives us 1 2 3 5 8 13 21, but 1s and 2s were not particularly common in our backlog, and when we saw a 13 we generally panicked and broke it into smaller pieces. That's what I suggest. If you have three or four stories that are truly super easy (1 or 2 in Fibonacci story points), just stick them all into one story and call it small. If you have something that seems very hard, like it's going to take one developer half the sprint to do it, break it up into a large and a few mediums. I think the &quot;small, medium, large&quot; terminology has another benefit of being obvious even when discussing with someone non-technical or not familiar with Scrum or Story Points. No explanation is required. Story Points using Fibonacci are subtle enough to require explanation, and perhaps they don't add enough value to justify their subtlety.
&lt;/p&gt;
&lt;p&gt;
OK, so now onto the sprint planning meeting. The default scrum schedule has a single marathon sprint planning meeting at the beginning of each sprint. For our team of five developers with four week sprints, we are looking to estimate somewhere around 600 hours worth of tasks ranging in granularity from 4-30 hours. It's just too much to focus on in a single session (again, my opinion). After two and a half hours of this, I'm bleary-eyed and fried and unable to motivate myself to do the kind of careful thinking required to make the task lists complete and accurate. I start falling into wanting to list the same three tasks for every story: 1. Figure it out 2. Code it 3. Test it. My suggestion is that this be done for one hour a week as many as four times during the sprint if needed (probably 3 of these will get the job done) just to break it up into manageable chunks.
&lt;/p&gt;
&lt;p&gt;
Now, another point on the sprint planning meetings. Our goal was to take user stories that had story points and acceptance criteria and define a full set of tasks and estimates for five developer-months worth of time in a single session, and then try not to have to add/remove/change during the sprint. (Note, the try not to have to add/remove/change is my own understanding, although my editor pointed out that Scrum itself has no such restriction and allows for task adjustments - focusing only on remaining work. However, it seems the goal is to define the tasks to whatever degree possible at the beginning or we wouldn't bother doing it in the first place, but then adjustments are accomodated). In any case, I wonder whether this is a realistic, achievable goal. Or let me say that in my experience at least about 10% churn in the tasks as development progresses (regardless of what overall methodology I am working within) seems to be my limit and I can't get it more accurate than that up front. So if the team is OK with somewhere around 10% task churn mid-sprint, then all is well.
&lt;/p&gt;
&lt;p&gt;
When it comes to estimating in hours an individual task in the backlog, our team was developing a new product from scratch with a team of all highly experienced engineers. So we actually ended up having pretty good interchangeability between developers where several different people could complete a task and generally take about the same amount of time to do it. However, when working on an big existing code base, it can be an order of magnitude difference in the estimate between when someone who has already learned a complex subsystem and implemented a similar change does a task verses someone else doing it for the first time.  I think if we want real accuracy we need some way to model this more accurately. I'm not sure how best to do this. Maybe one task for learning curve that gets skipped if it's not needed because the person who completes the task didn't need it? I don't have a good suggestion to improve this yet. Also, along similar lines, in general I always get nervous when one person (or a team) creates estimates for work that will be completed by another person. I think there's just a lot of risk there. Again, I don't have an alternative to propose at this time.
&lt;/p&gt;
&lt;p&gt;
With regard to ordering the work queue items in the queue, and thus determining which ones get into the sprint, I think in general the scrum notion that the product owner does this and it's based on business value to the product owner is reasonable and beneficial for the most part. However, we also tried to adhere to this order for the order in which items were implemented during the sprint, and I think in many cases this didn't work out as well as it could have. Even in the backlog, I feel that a certain amount of the time, a logical or technical dependency will suggest a different backlog item order than strict product owner importance. But for now I'm OK with the product owner importance ordering for the backlog. However, inside the sprint, I think it might work out better to allow the scrum team a certain amount of discretion and control of the order of implementation on any technical or logistical grounds they feel will help. Within a sprint, we bumped up against a fair number of interdependencies where two people would need to heavily edit the same source code file on the same day, or one person was blocking waiting for another person to complete some code needed to build their feature. I can understand that taking the most important item and implementing it last is probably not acceptable, but I think some well-reasoned adjustment should be allowed. Ultimately, I think more stories will get completed due to the efficiency gains. I think a good guideline would be just to discuss these proposed changes to implementation order with the product owner during the daily Scrum call, and based on the status of the burn down chart and how things seem to be going, the product owner may permit or veto a re-ordering.
&lt;/p&gt;

&lt;h4&gt;Sprint Planning, Estimation, and Time Tracking summary and suggestions:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Use small, medium, large instead of Fibonacci story points&lt;/li&gt;

&lt;li&gt;Divide the sprint planning across several meetings throughout the sprint, never exceeding the motivation/attention span threshold for this somewhat tedious endeavor&lt;/li&gt;

&lt;li&gt;Define criteria under which it is OK for developers to add/remove/change tasks during the sprint&lt;/li&gt;

&lt;li&gt;May need some way to estimate tasks differently depending on who implements them&lt;/li&gt;

&lt;li&gt;Within a single sprint, team should be allowed to make implementation order changes that will help with efficiency with consent of the product owner&lt;/li&gt;
&lt;/ul&gt;
    </content>
  </entry>
  <entry>
    <title>Code Conventions
    </title>
    <link href="http://peterlyons.com/problog/2009/03/code-conventions">
    </link><updated>2009-03-15T03:06:23.000Z</updated><id>http://localhost:9400/problog/2009/03/code-conventions</id>
    <content type="html">Here's a link to &lt;a href=&quot;/code_conventions.html&quot;&gt;my article on code conventions&lt;/a&gt;.  Feel free to post comments here.
    </content>
  </entry>
  <entry>
    <title>How to run two wordpress blogs on one web site
    </title>
    <link href="http://peterlyons.com/problog/2009/03/how-to-run-two-wordpress-blogs-on-one-web-site">
    </link><updated>2009-03-15T02:57:53.000Z</updated><id>http://localhost:9400/problog/2009/03/how-to-run-two-wordpress-blogs-on-one-web-site</id>
    <content type="html">&lt;p&gt;
There is ample detailed information out there on installing wordpress.  However, I wanted to just provide a small supplement about setting up two distinct wordpress blogs within a single apache2 web site. The system I am using is Ubuntu Linux 8.10, but other than the package installation, the configuration steps should be the same on other linux distributions.
&lt;/p&gt;

&lt;p&gt;
As a first step, read through the &lt;a href=&quot;http://codex.wordpress.org/Getting_Started_with_WordPress#Installation&quot;&gt;Wordpress Installation Instructions&lt;/a&gt;.  You will find them to be thorough and clear. The starting point for my setup is that I already had a web site up and running under apache2.  I just wanted to add the Wordpress (and underlying MySQL database) setup and have two separate blogs with separate themes and content.
&lt;/p&gt;

&lt;p&gt;
So here's my starting setup:
&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;Ubuntu 8.10 on an amd64 system&lt;/li&gt;
    &lt;li&gt;apache2 already installed and working&lt;/li&gt;
    &lt;li&gt;static content for the web site deployed in &lt;code&gt;/var/www/example.com&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;MySQL and Wordpress are not yet installed&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Install wordpress and mysql&lt;/h4&gt;

&lt;p&gt;First let's install wordpress and mysql.  I'll do this on the command line using the &lt;code&gt;apt-get&lt;/code&gt; program, but you can &lt;a href=&quot;https://help.ubuntu.com/8.10/add-applications/C/advanced.html&quot;&gt;use one of the graphical options as well&lt;/a&gt;&lt;/p&gt;

&lt;pre&gt;sudo apt-get install wordpress virtual-mysql-server&lt;/pre&gt;

&lt;p&gt;
You should see a bunch of packages that will get installed and press &lt;code&gt;y&lt;/code&gt; to proceed.  The MySQL install will prompt you to create a new mysql root account password, so go ahead and do that.
&lt;/p&gt;

&lt;h4&gt;Set up the wordpress databases&lt;/h4&gt;

&lt;p&gt;OK, so let's say we are going to call our blogs blog1 and blog2.  We need to create mysql databases for them.  Note that you may see tutorials telling you you can store the data for two separate blogs in one database.  While true, two databases is a much cleaner way to go.  End users shouldn't be going around making up database table names.  So, we are going to use the mysql command line tools to do this.  Again, the wordpress docs here are fine and describe graphical alternatives as well. Let's connect to mysql (use the password you created above) as root and create the databases.  We'll call them blog1 and blog2 and we'll also set up user accounts inside mysql that wordpress will use to access the databases.  Again for simplicity, we'll also call the user accounts blog1 and blog2.  Replace &quot;MakeUpPassword&quot; with your own chosen password.&lt;/p&gt;

&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;mysql -u root -p
create database blog1;
GRANT ALL PRIVILEGES ON blog1.* TO &quot;blog1&quot;@&quot;localhost&quot; IDENTIFIED BY &quot;MakeUpPassword&quot;;
flush privileges;
create database blog2;
GRANT ALL PRIVILEGES ON blog2.* TO &quot;blog2&quot;@&quot;localhost&quot; IDENTIFIED BY &quot;MakeUpPassword&quot;;
flush privileges;
quit;
&lt;/pre&gt;
&lt;/div&gt;
&lt;h4&gt;Install wordpress files and configure DB access&lt;/h4&gt;

&lt;p&gt;OK, when we installed wordpress above, Ubuntu put a copy of the wordpress PHP files into &lt;code&gt;/usr/share/wordpress&lt;/code&gt;, so now we're going to make 2 copies, one for each blog, underneath our web site's document root.  Note that since these are two blogs in the same web site, we don't want either blog to be the top level home page of the site, so each gets its own separate subdirectory.
&lt;/p&gt;

&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
sudo mkdir -p /var/www/example.com/blog1 /var/www/example.com/blog2
sudo cp -r /usr/share/wordpress/* /var/www/example.com/blog1
sudo cp -r /usr/share/wordpress/* /var/www/example.com/blog2
sudo chown -R www-data:www-data /var/www/example.com/blog*
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
Now I should note that Debian/Ubuntu has a customized wordpress configuration.  Often, these are well crafted by the experts and will save you a lot of time and hassle if you  follow the patterns they suggest.  In this case, I don't think their setup exactly matches my goal of two different blogs under one web site, so I am bypassing their pattern and doing my own simple alternative.  So what happens is that by default the file &lt;code&gt;/usr/share/wordpress/wp-config.php is a symbolic link to /etc/wordpress/wp-config.php&lt;/code&gt;, and when we copied these files, that symlink was copied too, which means if we aren't careful both of our blogs will point at the same database, making them one blog instead of two!  So we do the following:
&lt;/p&gt;

&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
sudo rm /var/www/example.com/blog*/wp-config.php
sudo cp /usr/share/wordpress/wp-config-sample.php /var/www/example.com/blog1/wp-config.php
sudo cp /usr/share/wordpress/wp-config-sample.php /var/www/example.com/blog2/wp-config.php
sudo chown -R www-data:www-data /var/www/example.com/blog*
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;OK, now each blog has it's own separate copy of wp-config.php.  Go ahead and edit those files to point blog1 at the blog1 database and blog2 at the blog2 database using vi or your text editor of choice.  When done, they should look as follows:
&lt;/p&gt;


&lt;pre&gt;/var/www/example.com/blog1/wp-config.php&lt;/pre&gt;

&lt;br/&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
&lt;?php
// ** MySQL settings ** //
define('DB_NAME', 'blog1');    // The name of the database
define('DB_USER', 'blog1');     // Your MySQL username
define('DB_PASSWORD', 'yourpasswordhere'); // ...and password
define('DB_HOST', 'localhost');    // 99% chance you won't need to change this value
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');

// Change SECRET_KEY to a unique phrase.  You won't have to remember it later,
// so make it long and complicated.  You can visit http://api.wordpress.org/secret-key/1.0/
// to get a secret key generated for you, or just make something up.
// Change this to a unique phrase.
define('SECRET_KEY', 'put your unique phrase here -- yes you change this now');

// You can have multiple installations in one database if you give each a unique prefix
$table_prefix  = 'wp_';   // Only numbers, letters, and underscores please!

// Change this to localize WordPress.  A corresponding MO file for the
// chosen language must be installed to wp-content/languages.
// For example, install de.mo to wp-content/languages and set WPLANG to 'de'
// to enable German language support.
define ('WPLANG', '');

/* That's all, stop editing! Happy blogging. */

define('ABSPATH', dirname(__FILE__).'/');
require_once(ABSPATH.'wp-settings.php');
?&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;br/&gt;
&lt;pre&gt;/var/www/example.com/blog2/wp-config.php&lt;/pre&gt;

&lt;br/&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
&lt;?php
// ** MySQL settings ** //
define('DB_NAME', 'blog2');    // The name of the database
define('DB_USER', 'blog2');     // Your MySQL username
define('DB_PASSWORD', 'yourpasswordhere'); // ...and password
define('DB_HOST', 'localhost');    // 99% chance you won't need to change this value
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');

// Change SECRET_KEY to a unique phrase.  You won't have to remember it later,
// so make it long and complicated.  You can visit http://api.wordpress.org/secret-key/1.0/
// to get a secret key generated for you, or just make something up.
// Change this to a unique phrase.
define('SECRET_KEY', 'put your unique phrase here -- yes you change this now');

// You can have multiple installations in one database if you give each a unique prefix
$table_prefix  = 'wp_';   // Only numbers, letters, and underscores please!

// Change this to localize WordPress.  A corresponding MO file for the
// chosen language must be installed to wp-content/languages.
// For example, install de.mo to wp-content/languages and set WPLANG to 'de'
// to enable German language support.
define ('WPLANG', '');

/* That's all, stop editing! Happy blogging. */

define('ABSPATH', dirname(__FILE__).'/');
require_once(ABSPATH.'wp-settings.php');
?&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;h4&gt;Permalinks&lt;/h4&gt;
&lt;p&gt;
OK, now let's make sure we have mod_rewrite enabled for wordpress permalinks, then we restart apache2 to get our new software and config to take effect. We'll also make sure we have a writeable .htaccess file so wordpress can set it up for us.
&lt;/p&gt;

&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
sudo a2enmod rewrite
sudo apache2ctl restart
sudo touch /var/www/example.com/blog1/.htaccess
sudo touch /var/www/example.com/blog2/.htaccess
sudo chown www-data:www-data /var/www/example.com/blog*/.htaccess
sudo chmod 644 /var/www/example.com/blog*/.htaccess
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Almost done, we can now point a web browser to &lt;a href=&quot;http://localhost/blog1/wp-admin/install.php&quot;&gt;http://localhost/blog1/wp-admin/install.php&lt;/a&gt; and fill out that form and launch the wordpress self-install.  Repeat the process for blog2 at &lt;a href=&quot;http://localhost/blog2/wp-admin/install.php&quot;&gt;http://localhost/blog2/wp-admin/install.php&lt;/a&gt;. Now, this is going to set up the blog in the database and create the admin user with a default password.  If your computer has a mail transport agent running, you should get the email with the default password.  If not, you won't, which is what happened to me, so we can just set the admin password to one of our choosing.  First, we need to know the MD5 checksum of our chosen password. You can use &lt;a href=&quot;http://epleweb.com/md5/&quot;&gt;this online form&lt;/a&gt; to get the MD5 of your password, but sending your password to some random web site in clear text is too insecure for me.  Therefore, if you have python, you can use this little python one-liner to do it.  This will securely prompt you for your password and print out the corresponding MD5.  Nothing is sent over the network. You will end up with a 32-character hex string.  Use this instead of the  For details from wordpress, read &lt;a href=&quot;http://codex.wordpress.org/Resetting_Your_Password&quot;&gt;resetting your password&lt;/a&gt; in the wordpress online docs.  The cheat sheet is below.
&lt;/p&gt;

&lt;pre&gt;python -c &quot;import getpass,md5;m=md5.new();m.update(getpass.getpass());print m.hexdigest()&quot;&lt;/pre&gt;

&lt;p&gt;
OK, so let's set this password into mysql so we can start managing our blog
&lt;/p&gt;

&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;
mysql -u root -p
use blog1;
update wp_users set user_pass=&quot;useyour32charhexstringmd5here&quot; where user_nicename=&quot;admin&quot;;
use blog2;
update wp_users set user_pass=&quot;useyour32charhexstringmd5here&quot; where user_nicename=&quot;admin&quot;;
quit;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
OK, you are good to go to &lt;a href=&quot;http://localhost/blog1&quot;&gt;http://localhost/blog1&lt;/a&gt;, log in as admin, and start managing your blog. You can go in and set up custom pretty permalinks in the admin section and when you save, wordpress should be able to write the rewrite settings to the .htaccess file we set up for you. Good luck and happy blogging!
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Music subscription and Rhapsody
    </title>
    <link href="http://peterlyons.com/problog/2009/03/music-subscription-and-rhapsody">
    </link><updated>2009-03-15T02:54:03.000Z</updated><id>http://localhost:9400/problog/2009/03/music-subscription-and-rhapsody</id>
    <content type="html">&lt;p&gt;
Flat fee music subscription service has changed my life.  I wish there was some less dramatic way to put it, but it's the honest truth.  I don't remember exactly when, but somewhere in late 2006 probably, after reading &lt;a href=&quot;http://www.joelonsoftware.com/items/2006/11/10.html&quot;&gt;this Joel on Software - The infinite music collection&lt;/a&gt; article, I was intrigued by the idea and did a little shopping.  I know I looked at Napster, Virgin, Yahoo, and Rhapsody at least.  I think I initially went with Rhapsody because of the Sonos integration.  I experimented for a bit listening on the computer and then I think that Christmas I asked for a basic set of Sonos gear, which was and still is exorbitantly priced.
&lt;/p&gt;

&lt;p&gt;
OK, so first some comments on the whole notion of subscription music service.  It's amazing.  For someone like me with a voracious appetite for new music cultivated spending long hours every week in the fantastic &lt;a href=&quot;http://www.oberlin.edu/library/con/&quot;&gt;Oberlin Conservatory Music Library&lt;/a&gt;, it was complete drooling brain-fry.  It was a bit overwhelming at first.  There were numerous aspects of this arrangement that were just awesome.  Obviously, the size of the library is number one.  Having extremely ecclectic and somewhat obscure taste in music, I was worried that it would be basically a top-40 archive that I would quickly grow bored with.  This was not the case.  The collection of jazz, classical, and less main stream stuff was really quite good.  Now, every CD in my personal library is not available, but the service library as a whole is so infinitely broad and compelling that I don't care that much.
&lt;/p&gt;

&lt;p&gt;
Secondly, there is the complete removal of the financial penalty for exploring.  Before I expound on this let me just state that I listen to whole albums in totality in order.  Fuck shuffle.  Fuck 30 second previews. I usually listen to complete albums start to finish numerous times before I decide how I feel about them.  In addition, when checking out a new artist, I actually prefer to listen to all the albums in chronological order.  Sometimes I'll scan a &quot;best of&quot; to see if it's worth my time, but usually I go straight for the debut album.  For years, I would gather suggestions from peers, and then go plunk down $17 for a CD.  No more.  Now I can try relentlessly at full speed for a flat fee.  This is fantastic and significant.  Now I can actually listen to artists I know I don't care for, if only to better understand what it is about them that I don't like.
&lt;/p&gt;
&lt;p&gt;
Third, in the jazz and classical genres, musicians are vastly more prolific compared to their pop/rock peers.  Go try to get the complete recordings of Miles Davis.  Be sure to bring like $1500.  With a flat fee subscription, I can go and listen to LOTS of music by folks I like.  One of the first things that got me completely hooked on this was listening to a 10-disc set of Steve Reich: Works 1965-1995.  This retails at &lt;a href=&quot;http://www.amazon.com/Steve-Reich-1965-1995/dp/B000005J4P/ref=pd_bbs_sr_1?ie=UTF8&amp;s=music&amp;qid=1236457622&amp;sr=8-1&quot;&gt;amazon.com for $99.98&lt;/a&gt;.  So the trade-off for this ONE collection is own this collection forever or get over six months access to millions of songs online.
&lt;/p&gt;
&lt;p&gt;
Fourth, you can compare recordings to your heart's content.  I have a physical copy of the venerable &lt;a href=&quot;http://www.amazon.com/Bela-Bartok-Quartets-Emerson-Quartet/dp/B000001G9O/ref=sr_1_1?ie=UTF8&amp;s=music&amp;qid=1236532264&amp;sr=1-1&quot;&gt;Emerson String Quartet's recording of the six Bela Bartok String Quartets&lt;/a&gt;. Currently going for $22 on amazon.  It is a cherished record in my collection.  However, the chances of me buying another recording of this work are slim to none, even though I would love to hear various interpretations.  Rhapsody has no less than ten complete recordings of these works available!  When it comes to jazz and you want to learn a new song, Rhapsody is going to give you about thirty recordings to choose from.  No better way to truly absorb the song's full meaning from the repertoire by listening to numerous different recordings.
&lt;/p&gt;
&lt;p&gt;
Fifth, there is no personal music library management.  I've spent hours and hours labeling my CD collection (over 800 discs), ripping them, fixing the cddb track metadata, transferring stuff to portable devices, re-transfering it when it gets corrupted, etc.  Then there's the idea that I have to keep this all backed up.  I have so far ripped about 30 GB of music and don't really want to deal with backing that up.  With a subscription, all the music is just instantly there.  It's all indexed and searchable.  The track metadata is correct. I don't have to personally back it all up.
&lt;/p&gt;
&lt;p&gt;
Sixth, it works with portable players in a reasonable way.  I think for the next Christmas I asked for the &lt;a href=&quot;http://reviews.cnet.com/mp3-players/sandisk-sansa-e280r-rhapsody/4505-6490_7-32102346.html&quot;&gt;SanDisk Sansa e280R portable MP3 player&lt;/a&gt; that is integrated with Rhapsody.  You can download tracks to the device and as long as you connect it once a month to verify your subscription is active, you are good to go.  It works pretty well and doesn't really add any hassle.  I'll be posting another entry soon about the various devices (basically all bad) I have used with Rhapsody and how they work.
&lt;/p&gt;
&lt;p&gt;
So I'm totally hooked on this model of flat fee all you can eat subscription music.  I was initially concerned that streaming this in real time would not work, but honestly there have been almost no glitches.  Either the service is working or it isn't, but it doesn't do any buffering interrupting the song or anything like that, which would be a complete deal-breaker. To try to listen to this much music on &lt;a href=&quot;http://www.itunes.com&quot;&gt;iTunes&lt;/a&gt; would cost me hundreds of dollars a month.
&lt;/p&gt;
&lt;p&gt;
So, let's look at some of the cons.  There are no linear notes.  I do miss that.  I also miss detailed personnel listings per track.  However, when I really am interested in that, the info is usually available online if I can remember to go look it up.  As of now, in order for this to work, it relies on Plays For Sure Digital Right Management (DRM) scheme.  Generally I am against DRM, but if it is required in order for the music industry to be willing to make a subscription service available, so be it. 
&lt;/p&gt;
&lt;p&gt;
OK, so that is mostly focussing on the subscription model in the abstract. Let's talk about the Rhapsody service in particular.  After a few years of using it, overall I'd probably give it about 7 out of 10.  It's good, but it has some annoyances.  On the plus side, the web based Rhapsody online product, which was formerly a proprietary plug-in that gave me a few hassles on Linux, is now entirely standard flash based app.  In general it works great on linux however my new laptop I installed 64 bit Ubuntu and the rhapsody flash app won't log in currently.  I'll be contacting support (sigh) to yell at them to make it work.
&lt;/p&gt;
&lt;p&gt;
So some of the annoyances include inability to re-order tracks in your queue/playlist.  You can append to the end of it and delete items, but you can't reorder.  Again, since I generally listen to whole albums it doesn't bother me much, but sometimes it can be a real pain.  Also, their web site used to have a frigging NORMAL LOGIN HTML FORM, thus I could save my credentials in my browser and not be bothered with it.  Now the flash app itself prompts for credentials which means I have to re-type my password a few times a day.  I asked support to make it go back to the old way but you can guess how that went.
&lt;/p&gt;
&lt;p&gt;
Reliability and bill payment have been good.  The Windows application that I need to use to actually transfer music to a portable player is decent.  It's better than the flash app, but still a bit cumbersome.  It's got a web browser pane embedded in it, which has all the annoyances of a web browser, but not all the screen components and tools to properly manage it. Here they have a proper playlist editor window. Ideally I would be able to transfer to my portable from a linux application, but I have realistic expectations here.  It ain't going to happen.
&lt;/p&gt;
&lt;p&gt;
Rhapsody has user and celebrity playlists, both of which are a great way to explore and check out new stuff.
&lt;/p&gt;
&lt;p&gt;
So if you are a big music fan, and still haven't tried a subscription service, give it a shot.  I can never go back.  Look for more posts soon about my experierces with music player devices over the years.
&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Announcing Pete's Points
    </title>
    <link href="http://peterlyons.com/problog/2009/03/announcing-petes-points">
    </link><updated>2009-03-15T02:53:24.000Z</updated><id>http://localhost:9400/problog/2009/03/announcing-petes-points</id>
    <content type="html">I have decided to start a &quot;professional&quot; blog.  I use the word &quot;professional&quot; only in the sense of &quot;not personal&quot;.  I will post about software engineering, computers, consumer electronics, and music technology primarily.  You may want to read this  &lt;a href=&quot;/problog/about/&quot;&gt;description of what this blog is all about&lt;/a&gt;.  I hope you enjoy it and look forward to any conversations this generates.
    </content>
  </entry>
</feed>
