<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.harukizaemon.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">

    <title>My hovercraft is full of eels</title>
    
    <link href="http://www.harukizaemon.com/" />
    <updated>2010-02-25T03:32:35-08:00</updated>
    <id>http://www.harukizaemon.com/</id>
    <author>
        <name>Simon Harris</name>
        <email>haruki.zaemon@gmail.com</email>
    </author>

    
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.harukizaemon.com/MyHovercraftIsFullOfEels" /><feedburner:info uri="myhovercraftisfullofeels" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><link rel="license" type="text/html" href="http://creativecommons.org/licenses/by-sa/3.0/" /><entry>
        <title>Tests as documentation</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/1bcS09xumsw/tests-as-documentation.html" />
        <updated>2010-02-11T00:00:00-08:00</updated>
        <id>http://www.harukizaemon.com/2010/02/tests-as-documentation</id>
        <content type="html">&lt;p&gt;Whilst I&amp;#8217;ve been playing around with &lt;a href='http://github.com/harukizaemon/hamster'&gt;immutable collection classes in Ruby&lt;/a&gt;, I&amp;#8217;ve also been working on ways to document behaviour without writing loads of RDOC that goes stale really quickly.&lt;/p&gt;

&lt;p&gt;Tests have always been touted as a form of documentation but I&amp;#8217;ve rarely &amp;#8211; if ever come to think of it &amp;#8211; seen that work in practice. &lt;a href='http://cukes.info/'&gt;Cucumber&lt;/a&gt; comes very close but I wanted something a little closer to the metal, something that allowed me to write unit tests with something like &lt;a href='http://rspec.info/'&gt;RSpec&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For sometime now, I&amp;#8217;d been structuring my specs with this in mind and I thought I was doing a reasonably good approximation. Then today I finally had cause to test my work. As part of a feature I was implementing in another project, I wanted to use a list method I thought provided just what I required but I couldn&amp;#8217;t remember exactly how it behaved or what the interface was so, naturally, I consulted the documentation. D&amp;#8217;oh! But wait, I thought smugly, I&amp;#8217;ve been writing these specs and as we all know, specs are documentation. Moreover, I&amp;#8217;ve been putting in quite a bit of effort to make them read as such so why not go read the specs?&lt;/p&gt;

&lt;p&gt;Suffice to say, they didn&amp;#8217;t live up to my expectations. After running &lt;code&gt;spec -f nested spec/hamster/list/span_spec.rb&lt;/code&gt; the result wasn&amp;#8217;t bad but wasn&amp;#8217;t great either:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='text'&gt;Hamster::List
  #span
    is lazy
    on []
      with a block
        preserves the original
        returns a tuple with two items
        correctly identifies the prefix
        correctly identifies the remainder
      without a block
        returns a tuple with two items
        returns self as the prefix
        leaves the remainder empty
    on [1]
      with a block
        preserves the original
        returns a tuple with two items
        correctly identifies the prefix
        correctly identifies the remainder
      without a block
        returns a tuple with two items
        returns self as the prefix
        leaves the remainder empty
    on [1, 2, 3, 4]
      with a block
        preserves the original
        returns a tuple with two items
        correctly identifies the prefix
        correctly identifies the remainder
      without a block
        returns a tuple with two items
        returns self as the prefix
        leaves the remainder empty
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;For a start, there was no narrative, nothing telling me what the desired outcome was; why do I want to use this method? Secondly, whilst the individual assertions seemed to make sense when reading the spec code, once they were in this purely textual form they were somewhat useless in helping me understand what to expect. And lastly, a purely aesthetic complaint, I didn&amp;#8217;t really like the indentation so much. Right when all that hard work should have paid off, it failed me. But not completely. I was still convinced there was some merit in what I wanted and perhaps a little more tweaking could get me closer to my ideal.&lt;/p&gt;

&lt;p&gt;After a few iterations of modifying the code, running the specs, and reading the output, I finally hit upon something I think is pretty close to what I&amp;#8217;ve been after:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='text'&gt;Hamster.list#span
  is lazy
  given a predicate (in the form of a block), splits the list into two lists
  (returned as a tuple) such that elements in the first list (the prefix) are
  taken from the head of the list while the predicate is satisfied, and elements
  in the second list (the remainder) are the remaining elements from the list
  once the predicate is not satisfied. For example:
    given the list []
      and a predicate that returns true for values &amp;lt;= 2
        preserves the original
        returns the prefix as []
        returns the remainder as []
      without a predicate
        returns a tuple
        returns self as the prefix
        returns an empty list as the remainder
    given the list [1]
      and a predicate that returns true for values &amp;lt;= 2
        preserves the original
        returns the prefix as [1]
        returns the remainder as []
      without a predicate
        returns a tuple
        returns self as the prefix
        returns an empty list as the remainder
    given the list [1, 2, 3, 4]
      and a predicate that returns true for values &amp;lt;= 2
        preserves the original
        returns the prefix as [1, 2]
        returns the remainder as [3, 4]
      without a predicate
        returns a tuple
        returns self as the prefix
        returns an empty list as the remainder
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This time there&amp;#8217;s a narrative describing what the method does, followed by a series of examples not only describing the behaviour but also providing concrete values. Now the output reads more like documentation only rather than duplicated as RDOC that rapidly becomes disconnected from reality, it&amp;#8217;s generated from the tests and automatically stays up-to-date.&lt;/p&gt;

&lt;p&gt;The underlying spec is not perfect by any stretch &amp;#8211; there is certainly a modicum of duplication between the test code and the descriptive text &amp;#8211; but I think it strikes a reasonable balance between tests that are readable as code as well as plain text documentation. I&amp;#8217;d certainly love to know what, if anything, others have done.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expand_path&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;../../../spec_helper&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='bp'&gt;__FILE__&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;

&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;hamster/list&amp;#39;&lt;/span&gt;

&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Hamster.list#span&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;

  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;is lazy&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
    &lt;span class='nb'&gt;lambda&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='no'&gt;Hamster&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;stream&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;item&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='nb'&gt;fail&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;span&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='kp'&gt;true&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should_not&lt;/span&gt; &lt;span class='n'&gt;raise_error&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class='no'&gt;DESC&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;span class='sh'&gt;given a predicate (in the form of a block), splits the list into two lists&lt;/span&gt;
&lt;span class='sh'&gt;  (returned as a tuple) such that elements in the first list (the prefix) are&lt;/span&gt;
&lt;span class='sh'&gt;  taken from the head of the list while the predicate is satisfied, and elements&lt;/span&gt;
&lt;span class='sh'&gt;  in the second list (the remainder) are the remaining elements from the list&lt;/span&gt;
&lt;span class='sh'&gt;  once the predicate is not satisfied. For example:&lt;/span&gt;
&lt;span class='no'&gt;DESC&lt;/span&gt;

    &lt;span class='o'&gt;[&lt;/span&gt;
      &lt;span class='o'&gt;[[]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[]]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='o'&gt;[[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[]]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='o'&gt;[[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[]]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='o'&gt;[[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='o'&gt;]]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='o'&gt;[[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;4&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;4&lt;/span&gt;&lt;span class='o'&gt;]]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='o'&gt;[[&lt;/span&gt;&lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;4&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;4&lt;/span&gt;&lt;span class='o'&gt;]]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='o'&gt;[[&lt;/span&gt;&lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;4&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;4&lt;/span&gt;&lt;span class='o'&gt;]]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='o'&gt;[[&lt;/span&gt;&lt;span class='mi'&gt;4&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;4&lt;/span&gt;&lt;span class='o'&gt;]]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='o'&gt;].&lt;/span&gt;&lt;span class='n'&gt;each&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;values&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;expected_prefix&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;expected_remainder&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;

      &lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;given the list &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;values&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;inspect&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;

        &lt;span class='n'&gt;before&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
          &lt;span class='vi'&gt;@original&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Hamster&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;list&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;values&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='k'&gt;end&lt;/span&gt;

        &lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;and a predicate that returns true for values &amp;lt;= 2&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;

          &lt;span class='n'&gt;before&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
            &lt;span class='vi'&gt;@result&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='vi'&gt;@original&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;span&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;item&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;item&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;=&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
            &lt;span class='vi'&gt;@prefix&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='vi'&gt;@result&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;first&lt;/span&gt;
            &lt;span class='vi'&gt;@remainder&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='vi'&gt;@result&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;last&lt;/span&gt;
          &lt;span class='k'&gt;end&lt;/span&gt;

          &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;preserves the original&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
            &lt;span class='vi'&gt;@original&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='no'&gt;Hamster&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;list&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;values&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
          &lt;span class='k'&gt;end&lt;/span&gt;

          &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;returns the prefix as &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;expected_prefix&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;inspect&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
            &lt;span class='vi'&gt;@prefix&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='no'&gt;Hamster&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;list&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;expected_prefix&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
          &lt;span class='k'&gt;end&lt;/span&gt;

          &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;returns the remainder as &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;expected_remainder&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;inspect&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
            &lt;span class='vi'&gt;@remainder&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='no'&gt;Hamster&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;list&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;expected_remainder&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
          &lt;span class='k'&gt;end&lt;/span&gt;

        &lt;span class='k'&gt;end&lt;/span&gt;

        &lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;without a predicate&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;

          &lt;span class='n'&gt;before&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
            &lt;span class='vi'&gt;@result&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='vi'&gt;@original&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;span&lt;/span&gt;
            &lt;span class='vi'&gt;@prefix&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='vi'&gt;@result&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;first&lt;/span&gt;
            &lt;span class='vi'&gt;@remainder&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='vi'&gt;@result&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;last&lt;/span&gt;
          &lt;span class='k'&gt;end&lt;/span&gt;

          &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;returns a tuple&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
            &lt;span class='vi'&gt;@result&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;is_a?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Hamster&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Tuple&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='kp'&gt;true&lt;/span&gt;
          &lt;span class='k'&gt;end&lt;/span&gt;

          &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;returns self as the prefix&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
            &lt;span class='vi'&gt;@prefix&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;equal&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='vi'&gt;@original&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
          &lt;span class='k'&gt;end&lt;/span&gt;

          &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;returns an empty list as the remainder&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
            &lt;span class='vi'&gt;@remainder&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;be_empty&lt;/span&gt;
          &lt;span class='k'&gt;end&lt;/span&gt;

        &lt;span class='k'&gt;end&lt;/span&gt;

      &lt;span class='k'&gt;end&lt;/span&gt;

    &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/1bcS09xumsw" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2010/02/tests-as-documentation.html</feedburner:origLink></entry>
    
    <entry>
        <title>Lazy spec task creation</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/LjMZ6-UMA-I/lazy-spec-task-creation.html" />
        <updated>2010-01-21T00:00:00-08:00</updated>
        <id>http://www.harukizaemon.com/2010/01/lazy-spec-task-creation</id>
        <content type="html">&lt;p&gt;I converted a Ruby project over to use &lt;a href='http://github.com/wycats/bundler'&gt;Bundler&lt;/a&gt; for gem dependency management today. For the most part it worked flawlessly except, that is, when the CI build ran for the first time after the conversion:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;LoadError: no such file to load -- vendor/gems/environment&lt;/span&gt;

&lt;span class='go'&gt;Stacktrace:&lt;/span&gt;
&lt;span class='go'&gt;tasks/spec.rb:8:in `require&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt;tasks/spec.rb:8:in `&amp;lt;top (required)&amp;gt;&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt;...&lt;/span&gt;
&lt;span class='go'&gt;Rake aborted!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The short story: The spec task definition needed the RSpec gem to be loaded but it wasn&amp;#8217;t until &lt;em&gt;after&lt;/em&gt; all tasks had been defined.&lt;/p&gt;

&lt;p&gt;Now, I could single out the spec task definition and ensure it was loaded last but that would mean adding a bunch of code to my otherwise trivial &lt;code&gt;Rakefile&lt;/code&gt;. The other option was to somehow defer the creation of the spec task until actually needed. After a bit of searching I couldn&amp;#8217;t find anything particularly useful so I rolled my own:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;namespace&lt;/span&gt; &lt;span class='ss'&gt;:spec&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;

  &lt;span class='n'&gt;desc&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Run specifications&amp;quot;&lt;/span&gt;
  &lt;span class='n'&gt;task&lt;/span&gt; &lt;span class='ss'&gt;:run&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='ss'&gt;:define&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
    &lt;span class='no'&gt;Rake&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Task&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:_run&lt;/span&gt;&lt;span class='o'&gt;].&lt;/span&gt;&lt;span class='n'&gt;invoke&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='n'&gt;task&lt;/span&gt; &lt;span class='ss'&gt;:define&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;

    &lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;spec/rake/spectask&amp;#39;&lt;/span&gt;

    &lt;span class='no'&gt;Spec&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Rake&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;SpecTask&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:_run&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;t&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
      &lt;span class='n'&gt;t&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;spec_opts&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;--options&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;spec/spec.opts&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;exists?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;spec/spec.opts&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;spec:run&lt;/code&gt; task depends on the &lt;code&gt;spec:define&lt;/code&gt; task to create a &amp;#8220;hidden&amp;#8221; &lt;code&gt;spec:_run&lt;/code&gt; task to actually do the work.&lt;/p&gt;

&lt;p&gt;The nice thing about this all the ickiness is hidden &amp;#8211; remove the &lt;code&gt;tasks/spec.rb&lt;/code&gt; file and nothing else really cares &amp;#8211; and means I can treat the spec task like any other when creating my task dependencies.&lt;/p&gt;

&lt;p&gt;As always, YMMV.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/LjMZ6-UMA-I" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2010/01/lazy-spec-task-creation.html</feedburner:origLink></entry>
    
    <entry>
        <title>Why Object-Oriented Languages Need Tail Calls</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/OAISsk5Yi-w/why-object-oriented-language-need-tail-calls.html" />
        <updated>2009-12-24T00:00:00-08:00</updated>
        <id>http://www.harukizaemon.com/2009/12/why-object-oriented-languages-need-tail-calls</id>
        <content type="html">&lt;p&gt;&lt;strong&gt;Update 2009/12/30&lt;/strong&gt;: MacRuby supports a limited form of TCO as well. I received similar results as for YARV (see below) the differences being you&amp;#8217;re not limited to the call being the &lt;em&gt;last&lt;/em&gt; statement in the method and there&amp;#8217;s a bug where you receive a segmentation fault rather than a stack overflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update 2009/12/27&lt;/strong&gt;: According to &lt;a href='http://redmine.ruby-lang.org/issues/show/1256'&gt;this redmine ticket&lt;/a&gt;, YARV has some limited TCO support which is disabled by default. I performed the necessary incantations to enable it, only to discover the true meaning of &amp;#8220;limited&amp;#8221;: optimise calls to the same method in the same instance &lt;em&gt;iff&lt;/em&gt; the call is the last statement in the method.&lt;/p&gt;

&lt;p&gt;Disclaimer: I unashamedly stole the title after reading &lt;a href='http://projectfortress.sun.com/Projects/Community/blog/ObjectOrientedTailRecursion'&gt;another article&lt;/a&gt; on the same topic.&lt;/p&gt;

&lt;p&gt;Some of you may know of a &lt;a href='http://github.com/harukizaemon/hamster'&gt;little project&lt;/a&gt; I&amp;#8217;ve been working on in my, albeit very limited, spare time. Hamster started out as an implementation of &lt;a href='http://lamp.epfl.ch/papers/idealhashtrees.pdf'&gt;Hash Array Mapped Trees&lt;/a&gt; (HAMT) for Ruby and has since expanded to include implementations of other &lt;a href='http://en.wikipedia.org/wiki/Persistent_data_structure'&gt;Persistent Data Structures&lt;/a&gt; such as Sets, Lists, Stacks, etc.&lt;/p&gt;

&lt;p&gt;For those that aren&amp;#8217;t up with HAMTs or persistent data structures in general, they have a really neat property: very efficient copy-on-write operations. This allows us to create immutable data-structures that only need copying when something changes, making them a very effective when writing multi-threaded code.&lt;/p&gt;

&lt;p&gt;Hamster also contains an implementation of &lt;a href='http://en.wikipedia.org/wiki/Cons'&gt;Cons Lists&lt;/a&gt; with all the usual methods you&amp;#8217;d expect from a Ruby collection such as &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;select&lt;/code&gt;, &lt;code&gt;reject&lt;/code&gt;, etc. thrown in for good measure.&lt;/p&gt;

&lt;p&gt;One of the things I really wanted to investigate was laziness. So, for example, when evaluating:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;Hamster&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;interval&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;1000000&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;filter&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='ss'&gt;:odd?&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;take&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;10&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Rather than generate a list with a million values, evaluate them all against the filter, and then select the first ten, Hamster lazily generates the list, the evaluation of &lt;code&gt;filter&lt;/code&gt;, and even &lt;code&gt;take&lt;/code&gt;. In fact, as it stands, the example code wont&amp;#8217; &lt;em&gt;actually&lt;/em&gt; do anything; you would need to call &lt;code&gt;head&lt;/code&gt; to kick-start anything happening at all. This behaviour extends, to the extent possible, to all other collection methods.&lt;/p&gt;

&lt;p&gt;Hamster also supports infinite lists. For example, the following code produces an infinite list of integers:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;integers&lt;/span&gt;
  &lt;span class='n'&gt;value&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;
  &lt;span class='no'&gt;Hamster&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;stream&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;value&lt;/span&gt; &lt;span class='o'&gt;+=&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now we can easily generate a list of odd numbers:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;integers&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;filter&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='ss'&gt;:odd?&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Again, rather than generate every possible integer and filter those into odd numbers, the list is generated as necessary.&lt;/p&gt;

&lt;p&gt;OK, so enough with the apparent shameless self-promotion. Let&amp;#8217;s get to the point.&lt;/p&gt;

&lt;p&gt;My first implementation of lists used recursion for collection methods. The code was succinct, and, IMHO elegant. It conveyed the essence of what I was trying to achieve. It was easier to understand and thus, I would surmise, easier to maintain. The problem was that for any reasonably large list, stack overflows were common place. The lack of Tail-Call-Optimisation (TCO) meant that the recursive code would eventually blow whatever arbitrary stack limits were in place. The solution: convert the recursive code to an equivalent iterative form.&lt;/p&gt;

&lt;p&gt;Once all methods had been re-implemented using iteration, the code ran just fine on large lists; no more stack overflow errors. The downside was, the code had almost doubled in size&amp;#8211;1~2 lines of code became 2~4 or in some cases even more. The code was now harder to read and far less intention revealing. In short, the lack of Tail-Call-Optimisation lead to less maintainable and I&amp;#8217;d hazard a guess, more error prone code.&lt;/p&gt;

&lt;p&gt;The story however, doesn&amp;#8217;t end there. Take another (albeit contrived) example that partitions integers into odds and evens:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;partitions&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;integers&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;partition&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='ss'&gt;:odd?&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='n'&gt;odds&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;partitions&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;car&lt;/span&gt;
&lt;span class='n'&gt;evens&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;partitions&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;cadr&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You would expect &lt;code&gt;odds&lt;/code&gt; to contain &lt;code&gt;[1, 3, 5, 7, 9, ...]&lt;/code&gt;, and &lt;code&gt;evens&lt;/code&gt; to contain &lt;code&gt;[2, 4, 6, 8, 10, ...]&lt;/code&gt;. But the way I initially implemented the code it didn&amp;#8217;t. Here&amp;#8217;s an example to show what happened:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;odds&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;take&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;5&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;    &lt;span class='c1'&gt;# =&amp;gt; [1, 3, 5, 7, 9]&lt;/span&gt;
&lt;span class='n'&gt;evens&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;take&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;5&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;   &lt;span class='c1'&gt;# =&amp;gt; [2, 12, 14, 16, 18]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Confused? So was I until it dawned on me that I had broken a fundamental principle: immutability. The underlying block that generates the list of integers has state! Enumerating the odd values first produces the expected results but once we get around to enumerating the even values, the state of the block is such that it no longer starts at 1&amp;#8211;reversing the order of enumeration produces a corresponding reversal of the error. Pure functional Languages such as Haskell have mechanisms for dealing with this but in Ruby, the only construct I really have available to me is explicit caching of generated values.&lt;/p&gt;

&lt;p&gt;Once I had cached the values all was well, or so I thought. I started to write some examples that used files as lists:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;open&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;my_100_mb_file.txt&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;io&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
  &lt;span class='n'&gt;io&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to_list&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;map&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='ss'&gt;:chomp&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;map&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='ss'&gt;:downcase&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;each&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;line&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
    &lt;span class='nb'&gt;puts&lt;/span&gt; &lt;span class='n'&gt;line&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Running the code above took forever to run, much slower than the non-list equivalent. I expected a little slow down sure, but nothing like that which I was seeing.&lt;/p&gt;

&lt;p&gt;At first I suspected garbage collection&amp;#8211;perhaps the virtual machine was being crushed by the sheer number of discarded objects; I could find no evidence for this. Next, I suspected synchronisation&amp;#8211;anything with state needs synchronisation. Again, I found no evidence for this either. A bit more fiddling and a few dozen print statements later&amp;#8211;Ruby has no real profiling tools that I&amp;#8217;m aware of, something that frustrates me no end at times&amp;#8211;I realised what the problem was.&lt;/p&gt;

&lt;p&gt;When I failed to find any evidence of garbage collection as the culprit, it had seemed a bit odd but I wasn&amp;#8217;t sure why I felt that way and thus moved on. Had I stopped and thought about it for a while I may have realised that in fact that was &lt;em&gt;exactly&lt;/em&gt; the problem: there was NO evidence of garbage collection at all. How could that be? Processing hundreds of thousands of lines in a 100MB text file using a linked list was sure to generate lots of garbage. Once a line had been processed, the corresponding list element should no longer have been referenced and thus made available for garbage collection, unless&amp;#8230; unless for some mysterious reason each element was still being referenced.&lt;/p&gt;

&lt;p&gt;My caching implementation worked like this: As each value is generated, it&amp;#8217;s stored in an element and linked to from the previous element: &lt;code&gt;[A] -&amp;gt; [B] -&amp;gt; [C]&lt;/code&gt;. At face value this works well&amp;#8211;if you never hold a references to &amp;#8220;A&amp;#8221; or &amp;#8220;B&amp;#8221;, they will become available for garbage collection. So what could possibly have been going wrong? Each line was being processed and then discarded. Surely, that meant each corresponding element should have become available for garbage collection?&lt;/p&gt;

&lt;p&gt;Now recall that I had converted the recursive code to an iterative equivalent. This had now come back to bite me, hard!&amp;#8211;though to be fair the recursive code would have suffered in a similar and perhaps more obvious way. The call to &lt;code&gt;map&lt;/code&gt; runs in the context of the very first line which, because of the caching, directly and indirectly references every other line that is processed! The lack of Tail-Call-Optimisation in Ruby means that whether I use recursion or iteration, if I process all elements from the head of a stream, the garbage collector can never reclaim anything because the head element is always referenced until the end of the process!&lt;/p&gt;

&lt;p&gt;Some of my colleagues have suggested that I just get over it and use a &amp;#8220;real&amp;#8221; language like &lt;a href='http://clojure.org/'&gt;Clojure&lt;/a&gt;. Whilst I understand the sentiment, the point of Hamster is not necessarily to implement a functional language in Ruby. Rather, it is to see what can be done in object-oriented languages and, in this case, Ruby.&lt;/p&gt;

&lt;p&gt;Hamster has allowed me to demonstrate that functional language idioms can, for the most part, translate quite well into object-oriented equivalents. However, the lack of Tail-Call-Optimisation severely limits what is possible.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/OAISsk5Yi-w" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2009/12/why-object-oriented-language-need-tail-calls.html</feedburner:origLink></entry>
    
    <entry>
        <title>Responsibility Traps</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/Of0eHWZX4pA/responsibility-traps.html" />
        <updated>2009-10-31T00:00:00-07:00</updated>
        <id>http://www.harukizaemon.com/2009/10/responsibility-traps</id>
        <content type="html">&lt;p&gt;I recently watched a wonderfully insightful presentation by Eric Evans on &lt;a href='http://www.infoq.com/presentations/design-strategic-eric-evans'&gt;Responsibility Traps&lt;/a&gt;. In it he describes a number of traps into which otherwise well meaning and capable developers fall. Among others these include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building a platform to make other (lesser) programmers more productive&lt;/li&gt;

&lt;li&gt;Cleaning up other people&amp;#8217;s mess; being a janitor&lt;/li&gt;

&lt;li&gt;Making hackers (pejorative) look even better&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;He concludes that the responsible developer often focuses her energy on solving the wrong problem, incorrectly believing it to be in the best interests of the project/company/etc. as a whole.&lt;/p&gt;

&lt;p&gt;Since then I have subconsciously been on the lookout for other areas of life where this anti-pattern arises and to my delight (or perhaps horror), I see it everywhere.&lt;/p&gt;

&lt;p&gt;I thought about the number of relationships that I had dragged on for much longer than was probably good for either party, where, as a result of my desire to avoid hurting anyone, both parties suffered needlessly.&lt;/p&gt;

&lt;p&gt;I spoke with a colleague who told me how, in a recent attempt to both fulfil his unspoken promise to help a project out of trouble and preserve the reputation of his sponsors, he continued to engage with the client well beyond his own reasonable belief of success. A near stress-related break down followed shortly thereafter leading to strained relationships all &amp;#8216;round.&lt;/p&gt;

&lt;p&gt;I can&amp;#8217;t count the number of times I&amp;#8217;ve tried to &amp;#8220;rescue&amp;#8221; a project I thought was doomed to failure, implement an unnecessarily complex story, &amp;#8220;protect&amp;#8221; a friend/relative/colleague/client from some bad news. In almost all cases the outcome was as bad as, if not worse than, it might have been had I confronted the reality at the outset.&lt;/p&gt;

&lt;p&gt;Why do I fall into these traps when I believe I&amp;#8217;m primarily motivated by of a sense of duty and responsibility? Partly I think it&amp;#8217;s about taking a path of least resistance &amp;#8211; It seems easier to try and press ahead to &amp;#8220;fix&amp;#8221; the symptom than to address the underlying cause; partly it&amp;#8217;s about self-esteem &amp;#8211; I&amp;#8217;m a failure unless I can solve the problem; there&amp;#8217;s an aspect of self-importance &amp;#8211; few others can see what I see, so it&amp;#8217;s up to me to do something about it. And then there&amp;#8217;s just a plain old misguided sense of responsibility.&lt;/p&gt;

&lt;p&gt;Many years ago, someone handed me a copy of a Garfield cartoon &amp;#8211; ironically it was probably a technically illegal copy. The cartoon showed Garfield in bed on a Monday morning with Jon telling him to &amp;#8220;Get out of bed Garfield!&amp;#8221; Garfield wonders to himself &amp;#8220;What&amp;#8217;s my motivation?&amp;#8221; Taken literally that doesn&amp;#8217;t really say much but the message it has left with me is to be honest with myself about what really drives my decisions.&lt;/p&gt;

&lt;p&gt;As a professional software developer, I believe I have a duty of care to act in the best interests of my employer even if that means delivering the news nobody wants to hear&amp;#8211; &amp;#8220;I think we&amp;#8217;d be better off killing this project than sinking anymore money into it.&amp;#8221; &amp;#8211; and that doing so is ultimately better for me as well.&lt;/p&gt;

&lt;p&gt;More often than not, the way a message is received has far more to do with the way in which it is delivered than with the substance of the message itself. Telling someone they&amp;#8217;re screwed unless they do what you say probably won&amp;#8217;t get you anywhere; suggesting humbly that they are spending double what they could be paying if all they are after is someone who&amp;#8217;ll unquestioningly implement whatever whacky ideas they present, has historically worked out much better for me.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve come to the conclusion that, more important than acting responsibly is to act ethically &amp;#8211; fairly and honestly. If you act ethically you will necessarily end up acting responsibly but not necessarily the other way &amp;#8216;round. Acting ethically takes courage, determination and optimism and that&amp;#8217;s bloody hard work&amp;#8482;. I encourage you to be brave, take a risk, act ethically and do the right thing.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/Of0eHWZX4pA" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2009/10/responsibility-traps.html</feedburner:origLink></entry>
    
    <entry>
        <title>Plugins: Grab 'em while they're stale</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/zhdyLl88okE/plugins-grab-em-while-theyre-stale.html" />
        <updated>2009-09-19T00:00:00-07:00</updated>
        <id>http://www.harukizaemon.com/2009/09/plugins-grab-em-while-theyre-stale</id>
        <content type="html">&lt;p&gt;I don&amp;#8217;t like leaving unused code lying around, unused applications installed, unused clothes in the wardrobe, etc. As a consequence I&amp;#8217;m often referred to as &amp;#8216;Mr. Detritus&amp;#8217;.&lt;/p&gt;

&lt;p&gt;As you probably know, I&amp;#8217;ve created a number of Ruby on Rails plugins over the years. Most of them when I first started out with Rails and for that matter, Ruby. Most of them had poor (if any) test coverage and the code looked generally like a dog&amp;#8217;s breakfast but they satisfied a need &amp;#8211; scratched an itch if you like &amp;#8211; I had at the time.&lt;/p&gt;

&lt;p&gt;Time marches on and although I will continue to use Ruby as a language, I no longer have any desire to use Rails. Some of my plugins ended up in Rails core, I&amp;#8217;ve continued to use others on recent projects but most of them have been left to rot &amp;#8211; some of them I wouldn&amp;#8217;t use even if I did another Rails project having long since considered them failed experiments.&lt;/p&gt;

&lt;p&gt;And so it is that I will very shortly (within the next month) delete most of the plugins from my &lt;a href='http://github.com/harukizaemon'&gt;GitHub account&lt;/a&gt;. If you wish to continue using them, feel free to fork and keep a copy for yourself. Republish them under your own name if you wish for they will no doubt be better cared for by you than me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; By popular demand, a &lt;a href='http://github.com/harukizaemon/redhillonrails'&gt;Once off never to be repeated copy of the UNSUPPORTED Rails plugins&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/zhdyLl88okE" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2009/09/plugins-grab-em-while-theyre-stale.html</feedburner:origLink></entry>
    
    <entry>
        <title>Random thoughts on our current Agile process</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/a5PqwUV-yY0/random-thoughts-on-our-current-agile.html" />
        <updated>2009-08-31T00:00:00-07:00</updated>
        <id>http://www.harukizaemon.com/2009/08/random-thoughts-on-our-current-agile</id>
        <content type="html">&lt;p&gt;It&amp;#8217;s late and I don&amp;#8217;t seem to be able to sleep so for something to do I thought I&amp;#8217;d jot down (ok copy from an email I sent out earlier in the week) some thoughts about our development process as it has evolved over the last few months or so.&lt;/p&gt;

&lt;p&gt;As always, I can only speak from personal experience (one data point doesn&amp;#8217;t really count for much) so here&amp;#8217;s my totally subjective perspective, YMMV:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Full-time pairing when possible/practical&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;No more than one card per pair in dev at any time&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;No more than one card per pair in ready-for-dev at any time&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Nothing to be blocked; If it&amp;#8217;s blocked we work to remove the blockage&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;New cards are demand pulled into read-for-dev as cards are moved from dev to ready-for-test&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We have big picture story card sessions as required&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We have planning meetings at the start of each iteration where we discuss what&amp;#8217;s &amp;#8220;planned&amp;#8221;&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We give everything t-shirt sizes (S, M, L)&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;No technical stories; everything must be done because it delivers business value&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We don&amp;#8217;t measure velocity&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We aggressively split cards&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;I repeat, we aggressively split cards&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Daily stand-ups&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Parking lot for post-stand up discussions&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;2-week iterations with retro followed by a kick-off to discuss the stories&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We try to focus on doing things as simply as possible&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We try to focus on building things correctly rather than as fast as possible&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We fight to have a REAL user available to better understand their needs&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We rally against the usual cries of &amp;#8220;but I know we&amp;#8217;ll need it&amp;#8221;; We trust that by building things simply we can always add on the extra functionality later&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Trying to deliver everything to everyone leads to delivering nothing at all to anyone&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;T-shirt sizes help the business prioritise not estimate delivery dates; Business value is a function of, among other things, time/cost to build&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Rolling technical stories into stories that deliver business value force us to change course slowly and justify changes&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We usually have parallel implementations of some things as a consequence&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Minimal changes are allowed on &amp;#8220;old&amp;#8221; implementations; anything substantial requires a migration to the &amp;#8220;new&amp;#8221; implementation&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;If we can deliver &lt;em&gt;some&lt;/em&gt; business value early by splitting the cards we do so as soon as possible; Eg. business can view existing data in the new form but editing is a new card because they can still use the old mechanism for that.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;It&amp;#8217;s critical that the whole team is taken on the &amp;#8220;journey&amp;#8221; so they understand &lt;em&gt;why&lt;/em&gt; things are being built. Doing so brings the team into alignment and also allows the team to make informed decisions such as re-structuring work to enable splitting cards for aggressively.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Bringing the team along for the journey can be painful, never gets easier, and is always worth the effort.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Just-in-time stories really does enable the business to leave the decision as to what&amp;#8217;s important to the last possible moment&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Implementing the smallest amount of code possible for each story is critical to enabling just-in-time development; less code == greater flexibility; E.g. don&amp;#8217;t use a database when the data comes from a spreadsheet and presently only ever changes in a spreadsheet.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We do as much forward thinking as possible/practical; We think of as many likely scenarios as we can and keep reducing the scope of the implementation so as not to preclude implementing them later on&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Almost nothing ends up looking as we thought it would when first envisaged.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;More important than writing the code is working out how to structure the implementation so that we get the job done without precluding possible future work; sometimes this means at least thinking through a strategy for migrating from one implementation to another later on if necessary.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;It&amp;#8217;s amazing how splitting stories reveals just how little the business value certain aspects of stories&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We almost never get through everything that was &amp;#8220;planned&amp;#8221;&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We almost always end up doing stories that weren&amp;#8217;t &amp;#8220;planned&amp;#8221;&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We are as close as we can get (due to the bureaucratic nature of the client&amp;#8217;s operations group) to on-demand deployment into production. Ideally we&amp;#8217;d like it to be automated but that&amp;#8217;s just not going to happen anytime soon&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We&amp;#8217;re motivated by getting things done; call that velocity if you will but we really haven&amp;#8217;t found a need to measure velocity. Delivering a constant stream of small but valuable stuff into production every week is VERY motivating.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We value delivering something over delivering nothing&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We actively plan for change Caveats:&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We have a highly competent team&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We have a fixed budget&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We have internal and external users&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We have UI and data-only users&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;We have an existing implementation we are evolving away from It&amp;#8217;s far from perfect and is constantly evolving but as &lt;a href='http://www.prozacblues.com/'&gt;Travis&lt;/a&gt; observed, it kinda represents a snapshot of what being Agile means to me right now.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/a5PqwUV-yY0" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2009/08/random-thoughts-on-our-current-agile.html</feedburner:origLink></entry>
    
    <entry>
        <title>We're Recruiting</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/rNPiF3dX7Zg/were-recruiting.html" />
        <updated>2009-08-01T00:00:00-07:00</updated>
        <id>http://www.harukizaemon.com/2009/08/were-recruiting</id>
        <content type="html">&lt;p&gt;If you haven&amp;#8217;t heard already, &lt;a href='http://www.cogentconsulting.com.au/'&gt;Cogent Consulting&lt;/a&gt; are recruiting.&lt;/p&gt;

&lt;p&gt;Cogent Consulting prides itself on the depth of its experience with agile software development, and its ability to leverage this experience to benefit Cogent clients. We are an open-book company, with comprehensive employee participation in decision-making.&lt;/p&gt;

&lt;p&gt;What do we do? We’re a three part story. We go out on site as consultants to help our clients get better at producing good software, by both coaching them in agile techniques and working as integral part of their development teams. We produce high-quality websites for clients from our own premises. Finally, we build our own (mostly web-based) products, using the range of great talents that make up our team.&lt;/p&gt;

&lt;p&gt;Right now we’re looking for people who can perform hands-on web application development both in our offices and on client sites, predominantly in Ruby on Rails.&lt;/p&gt;

&lt;p&gt;We&amp;#8217;re also looking for people who can provide hands-on support to clients undertaking agile transformations at both the small and large scale, as well as help out with internal product development&lt;/p&gt;

&lt;p&gt;In either case You’ll need to show us that you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;understand the principles of agile software development and have experience working on agile projects&lt;/li&gt;

&lt;li&gt;are collaborative, but willing to be a benevolent dictator when required&lt;/li&gt;

&lt;li&gt;can represent us on a client site in a way that makes us proud&lt;/li&gt;

&lt;li&gt;have a passion for software development and you continue your professional development outside of work hours&lt;/li&gt;

&lt;li&gt;have experience with Ruby on Rails, or you are able to learn it very quickly. Extra points if you’re a Smalltalk or Haskell expert&lt;/li&gt;

&lt;li&gt;understand that “done” means the software is in production&lt;/li&gt;

&lt;li&gt;have a demonstrated track record of successful delivery&lt;/li&gt;

&lt;li&gt;have a passion for software development and you continue your professional development outside of work hours&lt;/li&gt;

&lt;li&gt;think that the time after five o’clock belongs to your family (yes, that’s contradictory - we want to know how you deal with that contradiction)&lt;/li&gt;

&lt;li&gt;understand that software and process should be opinionated, but not bigoted&lt;/li&gt;

&lt;li&gt;can read, you choose to read, and you understand what you read&lt;/li&gt;

&lt;li&gt;are intellectually omnivorous&lt;/li&gt;

&lt;li&gt;consider communication (written and verbal) to be amongst your strongest skills&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In return, we’ll provide you with a collegial environment that rewards inquisitiveness rather than being an ongoing inquisition. We’ll treat you as part of the Cogent family, and give you a share of the profit and/or the products that we develop. We’ll provide an environment where you can work with your peers, be challenged, and be the best that you can be.&lt;/p&gt;

&lt;p&gt;If this sounds like your thing, you can visit our &lt;a href='http://www.cogentconsulting.com.au/'&gt;website&lt;/a&gt; for more information about Cogent, or email us directly: &lt;a href='mailto:info@cogentconsulting.com.au'&gt;info@cogentconsulting.com.au&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/rNPiF3dX7Zg" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2009/08/were-recruiting.html</feedburner:origLink></entry>
    
    <entry>
        <title>Less delicious, yet more satisfying</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/Mm3XXLQCiKY/less-delicious-yet-more-satisfying.html" />
        <updated>2009-07-14T00:00:00-07:00</updated>
        <id>http://www.harukizaemon.com/2009/07/less-delicious-yet-more-satisfying</id>
        <content type="html">&lt;p&gt;These days, I spread my research and reading between &lt;a href='http://www.instapaper.com/'&gt;Instapaper&lt;/a&gt; and &lt;a href='http://evernote.com/'&gt;Evernote&lt;/a&gt;. IMHO, &lt;a href='http://delicious.com/'&gt;delicious&lt;/a&gt; is essentially a big old shed with crap in it and no way to actually use any of it other than marvel at how much stuff I&amp;#8217;ve collected.&lt;/p&gt;

&lt;p&gt;On the other hand, both Instapaper and Evernote add value to the stuff I&amp;#8217;ve collected: Instapaper allows me to read blogs and websites on my phone, and Evernote allows me to collect and organise information according to project, tags, etc.&lt;/p&gt;

&lt;p&gt;As &lt;a href='http://www.prozacblues.com/'&gt;Travis&lt;/a&gt; pointed out, this makes it difficult (nay impossible) for others to see what I&amp;#8217;m reading and is largely the reason I send out almost daily emails to colleagues on stuff I think is more generally interesting.&lt;/p&gt;

&lt;p&gt;This morning I noticed that Instapaper helpfully provide a read-only feed of my list. So, for anyone interested, here is the link: &lt;a href='http://www.instapaper.com/rss/175381/1rDOvQp1xwBTeMIoml2TuzPjlmM'&gt;http://www.instapaper.com/rss/175381/1rDOvQp1xwBTeMIoml2TuzPjlmM&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt;If you&amp;#8217;d rather not wade through everything I read to find the good bits, I&amp;#8217;ve started &amp;#8220;starring&amp;#8221; items I found insightful and/or think are of more general interest. Here&amp;#8217;s the feed: &lt;a href='http://www.instapaper.com/starred/rss/175381/ttNEuQvOmmM5sX94f0HCO7ns'&gt;http://www.instapaper.com/starred/rss/175381/ttNEuQvOmmM5sX94f0HCO7ns&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/Mm3XXLQCiKY" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2009/07/less-delicious-yet-more-satisfying.html</feedburner:origLink></entry>
    
    <entry>
        <title>Problem Solving</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/v_eBQX-Zcjw/problem-solving.html" />
        <updated>2009-06-25T00:00:00-07:00</updated>
        <id>http://www.harukizaemon.com/2009/06/problem-solving</id>
        <content type="html">&lt;p&gt;It occurred to me recently that I have this notion of programming as a process that involves breaking a problem down into a sets of smaller and smaller problems until I have something I know how to solve. (I mentioned this to &lt;a href='http://iridescenturchin.blogspot.com/'&gt;Steve&lt;/a&gt; yesterday which reminded him of a &lt;a href='http://www.electronics.dit.ie/staff/sofearghail/mathematicians.htm'&gt;joke about an engineer and a mathematician&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;I have previously just assumed that I therefore follow this process when I&amp;#8217;m actually problem solving however, on reflection, I&amp;#8217;n not so sure. More specifically, I&amp;#8217;m either not doing it at all or, at the very least, I&amp;#8217;m doing it intuitively.&lt;/p&gt;

&lt;p&gt;I wonder how many people do (or have done) this as an explicit part of their own problem solving and if so, what effects they&amp;#8217;ve noticed as a consequence.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/v_eBQX-Zcjw" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2009/06/problem-solving.html</feedburner:origLink></entry>
    
    <entry>
        <title>Collaboration</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/vP7xRbgaSQA/collaboration.html" />
        <updated>2009-06-25T00:00:00-07:00</updated>
        <id>http://www.harukizaemon.com/2009/06/collaboration</id>
        <content type="html">&lt;p&gt;I&amp;#8217;ve heard the phrase &amp;#8220;Excuse my poor code&amp;#8221; (or words to that effect) a number of times recently. I&amp;#8217;ve said it to myself about my own code (if not to others), I&amp;#8217;ve heard two of my work colleagues say it, and I&amp;#8217;ve had a customer&amp;#8217;s sole developer apologise ad nauseum about his code.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;m not here to make any determination as to the veracity of these claims but the thing that I find interesting is that in all cases, the majority of the code in question was written by a single person.&lt;/p&gt;

&lt;p&gt;Now this may be difficult for some to believe but I inherently don&amp;#8217;t trust my own opinion. I do tend to confidently put ideas forward as bold assertions to be shot down when I&amp;#8217;m wrong. The confidence comes from knowing I&amp;#8217;ll defend my ideas to the hilt and not take it personally when my argument is proven flawed.&lt;/p&gt;

&lt;p&gt;I hate working alone because I don&amp;#8217;t trust decisions that weren&amp;#8217;t arrived at through furious debate. I don&amp;#8217;t like developers working alone no matter how good they are (or think they are) because I don&amp;#8217;t trust they can be objective enough by themselves. In fact I don&amp;#8217;t care if they&amp;#8217;re working in pairs of developers or not, I just want their decisions to be scrutinised by smart people as early as possible.&lt;/p&gt;

&lt;p&gt;The idea that the majority of a developer&amp;#8217;s work involves going off to think on their own is totally nonsensical to me. It feels like a lot of hocus pocus going on behind closed doors and then &lt;em&gt;poof&lt;/em&gt; a few days later something is divined. Not only is the process opaque, but the possibility for smart people to scrutinise is left as late as possible. Sure, I like to sit and ponder without the hullabaloo of every man and his dog trying to give me their 2c worth as much as the next but the lack of transparency and scrutiny in this as a process is something I find very difficult to accept.&lt;/p&gt;

&lt;p&gt;Smart people working alone is even worse. They&amp;#8217;re often implicitly promoted to the position of grand wizard, the seer and knower of all things. They go away, think about a problem and come back with much fanfare (trumpets playing, drums beating) to bestow upon the people their creation who will wonder in amazement at the design, so simple and yet, so complex, that only they can truly grasp the significance of what they have achieved.&lt;/p&gt;

&lt;p&gt;When I write code on my own, I feel personally, individually, responsible. When I write code on my own, the pressures I feel are, mostly, self imposed. They make me lie awake at night. They make me code into the wee hours of the morning, on the train home. I worry that I&amp;#8217;ve missed something. I start to believe my own hype. That I&amp;#8217;m good enough to do this on my own. That because I did most of it on my own, I should fix it on my own lest someone else realise how crap my code is. In a vacuum my ideas have no predators. As importantly (if not more so), my priorities have no predators. I race towards a goal without stopping to re-think that goal because I&amp;#8217;m stuck in a vicious cycle of self doubt and self confidence. Moreover, when I work alone, the production of code becomes the focus and not, as I believe it should be, thinking!&lt;/p&gt;

&lt;p&gt;When we write code together we have shared code ownership. I feel like someone has my back. That even if we got the decision &amp;#8220;wrong&amp;#8221;, we decided together and if two smart people can&amp;#8217;t get it right then maybe it&amp;#8217;s good enough, maybe there are diminishing returns for adding more people to solving the problem. When I go home at night, sure I might tinker but I know there&amp;#8217;s not much point to spending a lot of effort because I&amp;#8217;ll just be re-doing it tomorrow anyway. And if there is something we missed, someone has my back if need be.&lt;/p&gt;

&lt;p&gt;I speak as one who has been, done and experienced others doing all of the above and decided once and for all that it&amp;#8217;s just not worth working alone. Having more than one person working on something WILL cost more but I assert that the result will be better and will be achieved more quickly and with less churn. Anyone who thinks they&amp;#8217;re smart enough to do otherwise needs to learn to &lt;a href='http://teddziuba.com/2009/06/startups-keep-it-in-your-pants.html'&gt;keep it in their pants&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have this mental model of software development that is akin to the way I&amp;#8217;ve heard Abraham Lincoln&amp;#8217;s cabinet described. Apparently he got a whole bunch of really smart people together, even those from opposite sides of politics, and let them argue it out. His (Lincoln&amp;#8217;s) role was to decide when to stop the debate and which idea(s) to act on.&lt;/p&gt;

&lt;p&gt;I think that&amp;#8217;s what collaborative development should be.&lt;/p&gt;

&lt;p&gt;YMMV :)&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/vP7xRbgaSQA" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2009/06/collaboration.html</feedburner:origLink></entry>
    
    <entry>
        <title>No, Sleep, Till Bedtime</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/w2uNu3ZwOSg/no-sleep-till-bedtime.html" />
        <updated>2009-06-14T00:00:00-07:00</updated>
        <id>http://www.harukizaemon.com/2009/06/no-sleep-till-bedtime</id>
        <content type="html">&lt;p&gt;Or at least until all those &lt;a href='http://twitter.com/'&gt;Twitter&lt;/a&gt; client developers have fixed their &lt;a href='http://www.twitpocalypse.com/'&gt;Twitpocalypse&lt;/a&gt; bugs. In case you didn&amp;#8217;t know, a few days ago the ID range used for Twitter&amp;#8217;s messages exceeded 2^31 (approximately 2 billion) causing any apps that stored them as 32-bit integers to think they were really small negative numbers.&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s usual &amp;#8211; and I say usual because I don&amp;#8217;t always adhere to it &amp;#8211; policy for storing external identifiers is to treat them as text, even when I know they are numbers. Why? Essentially because I consider it a coincidence that they&amp;#8217;re numbers. That identifier, number though it may be, has no special significance to me over and above being an opaque handle to some entity in another system. As such, I like to treat them as text.&lt;/p&gt;

&lt;p&gt;Discussing this with a good friend and colleague of mine, the question of column width came up. Ie. if you&amp;#8217;re going to make it text, how long should the column be? If you&amp;#8217;re lucky enough to be using a database such as PostgreSQL, then the answer is: it doesn&amp;#8217;t matter &amp;#8211; there&amp;#8217;s no performance benefit to artificially limiting the size of the column. For other databases, the common practice is to use something like &lt;code&gt;VARCHAR(255)&lt;/code&gt;. Think about it, even if it is a number that&amp;#8217;s 10^255!&lt;/p&gt;

&lt;p&gt;Twitter claims that its &lt;a href='http://apiwiki.twitter.com/Twitter-API-Documentation'&gt;API&lt;/a&gt; is RESTful. And if to you, REST means nice, predictable URLs with some semantic path possibly followed by a numeric id and returning numeric ids in search results, then yes, it&amp;#8217;s RESTful. Want to see the most recent messages for a user? There&amp;#8217;s a simple HTTP request you can make to a nice, semantic (if you speak English) URL that returns a list of them and their identifiers. And, as expected, our Twitter clients have been dutifully squirrelling these ids away in integer fields (probably because that&amp;#8217;s the default) and all has well until 2 days ago.&lt;/p&gt;

&lt;p&gt;Now, without going into too much of an ideological rant, I happen subscribe to the principle that RESTful URLs should be opaque. That is, a URL is a URL is a URL. No slicing, no dicing, no assembling, no joining. If I have a URL to a resource then that&amp;#8217;s what I use. Period. End of story. (You can find plenty of discussion on this by Roy Fielding using Google.)&lt;/p&gt;

&lt;p&gt;So, back to our column widths. Assuming we have a text field in our database large enough to accommodate a URL, we could go one step further. Rather than treat the identifier as text and storing that, why not go the whole hog and store the URL instead?&lt;/p&gt;

&lt;p&gt;As far as I can tell, the only reason is that Twitter&amp;#8217;s API, RESTful though they may claim, sends back numeric identifiers rather than URLs which in turn leads developers to incorrectly assume that they should be storing them as numbers.&lt;/p&gt;

&lt;p&gt;On their own, identifiers are meaningless and in fact, useless. To utilise an identifier requires us to know the system in which it is stored and the collection in which it belongs. If instead each piece of information was identified by a URL we get all that context for free and the power to share information grows phenomenally.&lt;/p&gt;

&lt;p&gt;To me, the beauty and power of the internet is the ability to link together disparate systems in ways no one had previously imagined. More specifically, in ways the publishers of the information never considered.&lt;/p&gt;

&lt;p&gt;Opaque URLs combined with idiomatic use of HTTP verbs can help reduce the coupling between producers and consumers by giving back control to producers in how and where they store information and at the same time increasing the freedom for others to share and use that information.&lt;/p&gt;

&lt;p&gt;(That last paragraph reads like an &lt;a href='http://www.amnesty.org/'&gt;Amnesty International&lt;/a&gt; commercial!)&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/w2uNu3ZwOSg" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2009/06/no-sleep-till-bedtime.html</feedburner:origLink></entry>
    
    <entry>
        <title>Shameless Self Promotion</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/QxvJs_Tr3mA/shameless-self-promotion.html" />
        <updated>2009-05-15T00:00:00-07:00</updated>
        <id>http://www.harukizaemon.com/2009/05/shameless-self-promotion</id>
        <content type="html">&lt;p&gt;So the past couple of months, I&amp;#8217;ve finally had the luxury of starting to realise my (and &lt;a href='http://www.cogentconsulting.com.au'&gt;Cogent&amp;#8217;s&lt;/a&gt;) dream of doing product development.&lt;/p&gt;

&lt;p&gt;We just recently launched what we hope is a very simple, easy to use and somewhat opinionated web application for &lt;a href='http://www.runwayapp.com/'&gt;Getting Things Done&amp;#8482; (GTD)&lt;/a&gt;. It&amp;#8217;s a crowded market to be sure but we really believe we understand GTD well enough to deliver a system that is &lt;strong&gt;more than just a to-do list with GTD inspired keywords&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Runway is still in the early stages of feature development. For those that know anything about GTD, you&amp;#8217;ll be happy to hear that we&amp;#8217;re working on delivering Projects, Artifacts, Agendas and of course an Inbox, to name but a few, in the very near future.&lt;/p&gt;

&lt;p&gt;What you see now is, and will always be, free. at some point we&amp;#8217;ll be adding pay-for features but we&amp;#8217;ll also be doing the right thing by all our early adopters. So, if you have 5 or so minutes, we&amp;#8217;d love for you to &lt;a href='http://www.runwayapp.com/signup'&gt;sign up&lt;/a&gt;, have a play, and of course, tell us what you think.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/QxvJs_Tr3mA" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2009/05/shameless-self-promotion.html</feedburner:origLink></entry>
    
    <entry>
        <title>Web standards and all I got was this lousy website</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/UJ_0weTzNjU/web-standards-and-all-i-got-was-this.html" />
        <updated>2009-04-17T00:00:00-07:00</updated>
        <id>http://www.harukizaemon.com/2009/04/web-standards-and-all-i-got-was-this</id>
        <content type="html">&lt;p&gt;Over the Easter long weekend, I had a great break from work and a great opportunity to think about and reflect on my career, my job, and my profession as a whole. It&amp;#8217;s safe to say I become a bit disheartened and disillusioned. The one striking conclusion I kept arriving at is that we are so technology focused that we spend too much time, money and effort building things that the customer is &amp;#8220;happy&amp;#8221; with but not blown away by. That we artificially constrain the end user experience based on our notions of &amp;#8220;correctness&amp;#8221;. In particular, &lt;strong&gt;web application development is largely a bunch of dick-pulling technical masturbation&lt;/strong&gt;, forever re-inventing the wheel at a ridiculously low level of abstraction shoving our technological solutions down user&amp;#8217;s throats in the name of &amp;#8220;software engineering&amp;#8221;. &lt;strong&gt;What&amp;#8217;s worse is that I&amp;#8217;ve been complicit.&lt;/strong&gt; Not only by buying the hype but often by trying to do &amp;#8220;the right thing&amp;#8221; even when I felt as though I was bashing my head against a brick wall.&lt;/p&gt;

&lt;p&gt;Rewind the clock to somewhere between 1996 and 1999. During those years I, along with a good friend and colleague built a desktop application that was delivered to thousands of users across Australia using nothing more than good old-fashioned client-server SQL written in, of all things, PowerBuilder &amp;#8211; kinda like VisualBasic. &lt;strong&gt;More than 10 years ago, the user experience was compelling and sophisticated, it performed exceptionally well over 2400 baud dialup modems, and we built the initial release with only 2 people over 3 months from scratch.&lt;/strong&gt; As shameful as it is, especially coming from one so vocal about automated testing as I, we had nothing but manual testing but we also had few bugs and when users did find a problem, we fixed and redeployed within 24 hours &amp;#8211; mostly because we didn&amp;#8217;t want to interrupt users as they worked and so waited until after hours. Over the next 12 months, we were able to adapt to the user&amp;#8217;s needs immediately. Rarely did we add the features as request but we always managed to produce a solution they actually needed. Fast forward a decade and &lt;strong&gt;I feel like I suck&lt;/strong&gt; because I honestly don&amp;#8217;t believe I could do the same thing again today. In fact, &lt;strong&gt;I challenge any of us to build the same user experience with our existing technology stack&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To those that know me well, I will no doubt sound like a broken record but I can&amp;#8217;t help feel we&amp;#8217;ve been trying to coerce HTML &amp;amp; CSS into something they just aren&amp;#8217;t and doing so for a decade now.&lt;/p&gt;

&lt;p&gt;Think about it, HTML: HyperText Markup Language. Does that sound like it has anything to do with layout and design? In fact do you know any designers, even those that call themselves web designers, that do any of their design work in HTML/CSS? No &amp;#8211; well none that I&amp;#8217;ve ever heard of. The closest I can think of is a colleague who does his wireframes in OmniGraffle and then generates HTML/CSS. Why? I put it to you it&amp;#8217;s because &lt;strong&gt;we don&amp;#8217;t think in HTML/CSS&lt;/strong&gt;. You CAN&amp;#8217;T effectively think in HTML/CSS and if a guy who&amp;#8217;s expertise lies in designing user interfaces can&amp;#8217;t think in terms of HTML/CSS why the hell do we think we should?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTML was designed for linking documents&lt;/strong&gt; with a modicum of layout and has served that purpose admirably. As a result, the web browser largely won the battle for desktop supremacy and almost everyone has a web browser and regularly uses a number of web sites. Similarly, pretty much everyone has a computer running an 80x86 based CPU and run dozens of applications built specifically for it. &lt;strong&gt;HTML/CSS are the machine language of the web&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For those of us lucky enough to have done any assembler programming, we&amp;#8217;ve also been lucky enough not to have had to do any for a very long time. Instead, we chose to move away from assembler to other languages. C, C++, Java, Smalltalk, Python, Perl, Ruby, literally &lt;strong&gt;dozens of other programming languages that have systematically improved the level of abstraction.&lt;/strong&gt; Many of these languages now run on top of the JVM, LLVM, CLR, etc. themselves abstractions on top of the underlying CPU.&lt;/p&gt;

&lt;p&gt;Did we move because the runtime was faster? Hardly. In fact in almost all cases outrageous claims were made early on that poor performance would be the undoing of these languages and in almost all cases these claims ultimately proved unfounded. No, &lt;strong&gt;we moved to these languages because we hoped they would give us a better level of abstraction.&lt;/strong&gt; That we could code more closely to the way we think. That we would one day realise the dream of literally thinking in code.&lt;/p&gt;

&lt;p&gt;Even within languages we constantly strive to improve the level of abstraction. In many cases we&amp;#8217;ve created Domain-Specific-Languages in order that we are better able to think IN the language most appropriate to the task at hand rather than needing to perform some contorted mapping process. This is the reason the Ruby community has slowly moved from Test::Unit to RSpec/Shoulda: Test::Unit does the job just fine but it&amp;#8217;s verbose and &amp;#8220;too close to the metal&amp;#8221;. Just like assembler. &lt;strong&gt;When I&amp;#8217;m the most productive I&amp;#8217;m literally thinking in code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We&amp;#8217;ve largely sorted the back-end problems&lt;/strong&gt;: Database access layers, routing, data format conversion, validation, you name it it&amp;#8217;s all been largely worked out in whatever framework and language combination you can imagine. &lt;strong&gt;The same cannot be said of the front-end WHERE IT ACTUALLY MATTERS.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Granted, HTML/CSS has undergone change but to what extent and to what end? We have JSP, ASP, ERB, HAML, SASS, Liquid, blueprint, jQuery, Prototype, MooTools, Dojo, YUI, etc. but none of them appreciably raises the level of abstraction. &lt;strong&gt;Most advances in the world of HTML/CSS are lipstick.&lt;/strong&gt; They&amp;#8217;re all constrained by the fallacy that HTML/CSS is the holy grail of web design. No, the whole problem with web development is that &lt;strong&gt;we haven&amp;#8217;t abstracted away the underlying technology&lt;/strong&gt;, instead we&amp;#8217;ve been conned by a bunch of HTML/CSS gurus and boffins who think that designing the perfect machine code is all the world needs. &lt;strong&gt;There is nothing more primitive than HTML+CSS when it comes to the web.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;HTML &amp;amp; CSS try to be all things to all people and by doing so, much like J2EE, we ended up with a set of primitive tools that are repetitive, verbose, hard to test, maintain and refactor and ultimately provide a user experience that can best be described as &lt;strong&gt;a tarted up, 24-bit 3270 terminal.&lt;/strong&gt; Don&amp;#8217;t believe me? Point me at a website where the user experience feels liquid and natural. Where it literally gets out of your way so that you never even realise you&amp;#8217;re using it? For the most part you can&amp;#8217;t. The poster children of the Rails world provide at best a rudimentary user experience. I suspect people use them because there is no alternative, not because it&amp;#8217;s actually a great UX. Why? IMHO because the technology choices are just plain awful. If you can find a website with a rich user experience that just melts away, you&amp;#8217;ll likely find a bunch of developers who either had nervous breakdowns or spent many years building some superduper framework, or both!&lt;/p&gt;

&lt;p&gt;To be fair I&amp;#8217;m no doubt coming across as though HTML/CSS is to blame for all the world&amp;#8217;s problems. Not at all. We suffer from similar problems across the board in software development. It just so happens that I&amp;#8217;ve been in the world of web development for a long time now and feeling the effects.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;m not advocating the use of any particular technology &amp;#8211; that would kinda defeat the purpose of my argument. What I am saying is that I believe we&amp;#8217;re stuck in a mindset that only allows us to think inside the incredibly narrow bounds of something we&amp;#8217;re used to, IMHO, only because it&amp;#8217;s all we&amp;#8217;re used to.&lt;/p&gt;

&lt;p&gt;Rather than embracing the &amp;#8220;web paradigm&amp;#8221; how about we &lt;strong&gt;embrace the user and their experience and decide what technology would best enable us to deliver that.&lt;/strong&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/UJ_0weTzNjU" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2009/04/web-standards-and-all-i-got-was-this.html</feedburner:origLink></entry>
    
    <entry>
        <title>A Title Case Gem for Ruby</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/mxQ82w5Gl9o/title-case-gem-for-ruby.html" />
        <updated>2009-02-02T00:00:00-08:00</updated>
        <id>http://www.harukizaemon.com/2009/02/title-case-gem-for-ruby</id>
        <content type="html">&lt;p&gt;A project I&amp;#8217;m working on called for some &amp;#8220;smart&amp;#8221; capitalisation of page titles. Essentially I wanted to take a URL &lt;a href='http://en.wikipedia.org/wiki/Slug_(production'&gt;slug&lt;/a&gt;) and generate a page title.&lt;/p&gt;

&lt;p&gt;Rails comes with a built-in &lt;code&gt;String#titleize&lt;/code&gt; method that capitalises every word but that looked a little odd when the title was something like: &lt;em&gt;&amp;#8220;My Hovercraft Is Full Of Eels&amp;#8221;&lt;/em&gt;. So I went on a hunt for something &amp;#8220;smarter&amp;#8221;.&lt;/p&gt;

&lt;p&gt;After a little search I stumbled upon Marshall Elfstrand&amp;#8217;s &lt;a href='http://vengefulcow.com/titlecase/'&gt;JavaScript, Ruby, and Objective-C ports&lt;/a&gt; of &lt;a href='http://daringfireball.net/'&gt;John Gruber&amp;#8217;s&lt;/a&gt; &amp;#8220;Title Case&amp;#8221; algorithm and decided to turn it into a &lt;a href='http://github.com/harukizaemon/titleizer'&gt;Gem&lt;/a&gt; that adds &lt;code&gt;String#titleize&lt;/code&gt; and &lt;code&gt;String#titleize!&lt;/code&gt; (aliased as &lt;code&gt;#titlecase&lt;/code&gt;, and &lt;code&gt;#titlecase!&lt;/code&gt; respectively). When used in a Rails environment, this effectively replaces the Rails versions.&lt;/p&gt;

&lt;p&gt;Now my page titles look a little more human-like: &amp;#8220;My Hovercraft is Full of Eels&amp;#8221;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/mxQ82w5Gl9o" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2009/02/title-case-gem-for-ruby.html</feedburner:origLink></entry>
    
    <entry>
        <title>Plugins move</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/7b_xlOw5rMM/plugins-move.html" />
        <updated>2009-01-18T00:00:00-08:00</updated>
        <id>http://www.harukizaemon.com/2009/01/plugins-move</id>
        <content type="html">&lt;p&gt;Following hot on the heels of my &lt;a href='http://www.harukizaemon.com/2009/01/blog-move.html'&gt;blog move&lt;/a&gt;, I&amp;#8217;ve finally moved all my rails plugins off the venerable RubyForge and onto GitHub.&lt;/p&gt;

&lt;p&gt;Since I started working at &lt;a href='http://www.cogentconsulting.com.au'&gt;CogentConsulting&lt;/a&gt; &amp;#8211; no we&amp;#8217;re not &amp;#8220;The Company of ex-ThoughtWorkers&amp;#8221; unless you count all 3 of us as somehow statistically significant &amp;#8211; I&amp;#8217;ve had less and less time and less and less inclination to spend any appreciable effort on &lt;a href='http://www.redhillconsulting.com.au/'&gt;RedHill&lt;/a&gt; related stuff to the point where the company really exists just to support and market &lt;a href='http://www.redhillconsulting.com.au/simian'&gt;Simian&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As a consequence, I&amp;#8217;ve also dropped the RedhillOnRails moniker in favour of publishing the plugins under &lt;a href='http://github.com/harukizaemon/'&gt;my personal account&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/7b_xlOw5rMM" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2009/01/plugins-move.html</feedburner:origLink></entry>
    
    <entry>
        <title>Blog move</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/PP27sLBkOR4/blog-move.html" />
        <updated>2009-01-17T00:00:00-08:00</updated>
        <id>http://www.harukizaemon.com/2009/01/blog-move</id>
        <content type="html">&lt;p&gt;If you&amp;#8217;re reading this then the move of my blog was successful and thank-you for putting up with a screwy RSS feed during the transition. No doubt you received double or possibly even triple posts.&lt;/p&gt;

&lt;p&gt;Why the move? Well, even though &lt;a href='http://www.geekisp.com/'&gt;GeekISP&lt;/a&gt; have been a fantastic hosting provider over the years and MovableType has been pretty reliable as a blogging platform, in my never ending quest to Do Less Stuff, I figured it was time to move the pain somewhere else.&lt;/p&gt;

&lt;p&gt;From a technical perspective, the move was fairly easy though not without some pain. There is no direct way to import from MT to Blogger however I did find &lt;a href='http://movabletype2blogger.appspot.com/'&gt;a tool that helped&lt;/a&gt; convert the MT export file into something Blogger could import.&lt;/p&gt;

&lt;p&gt;I also wrote a quick script to replace all internal references with new links as well as generating a new .htaccess file for any links from the outside world. This step was pretty easy although it took some trial and error to work out what how Blogger converts titles into URLs &amp;#8211; as near as I can tell it truncates to a maximum of 40 characters with a bias towards word boundaries. The duplicate posts appearing in the RSS feed were as a direct result of me re-creating the entire blog several times fixing little things here and there.&lt;/p&gt;

&lt;p&gt;And so it is that my blog comes to be here on Blogger. The next step is to move all my domain hosting to Google Sites but that&amp;#8217;s for another day. Hopefully this will be the last move for some time and, with someone else maintaining my blogging software, hopefully less stuffing around on my part.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/PP27sLBkOR4" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2009/01/blog-move.html</feedburner:origLink></entry>
    
    <entry>
        <title>Rails, meet Drupal.</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/oM2dRSLJdWY/rails-meet-drupal.html" />
        <updated>2009-01-12T00:00:00-08:00</updated>
        <id>http://www.harukizaemon.com/2009/01/rails-meet-drupal</id>
        <content type="html">&lt;p&gt;If you&amp;#8217;ve been considering integrating (or replacing) your Drupal application with a Rails application, then &lt;a href='http://github.com/harukizaemon/drupal_fu'&gt;Drupal Fu&lt;/a&gt; may come in handy.&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s pretty rough-and-ready &amp;#8211; I essentially just ripped the code out of an existing application and cobbled it together &amp;#8211; with, as yet, no plugin infrastructure, Rakefile, or anything else that might give you a degree of confidence in the quality of the code :)&lt;/p&gt;

&lt;p&gt;That said, the code has been working in a production application for a while and we figured it might help out some others going through the same pain.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/oM2dRSLJdWY" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2009/01/rails-meet-drupal.html</feedburner:origLink></entry>
    
    <entry>
        <title>Acts As Teapot</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/iCoGMYk4-PU/acts-as-teapot.html" />
        <updated>2009-01-12T00:00:00-08:00</updated>
        <id>http://www.harukizaemon.com/2009/01/acts-as-teapot</id>
        <content type="html">&lt;p&gt;No, it&amp;#8217;s not April Fools yet but I thought I&amp;#8217;d get in early this year. &lt;a href='http://github.com/harukizaemon/acts_as_teapot'&gt;Acts As Teapot&lt;/a&gt; is a Ruby on Rails plugin that ensures your Ruby on Rails applications conform to &lt;a href='http://www.ietf.org/rfc/rfc2324.txt'&gt;RFC2324&lt;/a&gt;. My assumption here is that your application is not a coffee pot and therefore does not understand the Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0). Thus, if ever a BREW request or any other request with the Content-Type set to &amp;#8220;application/coffee-pot-command&amp;#8221; is received, the server will respond with 418 I’m a teapot.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/iCoGMYk4-PU" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2009/01/acts-as-teapot.html</feedburner:origLink></entry>
    
    <entry>
        <title>TimeMachine FTW!</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/ExOsrN7cduk/timemachine-ftw.html" />
        <updated>2008-07-25T00:00:00-07:00</updated>
        <id>http://www.harukizaemon.com/2008/07/timemachine-ftw</id>
        <content type="html">&lt;p&gt;Not withstanding the fact that I needed to restore my operating system in the first place &amp;#8211; due to an inexplicable and catastrophic failure of the Java installation resulting in segfaults &amp;#8211; I was able to restore my entire 100GB system in around 4 hours. For posterity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Boot off the OS X System Install DVD &amp;#8211; hold down option while the system starts&lt;/li&gt;

&lt;li&gt;Connect the external drive with the TimeMachine backup &amp;#8211; in my case a TimeCapsule attached via ethernet&lt;/li&gt;

&lt;li&gt;Select &amp;#8220;Restore from TimeMachine backup&amp;#8221; in the Utilities menu&lt;/li&gt;

&lt;li&gt;Select the specific backup (by timestamp) from which to restore&lt;/li&gt;

&lt;li&gt;And away you go!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The disk is then automatically erased and a fully bootable system is restored sans temp directories and cache files. It even managed to restore my PostgreSQL databases that were running at the time &amp;#8211; which probably says more about PostgreSQL than anything.&lt;/p&gt;

&lt;p&gt;The one grumble I do have is that the timestamps in the name of the backups were some non-obvious period relative to the actual date the backup was made. The difference wouldn&amp;#8217;t have been much of an issue had I simply needed to restore the most recent backup but as it turned out I needed to go back a couple of days in order to get a clean system. Thankfully I got lucky on the second attempt :)&lt;/p&gt;

&lt;p&gt;Once I had restored the system I took a look at the backup folders and sure enough there are two timestamps: the one in the folder name, and the created date. The created timestamp was spot on but the one in the folder name &amp;#8211; the one presented to you when restoring &amp;#8211; was whacky. I honestly didn&amp;#8217;t spend long enough to calculate if the difference was consistent.&lt;/p&gt;

&lt;p&gt;What is really interesting is that I had SuperDuper! on my list of software to start using but it would appear there is little need &amp;#8211; at least in my case.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/ExOsrN7cduk" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2008/07/timemachine-ftw.html</feedburner:origLink></entry>
    
    <entry>
        <title>Prevent accidental deployment with a prompt</title>
        <link href="http://feeds.harukizaemon.com/~r/MyHovercraftIsFullOfEels/~3/GdfJJEa_clw/prevent-accidental-deployment-with.html" />
        <updated>2008-06-07T00:00:00-07:00</updated>
        <id>http://www.harukizaemon.com/2008/06/prevent-accidental-deployment-with</id>
        <content type="html">&lt;p&gt;This morning I went to push out a new version of an application to our staging environment on an Engine Yard slice. I knew I had done exactly that last night so I navigated through my bash history and hit enter. Two minutes later the new version had been deployed and I was about to walk out the door to do some chores before coming back to start playing with the app. Thankfully, &lt;a href='htp://steve.cogentconsulting.com.au/'&gt;Steve&lt;/a&gt; came online and informed me that production was broken.&lt;/p&gt;

&lt;p&gt;A quick look through my bash history and it seemed I&amp;#8217;d used the deploy to production rather than deploy to staging but, being in a hurry, hadn&amp;#8217;t looked carefully enough. Of course some might argue that I should have looked more carefully. That I shouldn&amp;#8217;t have deployed before heading out. All valid points but I very rarely have full control over what&amp;#8217;s going on around me. So, while it&amp;#8217;s all very well and good to hope that I will be more careful next time, that&amp;#8217;s a bit like hoping global warming isn&amp;#8217;t a reality: we all hope it&amp;#8217;s not but maybe we should do something about it just in case?&lt;/p&gt;

&lt;p&gt;And so I added the following at the start of the &lt;code&gt;:production&lt;/code&gt; task in my &lt;code&gt;deploy.rb&lt;/code&gt; file:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;unless&lt;/span&gt; &lt;span class='no'&gt;Capistrano&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;CLI&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;ui&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;agree&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;Are you sure you want to deploy to production? (yes/no): &amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='nb'&gt;puts&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Phew! That was a close call.&amp;quot;&lt;/span&gt;
  &lt;span class='nb'&gt;exit&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class='highlight'&gt;&lt;pre&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;For all other environments, the deployment goes through without question. Attempt to deploy to production however, and I&amp;#8217;m now forced to be explicit about my intentions.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MyHovercraftIsFullOfEels/~4/GdfJJEa_clw" height="1" width="1"/&gt;</content>
    <feedburner:origLink>http://www.harukizaemon.com/2008/06/prevent-accidental-deployment-with.html</feedburner:origLink></entry>
    

</feed>
