<?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.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">

  <title><![CDATA[Robust Software]]></title>
  
  <link href="http://gshutler.com/" />
  <updated>2013-04-06T23:33:49+01:00</updated>
  <id>http://gshutler.com/</id>
  <author>
    <name><![CDATA[Garry Shutler]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/RobustSoftware" /><feedburner:info uri="robustsoftware" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
    <title type="html"><![CDATA[Ruby 2 - Module#prepend]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/ab1foofjV1s/" />
    <updated>2013-04-06T00:00:00+01:00</updated>
    <id>http://gshutler.com/2013/04/ruby-2-module-prepend</id>
    <content type="html">&lt;p&gt;As you may know &lt;a href="http://www.ruby-lang.org/en/news/2013/02/24/ruby-2-0-0-p0-is-released/"&gt;Ruby 2.0.0 has been released&lt;/a&gt;.
Despite the major version it is mostly an incremental release. However, it does
include a few breaking changes so &lt;a href="http://semver.org/"&gt;a major version is warranted&lt;/a&gt;.
However, whilst the usefulness of most of the new features was obvious to me, I
couldn&amp;#8217;t say the same of &lt;code&gt;Module#prepend&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, whilst listening to the
&lt;a href="http://rubyrogues.com/099-rr-ruby-2/"&gt;Ruby Rogues podcast on Ruby 2&lt;/a&gt; one of
the Rogues (I think it was &lt;a href="https://twitter.com/joshsusser"&gt;Josh Susser&lt;/a&gt;)
referred to &lt;a href="http://en.wikipedia.org/wiki/Memoization"&gt;memoization&lt;/a&gt; as a case
where it would make a difference. Because of that I thought I would implement
memoization in 1.9.3 and then in 2.0.0 using &lt;code&gt;Module#prepend&lt;/code&gt; and see what came
out of it. The process led to a personal &amp;#8220;ah-ha&amp;#8221; moment and so I thought I&amp;#8217;d
share what I found.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;h2&gt;Method dispatch&lt;/h2&gt;

&lt;p&gt;First, I think it&amp;#8217;s worth a little diversion into how method dispatch works in
Ruby to understand what &lt;code&gt;Module#prepend&lt;/code&gt; allows you to do that you couldn&amp;#8217;t do
before.&lt;/p&gt;

&lt;p&gt;Every object in Ruby has an ancestor chain, you can find out what this is at any
point by calling &lt;code&gt;.ancestors&lt;/code&gt; on a class:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Example&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="no"&gt;Example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ancestors&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="c1"&gt;# =&amp;gt; [Example, Object, Kernel, BasicObject]&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Whenever you call a method in Ruby, the ancestor chain is traversed looking for
a matching method to invoke. It follows it from beginning to end until it finds
a match.&lt;/p&gt;

&lt;p&gt;Modules fit into this story as when you use &lt;code&gt;Module#include&lt;/code&gt; they are added to
the ancestor chain after the class they were included in:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;After&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Example&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;After&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="no"&gt;Example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ancestors&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="c1"&gt;# =&amp;gt; [Example, After, Object, Kernel, BasicObject]&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;What &lt;code&gt;Module#prepend&lt;/code&gt; allows us to do is insert a module in-front of the class
it was prepended to:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Before&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Example&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;prepend&lt;/span&gt; &lt;span class="no"&gt;Before&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="no"&gt;Example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ancestors&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="c1"&gt;# =&amp;gt; [Before, Example, Object, Kernel, BasicObject]&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;On its own that&amp;#8217;s interesting, but what is it good for?&lt;/p&gt;

&lt;h2&gt;Memoization example&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Memoization"&gt;Memoization&lt;/a&gt; is a technique whereby
you cache the result of a, usually expensive, function so that the result can be
returned for subsequent calls without having to calculate it again. Sometimes
memoization is built into the implementation of a method, sometimes it is
implemented using the interceptor pattern so neither the caller nor the
implementation are aware that memoization is happening. We&amp;#8217;ll be using the
latter type of implementation within our example.&lt;/p&gt;

&lt;p&gt;There are already several implementations of this technique in Ruby, including
the &lt;a href="https://github.com/matthewrudy/memoist"&gt;Memoist gem&lt;/a&gt; that was extracted
from &lt;code&gt;ActiveSupport::Memoizable&lt;/code&gt; that used to be part of Rails. The problem with
these gem-ed implementations for our uses is that they tend to be quite hard to
read due to them being highly optimized and doing things such as guarding
against overwriting methods unexpectedly.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ll be creating a simple implementation to demonstrate the concept of
memoization and the effect &lt;code&gt;Module#prepend&lt;/code&gt; has on an implementation. This code
is not intended for production use!&lt;/p&gt;

&lt;p&gt;To require memoization, we really need an expensive call to make. We&amp;#8217;ll use the
same class and method for both versions of Ruby and their memoization
implementations:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Universe&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;meaning_of_life&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Calling &lt;code&gt;Universe#meaning_of_life&lt;/code&gt; once takes just over 1 second, and calling it
5 times takes just over 5 seconds. This is a prime example of a case where you
might want to memoize the result.&lt;/p&gt;

&lt;p&gt;For both implementations we will augment the &lt;code&gt;Universe&lt;/code&gt; class by memoizing the
&lt;code&gt;#meaning_of_life&lt;/code&gt; function through the use of a &lt;code&gt;Memoize&lt;/code&gt; module and its
&lt;code&gt;memoize&lt;/code&gt; method which will be implemented differently in each version of Ruby:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Universe&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;Memoize&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;meaning_of_life&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;memoize&lt;/span&gt; &lt;span class="ss"&gt;:meaning_of_life&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;This will mean that calling &lt;code&gt;Universe#meaning_of_life&lt;/code&gt; once takes just over 1
second, and calling it 5 times will also take just over 1 second too due to the
result of the first call being memoized.&lt;/p&gt;

&lt;h3&gt;Ruby 1.9&lt;/h3&gt;

&lt;p&gt;The way &lt;code&gt;Memoize&lt;/code&gt; is implemented in Ruby 1.9 is to rename the true
implementation of the method and replace it with a method that calls the true
implementation for the first call, and then returns the result of that first
call for all subsequent calls.&lt;/p&gt;

&lt;p&gt;This looks something like:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;span class='line-number'&gt;13&lt;/span&gt;
&lt;span class='line-number'&gt;14&lt;/span&gt;
&lt;span class='line-number'&gt;15&lt;/span&gt;
&lt;span class='line-number'&gt;16&lt;/span&gt;
&lt;span class='line-number'&gt;17&lt;/span&gt;
&lt;span class='line-number'&gt;18&lt;/span&gt;
&lt;span class='line-number'&gt;19&lt;/span&gt;
&lt;span class='line-number'&gt;20&lt;/span&gt;
&lt;span class='line-number'&gt;21&lt;/span&gt;
&lt;span class='line-number'&gt;22&lt;/span&gt;
&lt;span class='line-number'&gt;23&lt;/span&gt;
&lt;span class='line-number'&gt;24&lt;/span&gt;
&lt;span class='line-number'&gt;25&lt;/span&gt;
&lt;span class='line-number'&gt;26&lt;/span&gt;
&lt;span class='line-number'&gt;27&lt;/span&gt;
&lt;span class='line-number'&gt;28&lt;/span&gt;
&lt;span class='line-number'&gt;29&lt;/span&gt;
&lt;span class='line-number'&gt;30&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Memoize&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;memoize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="c1"&gt;# Work out what to rename the true implementation to&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;unmemoized_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:&amp;quot;__unmemoized_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="c1"&gt;# Create an alias to the true implementation for the&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="c1"&gt;# new name&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;alias_method&lt;/span&gt; &lt;span class="n"&gt;unmemoized_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="c1"&gt;# Overwrite the true implementation with a memoizing&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="c1"&gt;# version&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;define_method&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="c1"&gt;# Ensure we have a place to store the result in&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="c1"&gt;# case we memoize multiple methods&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="vi"&gt;@__memoized_results&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@__memoized_results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;include?&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="c1"&gt;# If we&amp;#39;ve already calculated the result of this&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="c1"&gt;# function, return it&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="vi"&gt;@__memoized_results&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="c1"&gt;# Otherwise calculate the result by calling the&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="c1"&gt;# original implementation and store it for&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="c1"&gt;# future calls&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="vi"&gt;@__memoized_results&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unmemoized_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;If we change the &lt;code&gt;Universe&lt;/code&gt; class&amp;#8217;s implementation to look like it will after
this bit of meta-programming, it would look like this:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;span class='line-number'&gt;13&lt;/span&gt;
&lt;span class='line-number'&gt;14&lt;/span&gt;
&lt;span class='line-number'&gt;15&lt;/span&gt;
&lt;span class='line-number'&gt;16&lt;/span&gt;
&lt;span class='line-number'&gt;17&lt;/span&gt;
&lt;span class='line-number'&gt;18&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Universe&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;meaning_of_life&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="vi"&gt;@__memoized_results&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@__memoized_results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;include?&lt;/span&gt; &lt;span class="ss"&gt;:meaning_of_life&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="vi"&gt;@__memoized_results&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:meaning_of_life&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="vi"&gt;@__memoized_results&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:meaning_of_life&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;__unmemoized_meaning_of_life&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__unmemoized_meaning_of_life&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;h3&gt;Ruby 2.0&lt;/h3&gt;

&lt;p&gt;Whilst the same implementation used for 1.9 would work in 2.0, due to
&lt;code&gt;Module#prepend&lt;/code&gt; we have a second option involving anonymous modules.&lt;/p&gt;

&lt;p&gt;Rather than renaming the method, we can create an anoymous module that has a
method with the same name. This module can then be prepended to the class so
that it comes &lt;strong&gt;before&lt;/strong&gt; the real implementation in the ancestor chain. The
method on the module can then call the true implementation of the method via
&lt;code&gt;super&lt;/code&gt; for the first call, and then return the result of that first call for
all subsequent calls.&lt;/p&gt;

&lt;p&gt;This looks something like this:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;span class='line-number'&gt;13&lt;/span&gt;
&lt;span class='line-number'&gt;14&lt;/span&gt;
&lt;span class='line-number'&gt;15&lt;/span&gt;
&lt;span class='line-number'&gt;16&lt;/span&gt;
&lt;span class='line-number'&gt;17&lt;/span&gt;
&lt;span class='line-number'&gt;18&lt;/span&gt;
&lt;span class='line-number'&gt;19&lt;/span&gt;
&lt;span class='line-number'&gt;20&lt;/span&gt;
&lt;span class='line-number'&gt;21&lt;/span&gt;
&lt;span class='line-number'&gt;22&lt;/span&gt;
&lt;span class='line-number'&gt;23&lt;/span&gt;
&lt;span class='line-number'&gt;24&lt;/span&gt;
&lt;span class='line-number'&gt;25&lt;/span&gt;
&lt;span class='line-number'&gt;26&lt;/span&gt;
&lt;span class='line-number'&gt;27&lt;/span&gt;
&lt;span class='line-number'&gt;28&lt;/span&gt;
&lt;span class='line-number'&gt;29&lt;/span&gt;
&lt;span class='line-number'&gt;30&lt;/span&gt;
&lt;span class='line-number'&gt;31&lt;/span&gt;
&lt;span class='line-number'&gt;32&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Memoize&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;memoize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="c1"&gt;# Create an anonymous module&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;memoizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Module&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="c1"&gt;# Define a method in the module with the same name&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="n"&gt;define_method&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="c1"&gt;# Ensure we have a place to store the result in&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="c1"&gt;# case we memoize multiple methods&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="vi"&gt;@__memoized_results&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@__memoized_results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;include?&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;          &lt;span class="c1"&gt;# If we&amp;#39;ve already calculated the result of&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;          &lt;span class="c1"&gt;# this function, return it&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;          &lt;span class="vi"&gt;@__memoized_results&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;          &lt;span class="c1"&gt;# Otherwise calculate the result by calling&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;          &lt;span class="c1"&gt;# the original implementation and store it for&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;          &lt;span class="c1"&gt;# future calls&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;          &lt;span class="vi"&gt;@__memoized_results&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="c1"&gt;# Prepend the anonymous module to the class so that&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="c1"&gt;# its method is called first&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;prepend&lt;/span&gt; &lt;span class="n"&gt;memoizer&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;If we change the implementation of the &lt;code&gt;Universe&lt;/code&gt; class to look like it will
after this bit of meta-programming, it would look like this:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;span class='line-number'&gt;13&lt;/span&gt;
&lt;span class='line-number'&gt;14&lt;/span&gt;
&lt;span class='line-number'&gt;15&lt;/span&gt;
&lt;span class='line-number'&gt;16&lt;/span&gt;
&lt;span class='line-number'&gt;17&lt;/span&gt;
&lt;span class='line-number'&gt;18&lt;/span&gt;
&lt;span class='line-number'&gt;19&lt;/span&gt;
&lt;span class='line-number'&gt;20&lt;/span&gt;
&lt;span class='line-number'&gt;21&lt;/span&gt;
&lt;span class='line-number'&gt;22&lt;/span&gt;
&lt;span class='line-number'&gt;23&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;AnonymousModule&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;meaning_of_life&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="vi"&gt;@__memoized_results&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@__memoized_results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;include?&lt;/span&gt; &lt;span class="ss"&gt;:meaning_of_life&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="vi"&gt;@__memoized_results&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:meaning_of_life&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="vi"&gt;@__memoized_results&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:meaning_of_life&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Universe&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;prepend&lt;/span&gt; &lt;span class="no"&gt;AnonymousModule&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;meaning_of_life&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;h3&gt;Comparison&lt;/h3&gt;

&lt;p&gt;In terms of lines of code, the two implementations are virtually the same. In
fact, their structure is virtually identical (whitespace ignored and comments
removed):&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;span class='line-number'&gt;13&lt;/span&gt;
&lt;span class='line-number'&gt;14&lt;/span&gt;
&lt;span class='line-number'&gt;15&lt;/span&gt;
&lt;span class='line-number'&gt;16&lt;/span&gt;
&lt;span class='line-number'&gt;17&lt;/span&gt;
&lt;span class='line-number'&gt;18&lt;/span&gt;
&lt;span class='line-number'&gt;19&lt;/span&gt;
&lt;span class='line-number'&gt;20&lt;/span&gt;
&lt;span class='line-number'&gt;21&lt;/span&gt;
&lt;span class='line-number'&gt;22&lt;/span&gt;
&lt;span class='line-number'&gt;23&lt;/span&gt;
&lt;span class='line-number'&gt;24&lt;/span&gt;
&lt;span class='line-number'&gt;25&lt;/span&gt;
&lt;span class='line-number'&gt;26&lt;/span&gt;
&lt;span class='line-number'&gt;27&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='diff'&gt;&lt;span class='line'&gt;&lt;span class="gd"&gt;--- memo19.rb&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="gi"&gt;+++ memo20.rb&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="gu"&gt;@@ -1,8 +1,7 @@&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt; module Memoize
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;   def memoize(method)
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="gd"&gt;-    unmemoized_name = :&amp;quot;__unmemoized_#{method}&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="gd"&gt;-    alias_method unmemoized_name, method&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="gi"&gt;+    memoizer = Module.new do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;     define_method method do
&lt;/span&gt;&lt;span class='line'&gt;       @__memoized_results ||= {}
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="gu"&gt;@@ -10,9 +9,13 @@&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;       if @__memoized_results.include? method
&lt;/span&gt;&lt;span class='line'&gt;         @__memoized_results[method]
&lt;/span&gt;&lt;span class='line'&gt;       else
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="gd"&gt;-        @__memoized_results[method] = send(unmemoized_name)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="gi"&gt;+          @__memoized_results[method] = super()&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;       end
&lt;/span&gt;&lt;span class='line'&gt;     end
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="gi"&gt;+    end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="gi"&gt;+    prepend memoizer&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;   end
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt; end
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;I believe the choice between the two comes down to taste, and I prefer the
&lt;code&gt;Module#prepend&lt;/code&gt; method. The reason for this is that it feels like the
&lt;code&gt;Universe&lt;/code&gt; class has been tampered with less.&lt;/p&gt;

&lt;p&gt;In the 1.9 implementation it has the method renamed and replaced, whereas in the
2.0 example it has an extra module added. Leveraging the ancestor chain also
feels like a cleaner way of achieving the goal of intercepting a call to a
method.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;I feel &lt;code&gt;Module#prepend&lt;/code&gt; is a good addition to the Ruby language. I
don&amp;#8217;t imagine I&amp;#8217;ll use it regularly, nor can I think of previously unsolveable
problems that it solves.&lt;/p&gt;

&lt;p&gt;However, from working through this memoization example I can see that it does
have its uses, and that perhaps we can now solve some problems in cleaner ways.&lt;/p&gt;

&lt;p&gt;If you&amp;#8217;ve got further thoughts on &lt;code&gt;Module#prepend&lt;/code&gt;, or anything else Ruby, you
can find me &lt;a href="https://twitter.com/gshutler"&gt;on Twitter&lt;/a&gt; or get in touch
&lt;a href="mailto:garry@robustsoftware.co.uk"&gt;via email&lt;/a&gt;.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=ab1foofjV1s:NIrCnk90oFI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=ab1foofjV1s:NIrCnk90oFI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=ab1foofjV1s:NIrCnk90oFI:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=ab1foofjV1s:NIrCnk90oFI:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=ab1foofjV1s:NIrCnk90oFI:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=ab1foofjV1s:NIrCnk90oFI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=ab1foofjV1s:NIrCnk90oFI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/ab1foofjV1s" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2013/04/ruby-2-module-prepend/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Logging]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/-H2p-w8gh_I/" />
    <updated>2012-12-30T00:00:00+00:00</updated>
    <id>http://gshutler.com/2012/12/logging</id>
    <content type="html">&lt;p&gt;If there was one thing that I learnt in 2012 that I would want to convey to all
the developers I know, it would be this:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;Logging is about so much more than failures&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;I don&amp;#8217;t know if it was just my experience but little to no emphasis was put on
logging aside from handling exceptions. In the .NET world this boiled down to
adding ELMAH to your project and then forgetting about it, with Rails it meant
having nothing but the logs that you got out of the box. I&amp;#8217;ve found that if
that&amp;#8217;s all you have, you&amp;#8217;re really missing out.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;h2&gt;Benefits of good logging&lt;/h2&gt;

&lt;h3&gt;Bug investigations&lt;/h3&gt;

&lt;p&gt;I think that every system I&amp;#8217;ve ever worked on has been distributed, at least in
some form. It may have been as simple as scheduled processes working alongside a
web site, or it may have been a truly distributed, message-driven system.
Distributed systems can be hard to monitor and bugs can be particularly tricky
to track down. The complex interactions dependent upon the state of different
components can be a nightmare to reproduce.&lt;/p&gt;

&lt;p&gt;Good logging virtually solves this problem. Given sufficient detail in your
logs, you no longer have to theorise or piece together how something happened.
Your logs will lay it out for you, plain as day.&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s a contrived example to demonstrate where better logging can help you:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;span class='line-number'&gt;13&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Background&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;privileged?&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;transient_condition?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="n"&gt;something_dangerous&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transient_condition?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_with?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random_character&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;&lt;code&gt;transient_condition?&lt;/code&gt; is the most contrived part but imagine you&amp;#8217;ve received a
bug report a day later and multiple things have changed on their account and the
entire system that &lt;code&gt;transient_condition?&lt;/code&gt; references since that time. You&amp;#8217;ll end
up with the same effect.&lt;/p&gt;

&lt;p&gt;So, why did &lt;code&gt;something_dangerous&lt;/code&gt; get executed for our friend Bob yesterday?
Where do you start? Check the database for their current state? How do you know
they haven&amp;#8217;t updated their profile since? Perhaps you use event sourcing, how
easy is it for you to roll back everything in the system that could have had an
effect to that point in time? What was that point in time?&lt;/p&gt;

&lt;p&gt;You can see where I&amp;#8217;m going so I won&amp;#8217;t labour the point. It probably will,
however, be laborious to investigate this bug report. This is where I would have
been a year ago, piecing together transient bits of information and responding
with sentences containing &amp;#8220;probably&amp;#8221;.&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s how I would write that same code today:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;span class='line-number'&gt;13&lt;/span&gt;
&lt;span class='line-number'&gt;14&lt;/span&gt;
&lt;span class='line-number'&gt;15&lt;/span&gt;
&lt;span class='line-number'&gt;16&lt;/span&gt;
&lt;span class='line-number'&gt;17&lt;/span&gt;
&lt;span class='line-number'&gt;18&lt;/span&gt;
&lt;span class='line-number'&gt;19&lt;/span&gt;
&lt;span class='line-number'&gt;20&lt;/span&gt;
&lt;span class='line-number'&gt;21&lt;/span&gt;
&lt;span class='line-number'&gt;22&lt;/span&gt;
&lt;span class='line-number'&gt;23&lt;/span&gt;
&lt;span class='line-number'&gt;24&lt;/span&gt;
&lt;span class='line-number'&gt;25&lt;/span&gt;
&lt;span class='line-number'&gt;26&lt;/span&gt;
&lt;span class='line-number'&gt;27&lt;/span&gt;
&lt;span class='line-number'&gt;28&lt;/span&gt;
&lt;span class='line-number'&gt;29&lt;/span&gt;
&lt;span class='line-number'&gt;30&lt;/span&gt;
&lt;span class='line-number'&gt;31&lt;/span&gt;
&lt;span class='line-number'&gt;32&lt;/span&gt;
&lt;span class='line-number'&gt;33&lt;/span&gt;
&lt;span class='line-number'&gt;34&lt;/span&gt;
&lt;span class='line-number'&gt;35&lt;/span&gt;
&lt;span class='line-number'&gt;36&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Background&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Hatchet&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Entering execute(user:#&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;do_something_dangerous&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;privileged?&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;#&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is privileged&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="n"&gt;do_something_dangerous&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;transient_condition?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;#&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; satisfied transient condition&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="n"&gt;do_something_dangerous&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;do_something_dangerous&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Doing something dangerous for #&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="n"&gt;something_dangerous&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Not doing something dangerous for #&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Exiting execute(user:#&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transient_condition?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Entering transient_condition?(user:#&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random_character&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Checking if first_name:#&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; starts with character:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_with?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random_character&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Exiting transient_condition?(user:#&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) - return:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Yes, this is much longer but it has the exact same logic. We&amp;#8217;re capturing the
context of each branch of logic so that we may be able to understand what
happened after the fact. For me, this is the difference between clean code and
production-ready code.&lt;/p&gt;

&lt;p&gt;Now let&amp;#8217;s go back to our bug report. Why did &lt;code&gt;something_dangerous&lt;/code&gt; get executed
for Bob yesterday? Let&amp;#8217;s find out with a little grep-fu:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ egrep -A 6 "Entering execute\(user:Bob" yesterday.log

13:53:01 INFO Background - Entering execute(user:Bob)
13:53:01 INFO Background - Entering transient_condition?(user:Bob)
13:53:01 INFO Background - Checking if first_name:Bob starts with character:B
13:53:01 INFO Background - Exiting transient_condition?(user:Bob) - return:true
13:53:01 INFO Background - Bob satisfied transient condition
13:53:01 INFO Background - Doing something dangerous for Bob
13:53:06 INFO Background - Exiting execute(user:Bob)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There&amp;#8217;s our answer, Bob satisfied the transient condition. Now you can
definitively respond to the bug report with the exact reason why something
dangerous happened for Bob yesterday, regardless of the state of the system
today. We can also see that doing something dangerous took around 5 seconds.&lt;/p&gt;

&lt;p&gt;Which brings me on to a second benefit of logging.&lt;/p&gt;

&lt;h3&gt;Performance profiling&lt;/h3&gt;

&lt;p&gt;Particularly when working with bulk processes it can be extremely difficult to
build up a test suite that truly reflects production. If you have a database
involved you also have the effects of simultaneous queries being executed, the
state of indexes, etc. The exact hardware can also have an effect. For all your
efforts, sometimes there is no better place to profile code than in your
production environment whilst working on real-world data.&lt;/p&gt;

&lt;p&gt;However, you don&amp;#8217;t want to attach a profiler to your production processes
directly. That would mean too much access to a production system, it can often
affect how the process executes, and if it&amp;#8217;s a nightly task you may have to be
up at 4am to watch it. None of those things are desirable.&lt;/p&gt;

&lt;p&gt;If you invest in your logs you will not only get the information needed to
troubleshoot things happening in your system, you&amp;#8217;ll also get a good level of
performance profiling too. Most logging frameworks can give you timestamps to
millisecond precision, and in my experience that&amp;#8217;s more than enough to narrow
down which method or methods are taking the most time. This allows you to focus
your efforts on where the problem lies in production, and avoids speculative
optimisation based upon sterile tests.&lt;/p&gt;

&lt;p&gt;Particularly with bulk processes I believe it is hard to log too much. They tend
to be extremely reliant on state and can rarely be re-run after the fact, they
often run overnight during your system&amp;#8217;s quiet window, and they are often left
untouched for months as their requirements tend to change much less frequently.&lt;/p&gt;

&lt;p&gt;Good logs give you view into the behaviour of all your code. That insight really
pays dividends for code that you rarely see and rarely touch.&lt;/p&gt;

&lt;h3&gt;Understanding unfamiliar code&lt;/h3&gt;

&lt;p&gt;Good logs give you insight into how a system works, beyond just looking at the
code. It helps you understand how frequently a class is used, how frequently a
method is called, how often the various branches in a method are hit, etc. Just
looking at the code cannot give you that same insight.&lt;/p&gt;

&lt;p&gt;Logs can be used to safely prune &amp;#8220;probably dead&amp;#8221; code or features. If you
suspect a branch of logic can be removed, or that a feature is rarely used, you
can simply add an additional log statement or raise the level of an existing
one:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perhaps_dead&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warn&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;DEPRECATED perhaps_dead called&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="c1"&gt;## does something&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;You can then review your logs a week or two later to see if any messages have
been recorded. If there aren&amp;#8217;t any messages you can be more confident in
removing it. If there are several messages then you can keep it in place and
remove the message without anyone ever being affected.&lt;/p&gt;

&lt;p&gt;Good logs replace the need for a debugger, but better than that they provide a
historic debugger. You can step through code executed days ago by following the
messages that were logged as you look through the code, following the branches
taken according to the next message logged. This is invaluable for answering
questions like &amp;#8220;how many people were affected by this bug?&amp;#8221;&lt;/p&gt;

&lt;h3&gt;Additional context for errors&lt;/h3&gt;

&lt;p&gt;I&amp;#8217;m pretty sure that everyone logs errors. However, they can often be pretty
useless apart from telling you what line of code the failure came from. Think of
the beloved null reference exception, if you have something like:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;If this throws a null reference exception which of left or right was null? Also,
if this is 5 steps down a path of execution what are left and right in the first
place?&lt;/p&gt;

&lt;p&gt;Good logs give you the additional context of the state of the system &lt;em&gt;before&lt;/em&gt;
the error happened which can be vital in understanding &lt;em&gt;why&lt;/em&gt; the error happened.&lt;/p&gt;

&lt;h2&gt;How to log well&lt;/h2&gt;

&lt;p&gt;Good logs are all about context. A good message answers the questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where are you?&lt;/li&gt;
&lt;li&gt;What state matters?&lt;/li&gt;
&lt;li&gt;Why are you there?&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;The good news is that most decent loggers are class-based and so they deal with
the majority of &amp;#8220;where&amp;#8221; on your behalf, you only have to add the method when it
is useful. The difficult parts are the &amp;#8220;what&amp;#8221; and the &amp;#8220;why&amp;#8221;.&lt;/p&gt;

&lt;p&gt;For the &amp;#8220;what&amp;#8221;, a trick I use when I am unsure is to imagine I have hit a
breakpoint. I then imagine what variables I would be inspecting, are there
particular properties of those variables that are interesting at that point in
time? The things I would be inspecting with a debugger are the things I record
in my log messages.&lt;/p&gt;

&lt;p&gt;Much like a bug is a sign of a missing unit test, the need to use a debugger is
the sign of a missing log message. Each time you reach for a debugger, ask
yourself what log message would avoid you doing so and add it. If you aren&amp;#8217;t
sure, mindfully use the debugger and notice what you are looking at, then put
the message in place.&lt;/p&gt;

&lt;p&gt;You can&amp;#8217;t attach a debugger to production, but you can read your logs. If the
log message is useful to you now, it may well be useful to you in production
some day.&lt;/p&gt;

&lt;p&gt;Recording the &amp;#8220;why&amp;#8221; tends to be a bit more straight forward. You want every
logical path in your code to result in a log statement, and that statement
should record what was satisfied in order for you to hit that path. Our original
example demonstrates this well.&lt;/p&gt;

&lt;p&gt;I refer to this state of thinking as the maintainer&amp;#8217;s mindset. Try and imagine
yourself as the maintainer of the code, trying to resolve a production issue,
and give yourself the information you would need to shorten the time to resolve
it.&lt;/p&gt;

&lt;h2&gt;How to avoid being overwhelmed by messages&lt;/h2&gt;

&lt;p&gt;You are probably thinking you&amp;#8217;ll start generating gigs and gigs of log files,
and you may be right. However, remember that log messages can be written at
various levels, and that you can configure your logger to discard particular
messages based upon level or even the class they came from.&lt;/p&gt;

&lt;p&gt;DEBUG, INFO, WARN, ERROR, and FATAL are pretty ubiquitous across all logging
frameworks and they all convey a level of importance.&lt;/p&gt;

&lt;p&gt;By default I have production loggers set up to record INFO or higher, meaning
that DEBUG messages are discarded. This may make you wonder why you would leave
them in, but if they were useful at one time they may be useful again. You can
quickly and easily change your production configuration to record DEBUG
messages, but you can&amp;#8217;t quickly and easily add them across your code base.&lt;/p&gt;

&lt;h2&gt;Chosing the right level for a message&lt;/h2&gt;

&lt;h3&gt;DEBUG&lt;/h3&gt;

&lt;p&gt;The finest level of detail. I rarely add a message at this level to begin with.
I will usually start with the message at INFO and then downgrade it in a later
release if it proves to have a low signal-to-noise level in production.&lt;/p&gt;

&lt;h3&gt;INFO&lt;/h3&gt;

&lt;p&gt;Something generally interesting but not something to be worried about. These
should give enough information to understand what the system is doing at any
point in time.&lt;/p&gt;

&lt;h3&gt;WARN&lt;/h3&gt;

&lt;p&gt;Not an error, but something worth highlighting to someone monitoring the logs.
For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handling a particularly rare edge case&lt;/li&gt;
&lt;li&gt;Hitting deprecated code&lt;/li&gt;
&lt;li&gt;Falling over to a secondary provider&lt;/li&gt;
&lt;li&gt;Particularly slow response times&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;ERROR&lt;/h3&gt;

&lt;p&gt;Generally tied to an error though not always. For example, you might log an
ERROR message when you encounter a state that can be handled gracefully but is
not reasonable to expect. There was no error as such but the fact it happened
deserves the additional attention a higher level demands.&lt;/p&gt;

&lt;p&gt;If your logging framework allows, always pass through the error object if there
is one. Logging frameworks can usually have their output configured to include
the stack trace formatted well without you having to do it in your log message.&lt;/p&gt;

&lt;h3&gt;FATAL&lt;/h3&gt;

&lt;p&gt;Reserved for when the underlying process will be halted as a result. To that end
I only use these on background processes as web servers should never die
completely.&lt;/p&gt;

&lt;h2&gt;Frameworks&lt;/h2&gt;

&lt;p&gt;For .NET I&amp;#8217;ve never seen a need to look beyond &lt;a href="http://logging.apache.org/log4net/"&gt;log4net&lt;/a&gt;,
similarly for Java I&amp;#8217;ve used &lt;a href="http://logging.apache.org/log4j/2.x/"&gt;log4j&lt;/a&gt;. For
Ruby I couldn&amp;#8217;t find a framework that did what I wanted so I created
&lt;a href="http://gshutler.github.com/hatchet/"&gt;Hatchet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Creating your own framework is rarely a good idea. In particular the parts that
deal with writing messages are tough to get right and existing frameworks have
been battle hardened. So &lt;a href="http://gshutler.github.com/hatchet/about.html"&gt;while I had my reasons for creating Hatchet&lt;/a&gt;,
the actual logging is performed by the logger in Ruby&amp;#8217;s standard library.&lt;/p&gt;

&lt;h3&gt;Performance and configuration&lt;/h3&gt;

&lt;p&gt;It&amp;#8217;s worth mentioning the performance hit of logging, and configuration come
hand-in-hand with that. One of the main concerns I&amp;#8217;ve heard for logging as I&amp;#8217;ve
described is &amp;#8220;won&amp;#8217;t it slow down the application?&amp;#8221; The answer is obviously yes,
you can&amp;#8217;t do anything for free. However, the benefits I&amp;#8217;ve outlined far outweigh
the small cost of writing the logs if you get your configuration right.&lt;/p&gt;

&lt;p&gt;The biggest mistake you can make in your log configuration is having everything
written to a database. Doing so incurs the cost of an insert for every log
message you write. In comparison, writing the message to a local disk is an
order of magnitude or more faster. The performance penalty of going to the
database can quickly add up. For example, I have switched some old SQL-backed
logging code to log4net writing to local disk and saw a 10-20% performance
increase from that change alone.&lt;/p&gt;

&lt;p&gt;Costly calls should be reserved for infrequent events such as errors. Think of
SaaS products such as Airbrake, they have the overhead of a network call but it
is worth it to get notification of something as significant as an error. At Zopa
we have errors sent to our HipChat channel as we feel the cost of the call is
justified by the real-time notifications we receive for production issues.&lt;/p&gt;

&lt;p&gt;Be aware that no message you log is free and configure the overhead incurred
accordingly. 95% of your log messages will be WARN or lower, and that is the
case that should be optimised.&lt;/p&gt;

&lt;h2&gt;Wrapping up&lt;/h2&gt;

&lt;p&gt;If in doubt log too much and log to a local disk. Having too much information
about what your system is doing is significantly better than having too little.
Logs can always be filtered, and message levels and log configurations can
always be changed.&lt;/p&gt;

&lt;p&gt;If you aren&amp;#8217;t used to working with log files you will have to learn some new
skills to query files rather than a database, but a decent log viewer like
&lt;a href="http://code.google.com/p/otroslogviewer/"&gt;Ostros&lt;/a&gt; will make most things as easy
if not easier than using SQL. Sometimes you can&amp;#8217;t beat a bit of grep-fu, but
those skills are useful any time you need to work with any kind of text file.&lt;/p&gt;

&lt;p&gt;I hope I&amp;#8217;ve conveyed the benefits of good logging and given some guidance on the
practices to be able to produce good logs for your own code.&lt;/p&gt;

&lt;p&gt;If you need any help getting started get in touch on
&lt;a href="https://twitter.com/gshutler"&gt;Twitter (@gshutler)&lt;/a&gt; or
&lt;a href="mailto:garry+logging@robustsoftware.co.uk"&gt;via email&lt;/a&gt;.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=-H2p-w8gh_I:BO-QqDs3GOY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=-H2p-w8gh_I:BO-QqDs3GOY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=-H2p-w8gh_I:BO-QqDs3GOY:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=-H2p-w8gh_I:BO-QqDs3GOY:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=-H2p-w8gh_I:BO-QqDs3GOY:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=-H2p-w8gh_I:BO-QqDs3GOY:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=-H2p-w8gh_I:BO-QqDs3GOY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/-H2p-w8gh_I" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2012/12/logging/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Minimalism in an Age of Tremendous Hardware]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/HbyIazf1SLw/" />
    <updated>2012-09-19T00:00:00+01:00</updated>
    <id>http://gshutler.com/2012/09/minimalism-in-an-age-of-tremendous-hardware</id>
    <content type="html">&lt;blockquote&gt;&lt;p&gt;Usually &amp;#8211; almost always &amp;#8211; there&amp;#8217;s a much simpler solution waiting to be&lt;br/&gt;discovered, one that doesn&amp;#8217;t involve all the architectural noise, convolutions&lt;br/&gt;of the straightforward, and misguided emphasis on hooks and options for all&lt;br/&gt;kinds of tangents which might be useful someday. Discovering that solution may&lt;br/&gt;not be easy, but it is time well spent.&lt;/p&gt;&lt;footer&gt;&lt;strong&gt;James Hague&lt;/strong&gt; &lt;cite&gt;&lt;a href='http://prog21.dadgum.com/151.html'&gt;Minimalism in an Age of Tremendous Hardware&lt;/a&gt;&lt;/cite&gt;&lt;/footer&gt;&lt;/blockquote&gt;

&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=HbyIazf1SLw:3X-Uh3VrBAc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=HbyIazf1SLw:3X-Uh3VrBAc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=HbyIazf1SLw:3X-Uh3VrBAc:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=HbyIazf1SLw:3X-Uh3VrBAc:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=HbyIazf1SLw:3X-Uh3VrBAc:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=HbyIazf1SLw:3X-Uh3VrBAc:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=HbyIazf1SLw:3X-Uh3VrBAc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/HbyIazf1SLw" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2012/09/minimalism-in-an-age-of-tremendous-hardware/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[The Boy Scout Rule]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/PcFseZYDWNA/" />
    <updated>2012-09-10T00:00:00+01:00</updated>
    <id>http://gshutler.com/2012/09/boy-scout-rule</id>
    <content type="html">&lt;p&gt;My friend &lt;a href="https://twitter.com/CleverFinn"&gt;Dom &amp;#8220;Knave of Nottingham&amp;#8221; Finn&amp;#8217;s&lt;/a&gt;
wrote a post &lt;a href="http://www.dominicfinn.co.uk/to-fix-or-not-to-fix"&gt;To Fix Or Not To Fix&lt;/a&gt;.
This is something I&amp;#8217;ve thought about quite a lot as, lets be honest, everyone is
maintaining a legacy codebase to some degree.&lt;/p&gt;

&lt;p&gt;The Boy Scout (or Girl Guide) Rule is to leave the campsite tidier than you
found it. This helps avoid making the problem any worse and the propagation of
&lt;a href="http://en.wikipedia.org/wiki/Broken_windows_theory"&gt;Broken Windows&lt;/a&gt;.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;h2&gt;What motivates refactoring?&lt;/h2&gt;

&lt;p&gt;The question Dom raised is how much is too much and when should you refactor?
Firstly, this is being done as part of a change with a business motivation isn&amp;#8217;t
it? If you are refactoring for the sake of refactoring, without some wider goal,
you are wasting time that could be spent adding value. Refactoring should be in
service of a change, not the change itself.&lt;/p&gt;

&lt;p&gt;You revisit important sections of code frequently. If all you do is make a small
improvement each time you alter a section of code, if that really is an
important area of the code you&amp;#8217;ll be back soon enough and those little
improvements will compound over time. Also, each time you revisit the code
you&amp;#8217;ll know a bit more about the domain and be able to refactor more
intelligently. Today&amp;#8217;s obvious grand refactor could be tomorrow&amp;#8217;s retarded idea.
Delay major decisions as long as possible.&lt;/p&gt;

&lt;h2&gt;Everything you didn&amp;#8217;t write is shit&lt;/h2&gt;

&lt;p&gt;The motivation for refactoring code comes from the fact you think it is shit.
For some measure of shit. It might not be using the right pattern for your
liking, it might be formatted wrong, it could be written in the wrong language.&lt;/p&gt;

&lt;p&gt;That could could be yours that wrote a few months ago leaving a lot to be
desired. There are many causes of this, you&amp;#8217;ve improved as a programmer in that
time, or you may understand the domain better with the experience gained.&lt;/p&gt;

&lt;p&gt;As for other people&amp;#8217;s code I always try, though it can be hard, to assume they
had some deeper insight than I have right now, or at worst they just didn&amp;#8217;t know
any better than to create the travesty in front of me. The first thing to
remember is that people generally do the best they can under whatever pressures
they were had at the time.&lt;/p&gt;

&lt;p&gt;Gain objectivity over the problem by removing the emotion from the situation. If
you&amp;#8217;re angry and start a code deleting frenzy you &lt;strong&gt;will&lt;/strong&gt; cause more problems
than you solve. Refactoring is a time for a cool, methodical head. Particularly
if that code has little or no tests and you don&amp;#8217;t fully understand what it is
doing. Dump your emotions before you contemplate starting.&lt;/p&gt;

&lt;h2&gt;How much should you refactor?&lt;/h2&gt;

&lt;p&gt;As much as you need to get the job done in a sensible manner. Any refactoring
you do needs to be kept within the context of the task that led you to that
code. Don&amp;#8217;t blow your allocated time because you&amp;#8217;ve spent 3 hours of a 1 hour
task tidying up because the code was &amp;#8220;such a mess&amp;#8221; without having made the
change yet.&lt;/p&gt;

&lt;p&gt;For me, there is an initial stage of refactoring where you move the code around,
making it abide by coding standard so you can read it, commenting methods as you
find out what they do. All this is to help you understand the code and identify
where you need to make the change. This shouldn&amp;#8217;t be more than 10% of the
overall time allocated to the task.&lt;/p&gt;

&lt;p&gt;Once you&amp;#8217;ve identified where the change is required, consider reverting some or
all of the changes you made in order to get there. Have you made the code better
or just moved it around? If you&amp;#8217;ve just moved it around perhaps revert it, it
will make the intent of your real change clearer in the history and you will
have changed less, reducing the possibility of introducing new bugs.&lt;/p&gt;

&lt;p&gt;At this point you may think that your change will be easier to make if only you
refactor this area first. This may be right, but always be sure to keep in mind
the scope of the original task. If you have one day to do a task and refactoring
for half a day will make it a 5 minute change that could be acceptable. But
always keep in mind the risk you are adding by altering what is, I hope, code
that works.&lt;/p&gt;

&lt;p&gt;Don&amp;#8217;t be afraid to throw changes away. Try that refactor and if you&amp;#8217;re 30
minutes in and the rabbit hole just seems to keep getting deeper, stop. Get your
bearings. Consider that going back to where you were and doing it without the
major refactor could be the more pragmatic approach.&lt;/p&gt;

&lt;p&gt;All this is made much easier if you commit often, with explanatory commit
messages. Being able to step back 30 minutes to after the general tidy but
before attempting the grand refactor gives you an easy way to back out.&lt;/p&gt;

&lt;h2&gt;I&amp;#8217;m not you, I don&amp;#8217;t know your code base&lt;/h2&gt;

&lt;p&gt;There are no hard and fast rules, every situation is different and you have to
use your own judgement to decide the best course of action. All of this is just
how I approach the problem, the steps I go through to try and avoid wasting time
making changes that don&amp;#8217;t really need to be made.&lt;/p&gt;

&lt;p&gt;Every developer loves a good refactorbation session but we&amp;#8217;re not paid to
pleasure ourselves, we&amp;#8217;re paid to use our skills to make our companies money.&lt;/p&gt;

&lt;p&gt;Keep that in mind at all times and good luck out there.&lt;/p&gt;

&lt;p&gt;Want to continue the conversation? You can get in touch with me by email at
&lt;a href="garry@robustsoftware.co.uk"&gt;garry@robustsoftware.co.uk&lt;/a&gt; or on Twitter at
&lt;a href="http://twitter.com/gshutler"&gt;@gshutler&lt;/a&gt;.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=PcFseZYDWNA:8ZL7MGnTtGI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=PcFseZYDWNA:8ZL7MGnTtGI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=PcFseZYDWNA:8ZL7MGnTtGI:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=PcFseZYDWNA:8ZL7MGnTtGI:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=PcFseZYDWNA:8ZL7MGnTtGI:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=PcFseZYDWNA:8ZL7MGnTtGI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=PcFseZYDWNA:8ZL7MGnTtGI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/PcFseZYDWNA" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2012/09/boy-scout-rule/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[DDD 10 - 10 Practices]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/bhZCpoMLxz0/" />
    <updated>2012-09-08T00:00:00+01:00</updated>
    <id>http://gshutler.com/2012/09/ddd10</id>
    <content type="html">&lt;p&gt;Among some of the &lt;a href="http://www.voxpop.at/ddd10/5/1"&gt;more interesting feedback&lt;/a&gt;
were some requests for some links and an overview of the things mentioned in my
talk.&lt;/p&gt;

&lt;p&gt;A few people mentioned they felt misled by
&lt;a href="http://developerdeveloperdeveloper.com/ddd10/ViewSession.aspx?SessionID=1018"&gt;the title and synopsis of the talk&lt;/a&gt;.
I read it back and I think it&amp;#8217;s accurate, but then I would. If you are one of
those people can you email me at &lt;a href="garry@robustsoftware.co.uk"&gt;garry@robustsoftware.co.uk&lt;/a&gt;
with suggestions on how I should change it. It would be greatly appreciated.&lt;/p&gt;

&lt;h2&gt;Slides&lt;/h2&gt;

&lt;p&gt;I&amp;#8217;ve put them &lt;a href="http://www.slideshare.net/gshutler/10-practices-that-make-me-the-developer-i-am-today"&gt;up on Slideshare&lt;/a&gt;
though without the context of the talk they might not be much use. I used the
&lt;a href="http://www.google.com/webfonts/specimen/Bangers"&gt;Bangers font from Google web fonts&lt;/a&gt;
and the &lt;a href="http://www.colourlovers.com/palette/15/tech_light"&gt;tech light palette from colourlovers&lt;/a&gt;.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;h2&gt;Talking points&lt;/h2&gt;

&lt;h3&gt;Standards&lt;/h3&gt;

&lt;p&gt;I made the point that standards are important for putting the focus on the logic
of the code rather than its layout. However, not to worry too much about what
the standards are, so long as they are consistent.&lt;/p&gt;

&lt;p&gt;If I have to put standards into place I will generally point to the canonical
standard for that language such as &lt;a href="http://msdn.microsoft.com/en-us/library/ff926074.aspx"&gt;the C# Coding Conventions on MSDN&lt;/a&gt;
though if there is a tool that can automate compliance such as &lt;a href="http://stylecop.codeplex.com/"&gt;StyleCop&lt;/a&gt;
I will favour that as it settles all arguments.&lt;/p&gt;

&lt;h3&gt;Code reviews&lt;/h3&gt;

&lt;p&gt;I believe peer code review to be one of the most effective ways of sharing
knowledge and improving the quality of a code base. Pairing comes close but I
see that as more beneficial in that it gets two heads together to solve a
problem. Code reviews have the benefit of the reviewer coming in cold with
little context. This helps them identify inconsistencies a pair will also think
of as obvious and flaws that a pair will have helped create.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve used pieces of paper, email, and bug trackers for code reviews but I would
recommend the ability to annotate the code inline that you get with &lt;a href="https://github.com/"&gt;Github&lt;/a&gt;
or the &lt;a href="http://www.reviewboard.org/"&gt;open source Review Board&lt;/a&gt;. The additional
context to the comments given make them much more effective and removes
ambiguity over what is being discussed.&lt;/p&gt;

&lt;h3&gt;Business analysis&lt;/h3&gt;

&lt;p&gt;Ah, the difficult practice of listening and understand something completely
foreign to you. The art of knowing what question to ask and when. The practice
is made much more apparent when you are talking to someone non-technical but
should extend to how you discuss topics with your technical peers.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve never found a shortcut for this one and no resources are coming to mind. I
think this is one of those skills that takes time, practice, and of course
failure, to get better at. You can observe someone who is good at it and that
will give you pointers but putting it in to practice is a different kettle of
fish.&lt;/p&gt;

&lt;h3&gt;Learn constantly&lt;/h3&gt;

&lt;p&gt;No-one else cares about your career and improvement as much as you do. Often
companies will support you in your efforts by buying books and so on but you
need to be the one pushing for it.&lt;/p&gt;

&lt;p&gt;It doesn&amp;#8217;t take masses of effort, a couple of hours a week will get you through
most technical books in a month or two. However, be sure to maximise your return
on investment. By that I mean don&amp;#8217;t read books where you&amp;#8217;ll know 90% of the
content. Pick a book on a new subject, particularly one that will give you tools
you can use every day over one about development in the language you use each
day.&lt;/p&gt;

&lt;p&gt;Books that fall into this category for me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.martinfowler.com/books/eaa.html"&gt;Patterns of Enterprise Application Architecture&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;a more practical, hands-on version of the Gang Of Four&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.amazon.co.uk/Peopleware-Productive-Projects-Teams-2nd/dp/0932633439"&gt;Peopleware&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;a great primer on managing people and teams&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="http://pragprog.com/book/trevan/driving-technical-change"&gt;Driving Technical Change&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;learn how to put your ideas into action more effectively&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;Learn new languages&lt;/h3&gt;

&lt;p&gt;If you want to invest the time in learning a new language, again maximise your
return by learning a different style of language. You won&amp;#8217;t learn much from Java
that you don&amp;#8217;t know from C#. Instead pick a different class of language: a
dynamic one such as Python, Ruby, or Javascript, a functional one like F#, or
Haskell, or something in between like Scala or Erlang.&lt;/p&gt;

&lt;p&gt;You&amp;#8217;ll find you&amp;#8217;ll become a better better programmer in your original language
as it will give you a different perspective on problems. Hell, you could hit a
problem and say &amp;#8220;Erlang is the perfect language to solve this&amp;#8221; and end up
writing an application in that language.&lt;/p&gt;

&lt;h3&gt;Testing&lt;/h3&gt;

&lt;p&gt;Testing is all about increasing confidence in a codebase. The difference between
believing something works and knowing is massive. Automated testing is about
reducing feedback loops from the point of code being written to knowing it
works.&lt;/p&gt;

&lt;p&gt;The smaller the feedback loop, the faster you can go as the easier problems
become to solve. If you&amp;#8217;ve changed one line of code since you last knew it
worked you can be pretty sure that one line of code is where the problem is.&lt;/p&gt;

&lt;p&gt;If it&amp;#8217;s been two months since you threw that code over the wall to the test team
you&amp;#8217;re going to have a hard time knowing where the problem is and you won&amp;#8217;t have
the knowledge required at the very front of your mind any more.&lt;/p&gt;

&lt;h3&gt;Automation&lt;/h3&gt;

&lt;p&gt;Once you have automated tests it seems a shame to not have them run regularly
one check-in. It then seems a shame to not automate the deployment of that code
to a staging environment. Once you&amp;#8217;re there it should be pretty easy to deploy
to production so you may as well do that as well. This again reduces the
feedback loop from developer machine to production, generally reducing the
amount that is released at once helping your narrow down the problem space when
issues arise.&lt;/p&gt;

&lt;p&gt;The question isn&amp;#8217;t &amp;#8220;why should I automate that?&amp;#8221; it&amp;#8217;s &amp;#8220;why shouldn&amp;#8217;t I automate
that?&amp;#8221;.&lt;/p&gt;

&lt;h3&gt;Know when to move&lt;/h3&gt;

&lt;p&gt;Sometimes it&amp;#8217;s not possible for you to achieve all that you want at your current
company. It&amp;#8217;s a shame but you need to recognise it and make a decision whether
you need to move company to avoid the glass ceiling you&amp;#8217;re under. However, have
a conversation with your current company first to see if they can help you
achieve what you want. It&amp;#8217;s always worth asking.&lt;/p&gt;

&lt;h3&gt;Know what you want&lt;/h3&gt;

&lt;p&gt;If you decide to move companies, make sure you know what you are looking for.
Interview your interviewers as to what they can offer you, the commitment of a
new job is two-way after all. Push them on the points that really matter to you.
If you&amp;#8217;ve ever interviewed anyone before and found their CV is an exaggeration
of the truth, don&amp;#8217;t think a company&amp;#8217;s job description is any different.&lt;/p&gt;

&lt;p&gt;A new job is a commitment for a year or more of your life, do whatever is needed
to find out whether you&amp;#8217;re committing yourself to the right company.&lt;/p&gt;

&lt;h3&gt;New jobs are scary&lt;/h3&gt;

&lt;p&gt;I made the point that joining a new company is always a bit of a scary time.
Will you fit in? Will you find out you&amp;#8217;re a fraud? Was everything you were told
in your interview true? Will I achieve what I want?&lt;/p&gt;

&lt;p&gt;Rather than trying to ignore it, embrace the fear and channel it into positive
action.&lt;/p&gt;

&lt;h3&gt;Leverage experience&lt;/h3&gt;

&lt;p&gt;Particularly when joining a new company leave your assumptions at the door. Just
because something was accepted as the right way at your previous company or
companies doesn&amp;#8217;t mean it has or will be accepted here. Similarly what you think
of as common knowledge may be completely foreign.&lt;/p&gt;

&lt;p&gt;Introduce useful tools and practices. Send around links that &amp;#8220;everyone must have
read already&amp;#8221;. At worst you&amp;#8217;re going to end up with strong agreement, at best
you can make a real positive difference to the company.&lt;/p&gt;

&lt;h3&gt;Prove yourself&lt;/h3&gt;

&lt;p&gt;The first thing you need to prove yourself to is yourself. That will give you
self-confidence which will help you win everyone else over. Focus on yourself
and everything else will sort itself out.&lt;/p&gt;

&lt;h3&gt;Be assertive&lt;/h3&gt;

&lt;p&gt;If you have an opinion and the forum is right for it, make it known. Just be
careful to not go overboard and shout down any conflicting opinions. Remember
the skill of listening.&lt;/p&gt;

&lt;h3&gt;Document everything&lt;/h3&gt;

&lt;p&gt;When developing code you have this mass of knowledge in your head about how the
code you are writing works. Document that knowledge before you forget it. The
next time you return to the code you&amp;#8217;ll read that documentation and you&amp;#8217;ll be
able to load the knowledge required to make the change much quicker.&lt;/p&gt;

&lt;p&gt;The only argument I hear against this is that stale documentation is worse than
no documentation. That&amp;#8217;s right but it&amp;#8217;s the same as badly maintained tests being
less than useful. Should I worry that test is failing or is it just out of date?&lt;/p&gt;

&lt;p&gt;Add reviews of the documentation to your code reviews. If the code changed did
the documentation change? Does the documentation still reflect the
implementation? Again, the act of someone coming in cold to review the code
helps here. If they read the documentation first it is much easier for them to
pick out inconsistencies than the person who made the change.&lt;/p&gt;

&lt;h3&gt;Preconditions&lt;/h3&gt;

&lt;p&gt;Preconditions are the simplest form of code contracts, but to me the most
effective. They can be easily implemented in any language and enforce the
concept of fail-fast. The deeper you go into a code-base the more they help
eliminate code as you can rely on assumptions that must have been verified
further up the stack.&lt;/p&gt;

&lt;p&gt;Without such enforcement you often end up with large amounts of &amp;#8220;null checking&amp;#8221;
code at the very point you want the most clarity.&lt;/p&gt;

&lt;p&gt;It also lends itself to more useful error messages. Your preconditions should
always come with descriptive messages so that when they are triggered in
development they can be resolved as quickly as possible.&lt;/p&gt;

&lt;h3&gt;Logging&lt;/h3&gt;

&lt;p&gt;Logging in its various forms is the only way you can peek into the guts of a
production system. You can&amp;#8217;t just attach a debugger or write a unit test on a
live production system. All you have are your logs.&lt;/p&gt;

&lt;p&gt;The habit use it to avoid using the debugger in development if at all possible.
The urge to hit the debugger to check the state of a variable is a smell that
I&amp;#8217;m missing a log statement. It might be that it&amp;#8217;s a debug one that will be
disabled in production but if it&amp;#8217;s there you can enable it if needed.&lt;/p&gt;

&lt;p&gt;Also, as systems become more distributed, aggregating your logs through the use
of tools such as &lt;a href="http://logstash.net/"&gt;Logstash&lt;/a&gt; is a great way to get a &amp;#8220;whole
world&amp;#8221; view. If you aren&amp;#8217;t logging then this isn&amp;#8217;t possible.&lt;/p&gt;

&lt;h3&gt;Experiment&lt;/h3&gt;

&lt;p&gt;If you&amp;#8217;re introducing or doing something for the third time, try something a
little different. The first time would have been a learning experience, the
second would have proved the first time wasn&amp;#8217;t a fluke. The third time you
should experiment a little to see if you can find a way that&amp;#8217;s even better. At
worst it will be no better and you can easily fall back to what you know, but
you could discover something that&amp;#8217;s massively better.&lt;/p&gt;

&lt;h3&gt;Stages of learning&lt;/h3&gt;

&lt;p&gt;I mentioned the &lt;a href="http://en.wikipedia.org/wiki/Dreyfus_model_of_skill_acquisition"&gt;Dreyfus model of skill acquisition&lt;/a&gt;
and my preferred moniker of &lt;a href="http://en.wikipedia.org/wiki/Shuhari"&gt;Shuhari&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Teaching other people is fun but you&amp;#8217;ve got to let them stub their toes a little
so they experience at least a little of the pain that make your best practices
understandable. The journey is as important as the destination when learning new
skills.&lt;/p&gt;

&lt;h3&gt;Goals&lt;/h3&gt;

&lt;p&gt;If there was one thing that I would highlight over all others that has made me
the developer I am today it would be goals. Ones I&amp;#8217;ve set myself, not those
given to me in my annual company review that are forgotten until two weeks
before the next one. Having these goals helped me focus my personal development
and make the day-to-day decision that compound upon one another.&lt;/p&gt;

&lt;h2&gt;Wrapping up&lt;/h2&gt;

&lt;p&gt;Those were the points covered in my talk. There&amp;#8217;s about 10 practices and 9
opinions in there.&lt;/p&gt;

&lt;p&gt;A few people mentioned my nerves and yes, I am well aware of them but I thought
I got them under control after the first 5 minutes or so. This was around my
fifth talk ever and I reckon I doubled the number of people I&amp;#8217;d ever spoke in
front of in that one hour. If you thought I was nervous in that one you should
have seen the other four. It&amp;#8217;s a scary experience but it&amp;#8217;s also something I want
to be good at so I&amp;#8217;m going to keep doing it.&lt;/p&gt;

&lt;p&gt;All feedback gratefully received. You can get in touch with me by email at
&lt;a href="garry@robustsoftware.co.uk"&gt;garry@robustsoftware.co.uk&lt;/a&gt; or on Twitter at
&lt;a href="http://twitter.com/gshutler"&gt;@gshutler&lt;/a&gt;.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=bhZCpoMLxz0:blPZXytQqgM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=bhZCpoMLxz0:blPZXytQqgM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=bhZCpoMLxz0:blPZXytQqgM:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=bhZCpoMLxz0:blPZXytQqgM:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=bhZCpoMLxz0:blPZXytQqgM:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=bhZCpoMLxz0:blPZXytQqgM:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=bhZCpoMLxz0:blPZXytQqgM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/bhZCpoMLxz0" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2012/09/ddd10/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Versioning APIs Sucks]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/UmpQwKMoFhY/" />
    <updated>2012-07-13T00:00:00+01:00</updated>
    <id>http://gshutler.com/2012/07/versioning-apis-sucks</id>
    <content type="html">&lt;blockquote&gt;&lt;p&gt;Also, versioning APIs sucks. It’s not that it’s hard, it’s that once you publish&lt;br/&gt;an API, you pretty much have to support it forever. This is especially true when&lt;br/&gt;your API is being consumed through multiple layers. I can’t force game&lt;br/&gt;developers to upgrade, because they can’t force their users to upgrade. The&lt;br/&gt;lesson here is simple, if you don’t have to make something public, don’t!&lt;/p&gt;&lt;footer&gt;&lt;strong&gt;Karl Seguin&lt;/strong&gt; &lt;cite&gt;&lt;a href='http://openmymind.net/What-I-Learned-Building-Mogade/'&gt;What I Learned Building Mogade&lt;/a&gt;&lt;/cite&gt;&lt;/footer&gt;&lt;/blockquote&gt;

&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=UmpQwKMoFhY:AsmV_qepeUI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=UmpQwKMoFhY:AsmV_qepeUI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=UmpQwKMoFhY:AsmV_qepeUI:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=UmpQwKMoFhY:AsmV_qepeUI:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=UmpQwKMoFhY:AsmV_qepeUI:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=UmpQwKMoFhY:AsmV_qepeUI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=UmpQwKMoFhY:AsmV_qepeUI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/UmpQwKMoFhY" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2012/07/versioning-apis-sucks/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Software and Schrödinger's Cat]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/6a3GsJKgfwY/" />
    <updated>2012-02-05T00:00:00+00:00</updated>
    <id>http://gshutler.com/2012/02/software-and-schrodingers-cat</id>
    <content type="html">&lt;p&gt;An interesting article exploring the relationship between quantum physics and
continuous delivery.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;None of it is real until it is in the hands of actual users. I don&amp;#8217;t mean&lt;br/&gt;someone who will poke at it a bit or evaluate it. And I don’t mean a proxy who&lt;br/&gt;will tell you if the users might like it. I mean someone who will use it for its&lt;br/&gt;intended purpose as part of their normal routine. The experience those users&lt;br/&gt;report is reality. Everything else is speculation.&lt;/p&gt;&lt;footer&gt;&lt;strong&gt;Elisabeth Hendrickson&lt;/strong&gt; &lt;cite&gt;&lt;a href='http://testobsessed.com/2012/01/what-software-has-in-common-with-schrodingers-cat/'&gt;What Software Has in Common With Schrödinger&amp;#8217;s Cat&lt;/a&gt;&lt;/cite&gt;&lt;/footer&gt;&lt;/blockquote&gt;

&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=6a3GsJKgfwY:RKmfBArUV1s:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=6a3GsJKgfwY:RKmfBArUV1s:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=6a3GsJKgfwY:RKmfBArUV1s:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=6a3GsJKgfwY:RKmfBArUV1s:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=6a3GsJKgfwY:RKmfBArUV1s:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=6a3GsJKgfwY:RKmfBArUV1s:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=6a3GsJKgfwY:RKmfBArUV1s:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/6a3GsJKgfwY" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2012/02/software-and-schrodingers-cat/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Gain Trust and Create Change - LDNUG - Followup]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/Bev3obIjtHk/" />
    <updated>2012-02-05T00:00:00+00:00</updated>
    <id>http://gshutler.com/2012/02/gain-trust-create-change-ldnug</id>
    <content type="html">&lt;p&gt;Last Monday I gave my talk “Gain Trust and Create Change” for the first time at
the &lt;a href="http://www.dnug.org.uk/"&gt;London .NET user group&lt;/a&gt;. The guys at Skills Matter
&lt;a href="http://skillsmatter.com/podcast/open-source-dot-net/ldnug-trust-change"&gt;recorded the whole thing so you can watch it online&lt;/a&gt;
if you missed it.&lt;/p&gt;

&lt;p&gt;I am reasonably pleased with how the presentation went. Watching it back was an
uncomfortable experience but is very useful to feed into the next time I give
this talk. Speaking of which, if you run a user group and would like me to give
this talk get in touch.&lt;/p&gt;

&lt;p&gt;For those of you interested in buying the two books I mentioned during the talk,
they are &lt;a href="http://pragprog.com/book/cfcar2/the-passionate-programmer"&gt;The Passionate Programmer&lt;/a&gt;
and &lt;a href="http://pragprog.com/book/trevan/driving-technical-change"&gt;Driving Technical Change&lt;/a&gt;,
both from &lt;a href="http://pragprog.com/"&gt;The Pragmatic Programmers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’d like to thank everyone who turned up, &lt;a href="https://twitter.com/icooper"&gt;Ian Cooper&lt;/a&gt;
for giving me the opportunity and Skills Matter for hosting.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=Bev3obIjtHk:aYzHUQIfnNc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=Bev3obIjtHk:aYzHUQIfnNc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=Bev3obIjtHk:aYzHUQIfnNc:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=Bev3obIjtHk:aYzHUQIfnNc:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=Bev3obIjtHk:aYzHUQIfnNc:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=Bev3obIjtHk:aYzHUQIfnNc:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=Bev3obIjtHk:aYzHUQIfnNc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/Bev3obIjtHk" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2012/02/gain-trust-create-change-ldnug/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Heroku in Europe]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/6drgoCbcYrs/" />
    <updated>2012-01-26T00:00:00+00:00</updated>
    <id>http://gshutler.com/2012/01/heroku-in-europe</id>
    <content type="html">&lt;p&gt;I &lt;a href="https://twitter.com/#!/gshutler/status/162123047673925632"&gt;tweeted yesterday&lt;/a&gt;
(25th Jan 2012) to ask if anyone knew of a solution as convenient as
&lt;a href="http://www.heroku.com/"&gt;Heroku&lt;/a&gt; but based in the UK or Europe.&lt;/p&gt;

&lt;p&gt;The reason I asked was at &lt;a href="http://uk.zopa.com/"&gt;Zopa&lt;/a&gt; we are thinking of
migrating our front-end over to a Ruby stack and Heroku was the obvious option
for hosting such a solution.  However, as 95% or more of our traffic comes from
the UK it doesn’t make complete sense to host our website outside of the UK or
Europe at worst.&lt;/p&gt;

&lt;p&gt;I got a lot of interest from Twitter as to whether I found anything but no
suggestions of similar services based this side of the pond. At the same time I
sent an email to Heroku’s support asking if they had any plans to give an UK or
European hosting option.&lt;/p&gt;

&lt;p&gt;Heroku got back to me yesterday evening and said they have no immediate plans
for server resources outside the US but they are working on Safe Harbor
certification if your concerns are of a more legal nature. They also confirmed
that they are hosted within the US East region of AWS which is better than West
for Europeans.&lt;/p&gt;

&lt;p&gt;Their suggested strategy is to use a CDN alongside Heroku so that the majority
of your assets will come from a local source and only your dynamic content will
come from the US.&lt;/p&gt;

&lt;p&gt;This probably rules out Heroku for us at Zopa as a CDN adds needless complexity
for us given our traffic volumes and the fact that almost all of our traffic is
from the same location. However, I thought I should knock a blog post together
to share what I found.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=6drgoCbcYrs:myH0033TpMw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=6drgoCbcYrs:myH0033TpMw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=6drgoCbcYrs:myH0033TpMw:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=6drgoCbcYrs:myH0033TpMw:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=6drgoCbcYrs:myH0033TpMw:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=6drgoCbcYrs:myH0033TpMw:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=6drgoCbcYrs:myH0033TpMw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/6drgoCbcYrs" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2012/01/heroku-in-europe/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[DDD North - Introduction to Backbone.js]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/l6tI7WW0HJs/" />
    <updated>2011-10-09T00:00:00+01:00</updated>
    <id>http://gshutler.com/2011/10/introduction-to-backbone-ddd-north</id>
    <content type="html">&lt;p&gt;Yesterday, I gave a presentation on &lt;a href="http://documentcloud.github.com/backbone/"&gt;Backbone.js&lt;/a&gt;
at the inaugural &lt;a href="http://www.developerdeveloperdeveloper.com/north/"&gt;DDD North&lt;/a&gt;.
My thanks go out to &lt;a href="http://www.andrewwestgarth.co.uk/blog"&gt;Andrew Westgarth&lt;/a&gt;
and his team for organising it, it was a great event.&lt;/p&gt;

&lt;p&gt;I was a bit nervous as it was my first time speaking at a conference and I think
it showed. I rattled through my presentation at break-neck speed, unfortunately
finishing well under my allocated hour. &lt;a href="http://twitter.com/nmerrigan"&gt;Niall Merrigan&lt;/a&gt;
gave me some tips for sorting that out so I’ll hopefully be more composed next
time!&lt;/p&gt;

&lt;p&gt;I’d like to thank everyone who showed up for my talk and look forward to
receiving their feedback. The slides for my presentation are now
&lt;a href="http://www.slideshare.net/gshutler/introduction-to-backbonejs-ddd-north-9617777"&gt;up on Slideshare&lt;/a&gt;
and the code I demonstrated is &lt;a href="https://github.com/gshutler/dddnorth"&gt;available on Github&lt;/a&gt;.&lt;/p&gt;

&lt;!-- more --&gt;




&lt;div style="text-align:center"&gt;
&lt;iframe src="http://www.slideshare.net/slideshow/embed_code/9617777" width="512"
height="421" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"
style="border:1px solid #CCC;border-width:1px 1px 0;margin-bottom:5px"
allowfullscreen webkitallowfullscreen mozallowfullscreen&gt; &lt;/iframe&gt;
&lt;/div&gt;



&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=l6tI7WW0HJs:wNADHScgDLk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=l6tI7WW0HJs:wNADHScgDLk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=l6tI7WW0HJs:wNADHScgDLk:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=l6tI7WW0HJs:wNADHScgDLk:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=l6tI7WW0HJs:wNADHScgDLk:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=l6tI7WW0HJs:wNADHScgDLk:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=l6tI7WW0HJs:wNADHScgDLk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/l6tI7WW0HJs" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2011/10/introduction-to-backbone-ddd-north/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Finagle]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/yp5LlZwXGIo/" />
    <updated>2011-08-25T00:00:00+01:00</updated>
    <id>http://gshutler.com/2011/08/finagle</id>
    <content type="html">&lt;p&gt;I came across &lt;a href="https://github.com/twitter/finagle"&gt;Finagle&lt;/a&gt; a few weeks ago,
it’s:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;… a protocol-agnostic, asynchronous RPC system for the JVM that makes it easy
to build robust clients and servers in Java, Scala, or any JVM-hosted
language.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;And they’ve now written &lt;a href="http://engineering.twitter.com/2011/08/finagle-protocol-agnostic-rpc-system.html"&gt;a blog post announcing it to the world&lt;/a&gt;.
I had a skim over it before but it looks like they’ve added to the documentation
since then.&lt;/p&gt;

&lt;p&gt;I’ve been looking for a framework for writing servers in Scala, the Play
framework is on my radar for web applications, and this looks like a great
contender.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=yp5LlZwXGIo:ZTV2nZMHqQo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=yp5LlZwXGIo:ZTV2nZMHqQo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=yp5LlZwXGIo:ZTV2nZMHqQo:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=yp5LlZwXGIo:ZTV2nZMHqQo:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=yp5LlZwXGIo:ZTV2nZMHqQo:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=yp5LlZwXGIo:ZTV2nZMHqQo:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=yp5LlZwXGIo:ZTV2nZMHqQo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/yp5LlZwXGIo" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2011/08/finagle/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[LMAX Architecture - Martin Fowler]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/3YT5BXFukqM/" />
    <updated>2011-07-13T00:00:00+01:00</updated>
    <id>http://gshutler.com/2011/07/lmax-architecture</id>
    <content type="html">&lt;p&gt;I remember &lt;a href="http://www.infoq.com/presentations/LMAX"&gt;watching one of the original presentations on this architecture&lt;/a&gt;
a few months ago (no idea why I didn’t link to it at the time) and
&lt;a href="http://martinfowler.com/articles/lmax.html"&gt;Martin’s article&lt;/a&gt; puts it into a
format that’s easier to consume at your leisure.&lt;/p&gt;

&lt;p&gt;A really interesting approach!&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=3YT5BXFukqM:wGKMiPDwn-c:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=3YT5BXFukqM:wGKMiPDwn-c:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=3YT5BXFukqM:wGKMiPDwn-c:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=3YT5BXFukqM:wGKMiPDwn-c:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=3YT5BXFukqM:wGKMiPDwn-c:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=3YT5BXFukqM:wGKMiPDwn-c:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=3YT5BXFukqM:wGKMiPDwn-c:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/3YT5BXFukqM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2011/07/lmax-architecture/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[The most important code isn't code]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/nArQ5TxD_Bs/" />
    <updated>2011-06-08T00:00:00+01:00</updated>
    <id>http://gshutler.com/2011/06/the-most-important-code-isnt-code</id>
    <content type="html">&lt;p&gt;I cannot stress how much I agree with this. I’ve come to
&lt;a href="http://zachholman.com/posts/documentation/"&gt;exactly the same conclusions as Zach&lt;/a&gt;
in the last year myself. I wish I had written this post myself!&lt;/p&gt;

&lt;p&gt;Here are my highlights:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;Documentation is the best way to communicate your thoughts to yourself.&lt;/p&gt;

&lt;p&gt;Forcing myself first to consider the API, the interface, and the end result
led to a clarity that inspired less code and a more impactful project.&lt;/p&gt;

&lt;p&gt;It improves the clarity of my code. I get more done in less lines.&lt;/p&gt;

&lt;p&gt;But documenting your code is cool as hell. More importantly, it makes me feel
more confident about my own code.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Go read &lt;a href="http://zachholman.com/posts/documentation/"&gt;the whole thing&lt;/a&gt; now!&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=nArQ5TxD_Bs:h1qE_WbGwpY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=nArQ5TxD_Bs:h1qE_WbGwpY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=nArQ5TxD_Bs:h1qE_WbGwpY:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=nArQ5TxD_Bs:h1qE_WbGwpY:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=nArQ5TxD_Bs:h1qE_WbGwpY:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=nArQ5TxD_Bs:h1qE_WbGwpY:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=nArQ5TxD_Bs:h1qE_WbGwpY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/nArQ5TxD_Bs" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2011/06/the-most-important-code-isnt-code/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[How to keep your software awesome]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/T5HIJ-5Eq-M/" />
    <updated>2011-06-02T00:00:00+01:00</updated>
    <id>http://gshutler.com/2011/06/how-to-keep-your-software-awesome</id>
    <content type="html">&lt;blockquote&gt;&lt;p&gt;There’s a whole school of thought that quantity of features is directly&lt;br/&gt;proportional to what you can charge for software. While clearly this is true in&lt;br/&gt;practice, that doesn’t mean that it’s not incredibly stupid. Every new feature&lt;br/&gt;makes your software more complex to use.&lt;/p&gt;&lt;footer&gt;&lt;strong&gt;Jesse Emery&lt;/strong&gt; &lt;cite&gt;&lt;a href='http://www.jemery.com/2011/06/01/how-to-keep-your-software-awesome/'&gt;How to Keep Your Software Awesome&lt;/a&gt;&lt;/cite&gt;&lt;/footer&gt;&lt;/blockquote&gt;



&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=T5HIJ-5Eq-M:llHevVv8bag:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=T5HIJ-5Eq-M:llHevVv8bag:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=T5HIJ-5Eq-M:llHevVv8bag:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=T5HIJ-5Eq-M:llHevVv8bag:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=T5HIJ-5Eq-M:llHevVv8bag:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=T5HIJ-5Eq-M:llHevVv8bag:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=T5HIJ-5Eq-M:llHevVv8bag:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/T5HIJ-5Eq-M" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2011/06/how-to-keep-your-software-awesome/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Tags are magic!]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/AaHvdH9Py1o/" />
    <updated>2011-02-09T00:00:00+00:00</updated>
    <id>http://gshutler.com/2011/02/tags-are-magic</id>
    <content type="html">&lt;p&gt;The Guardian produced a interesting series of blog posts on how they use tags to
categorise their content, display related articles and so forth:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.guardian.co.uk/info/developer-blog/2011/jan/10/tags-are-magic-1"&gt;Part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.guardian.co.uk/info/developer-blog/2011/jan/19/tags-are-magic-2"&gt;Part 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.guardian.co.uk/info/developer-blog/2011/jan/25/tags-are-magic-3"&gt;Part 3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=AaHvdH9Py1o:2U5ZwLqUAD4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=AaHvdH9Py1o:2U5ZwLqUAD4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=AaHvdH9Py1o:2U5ZwLqUAD4:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=AaHvdH9Py1o:2U5ZwLqUAD4:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=AaHvdH9Py1o:2U5ZwLqUAD4:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=AaHvdH9Py1o:2U5ZwLqUAD4:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=AaHvdH9Py1o:2U5ZwLqUAD4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/AaHvdH9Py1o" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2011/02/tags-are-magic/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Pragmatic web service design]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/r10frxWXiX4/" />
    <updated>2011-02-07T00:00:00+00:00</updated>
    <id>http://gshutler.com/2011/02/pragmatic-web-service-design</id>
    <content type="html">&lt;p&gt;Web services are a crucial part of most solutions nowadays, I spend a
significant portion of my time designing and writing them and I have read a lot
about them to make them better, faster and more resilient each time. This is a
summary of how I approach web service design and the things I bear in mind.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;h2&gt;Protocols and content types&lt;/h2&gt;

&lt;p&gt;Unless you require extreme performance from your service then use the most
compatible technologies available. Today that means HTTP, JSON and HTML forms.
The lowest common denominator in any solution is usually Javascript in the
browser. This shapes all your decisions about how to expose your service. HTTP,
JSON and HTML forms are the easiest things to work with in Javascript and they
are well supported in other languages. XML is an option but JSON is a more
efficient transport medium and much easier to work with in Javascript.&lt;/p&gt;

&lt;p&gt;Before you write a web service make sure to learn HTTP inside and out. It is a
powerful protocol that solves many more problems than most people realise. I
would recommend &lt;a href="http://oreilly.com/catalog/9780596529260"&gt;RESTful web services&lt;/a&gt;
as a starting point, it demonstrates how to create a web service that is
sympathetic to HTTP and there is a useful glossary in the back. This is not
going to be another post about REST but if you know about it already there will
be some familiar concepts.&lt;/p&gt;

&lt;p&gt;Using the correct HTTP code is important, think of it as a well established
domain-specific language for what the server thought of your request. It removes
the need to duplicate something like a response code within the body of the
response. A caveat on exploiting all that HTTP gives you is to avoid 300 errors
as browsers will redirect the whole page even when it is a response to an AJAX
request.&lt;/p&gt;

&lt;p&gt;For sending and receiving data I recommend HTML form values in, JSON out. You
may want to send requests in as JSON instead and that’s not a bad choice, I just
find HTML forms easier. However, I would recommend using the same input and
output content type for requests across your whole API whenever possible. It
makes it easier to consume an API when you do not have to think what format a
given method accepts and responds with.&lt;/p&gt;

&lt;p&gt;Avoid supporting several content types, particularly for the first couple of
releases. You will end up iterating over your service during initial development
and having to maintain compatibility for several content types will be adding
wasteful overhead at this point. Get it shipped with one pair of content types
then look to fix your bugs, harden your API and learn from real usage patterns.
Put this into the next version and repeat. Once things stabilise you can
evaluate whether support for several content types is needed based upon real
world requests without it being such a burden as your API goes through the churn
of first contact with the real world.&lt;/p&gt;

&lt;h2&gt;Core design considerations&lt;/h2&gt;

&lt;p&gt;A lot of the considerations are identical to those of &lt;a href="http://www.youtube.com/watch?v=aAb7hSCtvGw"&gt;designing any API&lt;/a&gt;
but there are some additional ones that are specific to web services as they
involve transmission over a network.&lt;/p&gt;

&lt;h3&gt;Log everything&lt;/h3&gt;

&lt;p&gt;It should be obvious but you need to log every request that comes in. You may
need to replicate an issue that is a result of several requests, without logging
this will be difficult. You want to easily answer the question “what was the
consumer doing at the time?”. Logs will also allow you to monitor usage,
response times and other useful metrics. As your first attempt at an API is no
more than an informed guess you need to be collecting metrics in order to make
an educated decision about what to do next.&lt;/p&gt;

&lt;h3&gt;Comply with the expected behaviour of HTTP&lt;/h3&gt;

&lt;p&gt;There are several expected behaviours with HTTP such as GET not producing side
effects and PUT and DELETE requests being idempotent. This all comes from
knowing HTTP as previously encouraged. Being able to justify a design decision
by referring to RFCs is awesome.&lt;/p&gt;

&lt;h3&gt;Less methods returning more data&lt;/h3&gt;

&lt;p&gt;The biggest bottleneck in communicating with a server is no longer the size of
the data being communicated, it is in establishing connections. This is
particularly true for internal services. The common bottleneck with web services
is the number of concurrent requests they can serve, not the amount of data
being transferred. You want to aim to have less methods but return more data
from them, therefore reducing the number of requests consumers need to make.
Having less methods also makes it easier for the consumer to make the right
choice.&lt;/p&gt;

&lt;p&gt;Imagine everything a consumer is likely to want to display as a result of making
the request and give it to them. The only thing you need to be careful of is
your internal implementation bleeding into your API. You do not want to expose
yourself in public, especially early on. Following this advice makes it likely
the consumer will get all the data they need in one request rather than making a
separate request for each part. This reduces the surface area of your API which
has several great benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;as a developer I have less methods to maintain&lt;/li&gt;
&lt;li&gt;as a consumer I have a lower cognitive load, I might even be able to memorise
your API&lt;/li&gt;
&lt;li&gt;as an administrator less requests helps me with caching and usually means I
can use less hardware, improving service and making scaling up cheaper&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;In terms of web service surface area and required requests, less is definitely
more.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;This may seem to contradict my recommendation of using JSON but choosing a
more efficient protocol when there is no compelling reason not to is foolish.&lt;/p&gt;&lt;/blockquote&gt;

&lt;h3&gt;Highlander principle&lt;/h3&gt;

&lt;p&gt;There should be one, and only one, way to do something. Not only does it save
you effort, it makes things easier for the consumer. Sometimes this may mean
that one action requires several requests but this is less confusing in the long
run than creating a specific method for every action. It is generally acceptable
that the solution is good enough, performing several simple steps is cognitively
easier than wading through a sea of methods to find the one intended for your
task.&lt;/p&gt;

&lt;p&gt;Again I may seem to be contradicting myself as this goes againsts the idea of
trying to reduce the number of connections required to do something and it does
to some extent. However, when you consider most systems are at least 80% reads,
that this situation usually applies to writes and that this should not be a
regularly occurring problem if you have modelled the domain correctly then it
should be a drop in the ocean of the general usage of the API.&lt;/p&gt;

&lt;p&gt;Any method you add on a hunch as to what future use will be you will have to
support for the lifetime of the API. It is better to wait until you have real
statistics and use-cases to work from than to increase the surface area of your
API speculatively. If an action requiring several requests becomes common
practice then you can add a method to simplify it and you will be certain that
it is adding value to your code base and consumers.&lt;/p&gt;

&lt;h3&gt;Give people URLs&lt;/h3&gt;

&lt;p&gt;Whenever possible provide URLs to the consumer, do not make them work them out.
Every URL a consumer has to create is a support call waiting to happen. When
someone has to create a URL it is likely your internals are bleeding into your
API. Once someone else is creating a URL you can never change that URL and that
restricts refactoring and scaling options. When you create URLs for your
consumers you can rename them and point them towards different servers to name
just two options that would be closed to you the second you are not in control
of your URLs.&lt;/p&gt;

&lt;h3&gt;Version from the outset&lt;/h3&gt;

&lt;p&gt;There will be several versions of your web service. Think about identifying the
schema of your data when it is returned, think about how you will host several
versions of the API as you cannot just switch one on and the previous version
off. It will happen and it will be difficult to lever in at a later date, make
it a solved problem.&lt;/p&gt;

&lt;h3&gt;Example - Twitter’s feed&lt;/h3&gt;

&lt;p&gt;Here’s the response for a single tweet from my timeline:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;span class='line-number'&gt;13&lt;/span&gt;
&lt;span class='line-number'&gt;14&lt;/span&gt;
&lt;span class='line-number'&gt;15&lt;/span&gt;
&lt;span class='line-number'&gt;16&lt;/span&gt;
&lt;span class='line-number'&gt;17&lt;/span&gt;
&lt;span class='line-number'&gt;18&lt;/span&gt;
&lt;span class='line-number'&gt;19&lt;/span&gt;
&lt;span class='line-number'&gt;20&lt;/span&gt;
&lt;span class='line-number'&gt;21&lt;/span&gt;
&lt;span class='line-number'&gt;22&lt;/span&gt;
&lt;span class='line-number'&gt;23&lt;/span&gt;
&lt;span class='line-number'&gt;24&lt;/span&gt;
&lt;span class='line-number'&gt;25&lt;/span&gt;
&lt;span class='line-number'&gt;26&lt;/span&gt;
&lt;span class='line-number'&gt;27&lt;/span&gt;
&lt;span class='line-number'&gt;28&lt;/span&gt;
&lt;span class='line-number'&gt;29&lt;/span&gt;
&lt;span class='line-number'&gt;30&lt;/span&gt;
&lt;span class='line-number'&gt;31&lt;/span&gt;
&lt;span class='line-number'&gt;32&lt;/span&gt;
&lt;span class='line-number'&gt;33&lt;/span&gt;
&lt;span class='line-number'&gt;34&lt;/span&gt;
&lt;span class='line-number'&gt;35&lt;/span&gt;
&lt;span class='line-number'&gt;36&lt;/span&gt;
&lt;span class='line-number'&gt;37&lt;/span&gt;
&lt;span class='line-number'&gt;38&lt;/span&gt;
&lt;span class='line-number'&gt;39&lt;/span&gt;
&lt;span class='line-number'&gt;40&lt;/span&gt;
&lt;span class='line-number'&gt;41&lt;/span&gt;
&lt;span class='line-number'&gt;42&lt;/span&gt;
&lt;span class='line-number'&gt;43&lt;/span&gt;
&lt;span class='line-number'&gt;44&lt;/span&gt;
&lt;span class='line-number'&gt;45&lt;/span&gt;
&lt;span class='line-number'&gt;46&lt;/span&gt;
&lt;span class='line-number'&gt;47&lt;/span&gt;
&lt;span class='line-number'&gt;48&lt;/span&gt;
&lt;span class='line-number'&gt;49&lt;/span&gt;
&lt;span class='line-number'&gt;50&lt;/span&gt;
&lt;span class='line-number'&gt;51&lt;/span&gt;
&lt;span class='line-number'&gt;52&lt;/span&gt;
&lt;span class='line-number'&gt;53&lt;/span&gt;
&lt;span class='line-number'&gt;54&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='line'&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;entities&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;hashtags&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="s2"&gt;&amp;quot;urls&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="s2"&gt;&amp;quot;user_mentions&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;id&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1102&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;              &lt;span class="s2"&gt;&amp;quot;id_str&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;1102&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;              &lt;span class="s2"&gt;&amp;quot;indices&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&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&gt;&lt;span class='line'&gt;                  &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;                &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;              &lt;span class="s2"&gt;&amp;quot;name&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;David Ulevitch&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;              &lt;span class="s2"&gt;&amp;quot;screen_name&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;davidu&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;favorited&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;geo&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;id&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;34612066580955136&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;id_str&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;34612066580955136&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;in_reply_to_screen_name&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;in_reply_to_status_id&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;in_reply_to_status_id_str&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;in_reply_to_user_id&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;in_reply_to_user_id_str&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;place&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;retweet_count&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;retweeted&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;retweeted_status&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="p"&gt;...,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="s2"&gt;&amp;quot;id&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;34583191385935872&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="s2"&gt;&amp;quot;id_str&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;34583191385935872&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="p"&gt;...,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="s2"&gt;&amp;quot;retweet_count&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="p"&gt;...,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="s2"&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;The last 5% of a project is always the worst half of a project. :-)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="s2"&gt;&amp;quot;truncated&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="s2"&gt;&amp;quot;user&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;contributors_enabled&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;            &lt;span class="s2"&gt;&amp;quot;created_at&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Sun Jul 16 02:30:23 +0000 2006&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;            &lt;span class="s2"&gt;&amp;quot;description&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Positively disruptive.  Started OpenDNS, ...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;            &lt;span class="p"&gt;...,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;            &lt;span class="s2"&gt;&amp;quot;id&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1102&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;            &lt;span class="s2"&gt;&amp;quot;id_str&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;1102&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;            &lt;span class="p"&gt;...,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;            &lt;span class="s2"&gt;&amp;quot;profile_background_color&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;9BE5E9&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;            &lt;span class="s2"&gt;&amp;quot;profile_background_image_url&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;http://a3.twimg.com/profile...jpg&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;            &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;          &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;source&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;web&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;RT @davidu: The last 5% of a project is always the worst half of a project. :-)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;truncated&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;user&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;contributors_enabled&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="s2"&gt;&amp;quot;created_at&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Thu May 29 20:25:50 +0000 2008&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="s2"&gt;&amp;quot;description&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Freelance software developer fond of Linux, ...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;As you can see this contains everything about the tweet, including which tweet
was being retweeted, who they were, what their profile preferences are, where
their profile image is, everything you could want really. As a consumer I don’t
need to make at least two additional request to retrieve the details for the
involed users which would lead to a N+1 load on the server.&lt;/p&gt;

&lt;p&gt;The only thing that sticks out to me as possibly being bad is the id values,
that smells of internals details leaking out. Instead they might use user_url
and instead give a URL for the user’s full profile and so forth. There also
doesn’t appear to be any mention of a version for the message but they may
handle versioning by using different URLs for different versions of the API.&lt;/p&gt;

&lt;h2&gt;Things to bear in mind during implementation&lt;/h2&gt;

&lt;h3&gt;Do not reinvent the wheel&lt;/h3&gt;

&lt;p&gt;For example, methods to achieve caching already exist which utilise the caching
mechanisms built into HTTP itself. Use these when possible rather than
reimplementing it yourself. Squid and Varnish are two open source software based
solutions that are easy to set up. Learn about the wide array of established
HTTP headers available, it is rare that you need to create a custom header.&lt;/p&gt;

&lt;h3&gt;Comprehensive documentation&lt;/h3&gt;

&lt;p&gt;Yes, even for internal use. If your API is not documented it will be hard to use
and under utilised. Without documentation you will spend a lot of time
explaining how you go about using your API when you could tell a HTML file once
and have that explain it to everyone.&lt;/p&gt;

&lt;p&gt;Use examples of performing a common task with your API on top of an example for
each method. Ideally the reader will be able to run these examples as they read.
Ask a designer to throw a template together for you, it doesn’t need to be
amazing but it will make reading your documentation a more pleasurable
experience.&lt;/p&gt;

&lt;h3&gt;No breaking changes, ever&lt;/h3&gt;

&lt;p&gt;Once you’ve published it and it is exposed to the world, you cannot change
anything, ever. Not even fix bugs as people will have written workarounds for
them. You can add methods or return more data in response but you must never
alter what has been published before. People will be relying on it and you will
fuck them up.&lt;/p&gt;

&lt;h2&gt;TL;DR&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Writing an API is very hard&lt;/li&gt;
&lt;li&gt;Really learn HTTP&lt;/li&gt;
&lt;li&gt;Remove choices and complexity by using less content types and exposing less
methods&lt;/li&gt;
&lt;li&gt;Reduce the quantity of requests by returning more data in responses&lt;/li&gt;
&lt;li&gt;Give URLs to the consumer, don’t let them create them&lt;/li&gt;
&lt;li&gt;Documentation, documentation, documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=r10frxWXiX4:9H29BKT8EzM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=r10frxWXiX4:9H29BKT8EzM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=r10frxWXiX4:9H29BKT8EzM:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=r10frxWXiX4:9H29BKT8EzM:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=r10frxWXiX4:9H29BKT8EzM:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=r10frxWXiX4:9H29BKT8EzM:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=r10frxWXiX4:9H29BKT8EzM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/r10frxWXiX4" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2011/02/pragmatic-web-service-design/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Cross-site XmlHttpRequest with CORS]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/qh3VgmLQ4WQ/" />
    <updated>2011-01-05T00:00:00+00:00</updated>
    <id>http://gshutler.com/2011/01/cross-site-ajax-with-cors</id>
    <content type="html">&lt;p&gt;I was working on a personal project over the Christmas period and wanted to make
cross-site requests from an AJAX client to an API.&lt;/p&gt;

&lt;p&gt;As the client and the API were hosted on different domains they violated the
&lt;a href="http://en.wikipedia.org/wiki/Same_origin_policy"&gt;same origin policy&lt;/a&gt;
implemented by almost all browsers. Modern browsers allow
cross-site AJAX requests if the API allows
&lt;a href="http://en.wikipedia.org/wiki/Cross-Origin_Resource_Sharing"&gt;cross-origin resource sharing (CORS)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The resources I found at the time on this were a little bare on details but I
managed to piece it together and get something working. I was planning on
writing a blog post about how to do it but now I know all the terminology I came
across &lt;a href="http://hacks.mozilla.org/2009/07/cross-site-xmlhttprequest-with-cors/"&gt;this complete article that explains CORS very well&lt;/a&gt;,
linking to several useful resources.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=qh3VgmLQ4WQ:rPZMmQRHpoI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=qh3VgmLQ4WQ:rPZMmQRHpoI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=qh3VgmLQ4WQ:rPZMmQRHpoI:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=qh3VgmLQ4WQ:rPZMmQRHpoI:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=qh3VgmLQ4WQ:rPZMmQRHpoI:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=qh3VgmLQ4WQ:rPZMmQRHpoI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=qh3VgmLQ4WQ:rPZMmQRHpoI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/qh3VgmLQ4WQ" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2011/01/cross-site-ajax-with-cors/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Why Rack should matter to .NET developers]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/srgAo_cbL5o/" />
    <updated>2010-12-07T00:00:00+00:00</updated>
    <id>http://gshutler.com/2010/12/why-rack-should-matter-to-dot-net-developers</id>
    <content type="html">&lt;p&gt;There&amp;#8217;s been a lot of talk in the .NET community about &lt;a href="http://www.sinatrarb.com/"&gt;Sinatra&lt;/a&gt;
clones, namely &lt;a href="http://nancyfx.org/"&gt;Nancy&lt;/a&gt; and &lt;a href="http://jondot.github.com/nina/"&gt;Nina&lt;/a&gt;
in recent times but there are &lt;a href="http://twitter.com/dahlbyk/status/9070232853286913"&gt;several others as well&lt;/a&gt;.
Unfortunately, all these frameworks are missing the underlying reason that
Sinatra is awesome and that is &lt;a href="http://rack.rubyforge.org/"&gt;Rack&lt;/a&gt;. Sinatra packs
a hell of a lot of functionality yet weighs in at 1198 lines of code including
documentation. The main reason for that is the fact it is building on top of
Rack which has allowed the authors to concentrate on creating a framework
without having to worry about the details of dealing with server requests. So
what is Rack, why you should care and why does it matter to you as a .NET
developer.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;h2&gt;What is Rack?&lt;/h2&gt;

&lt;p&gt;Rack is a layer of abstraction which sits between servers and frameworks. What
it brings as a result is greater interoperability between HTTP server
implementations and web frameworks. As the author of a HTTP server if you get
your server to speak Rack you can now host applications written in several Ruby
frameworks, Sinatra and &lt;a href="http://rubyonrails.org/"&gt;Rails&lt;/a&gt; being two of the most
well known. As a web framework author if you get your framework to speak Rack
your application can now be hosted on several types of HTTP server.&lt;/p&gt;

&lt;p&gt;This greatly reduces the barrier to adoption for both servers and frameworks.
Rather than the previous method of having to write an adapter for each server or
framework you wished to be compatible with; you are now able to write a single
adapter for Rack and be hosted by several types of server or host multiple
frameworks depending on which side of the boundary you are sitting.&lt;/p&gt;

&lt;p&gt;Needless to say, the Ruby community has jumped all over this. It removes the
duplication of effort that was previously required to get the same level of
interoperability allowing the Ruby community to focus on innovation. For
example, Sinatra did not exist before Rack. Rack didn’t make Sinatra possible
but it made it much easier to create. Dealing with HTTP servers had been done
for them. They could instead focus on turning requests into responses in an
elegant manner.&lt;/p&gt;

&lt;h2&gt;Why should you care?&lt;/h2&gt;

&lt;p&gt;First, lets talk about testing. Rack is entirely Ruby based at its core. That
means that when you are testing your application you can mimic a real HTTP
request being invoked and see what response your application turns that into.
Let me show you an example of that being done using RSpec against a
&lt;a href="https://github.com/gshutler/why_rack/blob/master/server.rb"&gt;simple Sinatra application&lt;/a&gt;:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/hello/:person&amp;#39;&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;person&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="s2"&gt;&amp;quot;Hello, &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;We can test how that application will respond to a HTTP request using RSpec:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;span class='line-number'&gt;13&lt;/span&gt;
&lt;span class='line-number'&gt;14&lt;/span&gt;
&lt;span class='line-number'&gt;15&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;making a hello request&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="ss"&gt;Rack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Test&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Methods&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;saying hello to world&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/hello/world&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;should respond &amp;quot;Hello, world&amp;quot;&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="n"&gt;last_response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&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="s1"&gt;&amp;#39;Hello, world&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;blockquote&gt;&lt;p&gt;I have commented &lt;a href="https://github.com/gshutler/why_rack"&gt;the full source for these examples up on Github&lt;/a&gt;
if you want further explanation of what is happening here.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;We’ve just written an integration test for our application without having to
deploy to a server or create our own abstraction of HTTP. This same type of test
will work for any application written using a Rack-based framework. Just think
of how painful the same thing is in .NET currently. Not only does Rack give us
greater interoperability between servers and frameworks, it gives us a way to
test them easily as well.&lt;/p&gt;

&lt;p&gt;Rack also has the concept of &lt;a href="https://github.com/rack/rack/wiki/List-of-Middleware"&gt;middleware&lt;/a&gt;.
These are modules built on top of Rack that can then be utilised by any
application based upon Rack. This again reduces duplication of effort within the
community. Things that can be implemented in the Rack layer, for example
&lt;a href="https://github.com/rtomayko/rack-cache"&gt;caching&lt;/a&gt;, are and that removes the need
for functional clones for each Rack-based framework that wants the same
functionality.&lt;/p&gt;

&lt;h2&gt;Why it matters to you as a .NET developer&lt;/h2&gt;

&lt;p&gt;If you’re thinking “well I don’t do integration tests, write HTTP servers or web
frameworks, why should I care?” I’ll tell you why you should. For the frameworks
you use, a .NET equivalent to Rack would eliminate a large amount of tedious and
brittle code that adds no direct value to a framework. This would allow the
developers of frameworks to focus their time on refinement giving you more
powerful and stable tools to work with. You may not use it yourself but you will
benefit from its existence within .NET.&lt;/p&gt;

&lt;p&gt;A .NET equivalent to Rack would open the door for a whole new generation of web
frameworks to be written. The barrier for writing a quality web framework would
be lowered as the nuts and bolts of request and response handling would be a
solved problem. We could see some real innovation in .NET rather than a mass of
clones of frameworks from other languages.&lt;/p&gt;

&lt;p&gt;The door would also be opened for existing servers, such as Apache, to be used
for hosting .NET applications and enable entirely new servers to be created. IIS
would have competitors. This alone makes it worthwhile. It would make the choice
of .NET as a platform less tied, if at all, to choosing Microsoft as a vendor.
Even more so than Mono does today, particularly in the web space.&lt;/p&gt;

&lt;p&gt;There is a &lt;a href="http://groups.google.com/group/net-http-abstractions"&gt;discussion going on right now about creating or choosing a single, common abstraction around HTTP, like Rack, for .NET&lt;/a&gt;.
Get involved in this discussion and contribute some of your time. This is an
opportunity to vastly improve the .NET ecosystem. We cannot afford to miss it.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=srgAo_cbL5o:FLnuqEj1HmA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=srgAo_cbL5o:FLnuqEj1HmA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=srgAo_cbL5o:FLnuqEj1HmA:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=srgAo_cbL5o:FLnuqEj1HmA:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=srgAo_cbL5o:FLnuqEj1HmA:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=srgAo_cbL5o:FLnuqEj1HmA:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=srgAo_cbL5o:FLnuqEj1HmA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/srgAo_cbL5o" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2010/12/why-rack-should-matter-to-dot-net-developers/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Use integration tests when working in a new language]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/4763E-an5MI/" />
    <updated>2010-11-19T00:00:00+00:00</updated>
    <id>http://gshutler.com/2010/11/integration-tests-new-language</id>
    <content type="html">&lt;p&gt;This is in some respects a &lt;a href="http://www.robustsoftware.co.uk/post/1609359955/simple-tools-fundamental-principles"&gt;follow up to my previous post&lt;/a&gt;
in that it has been triggered by our internal project in Ruby. When working in
Ruby I consciously lean towards integration tests. &lt;a href="http://www.sinatrarb.com/"&gt;Sinatra&lt;/a&gt;
and &lt;a href="http://sequel.rubyforge.org/"&gt;Sequel&lt;/a&gt; make this really easy as using
rack-test and running against an in-memory SQLite database it is easy to
simulate a full conversation of HTTP requests and they run reasonably quickly
too.&lt;/p&gt;

&lt;p&gt;How does this relate to learning a new language? When writing anything in a new
language you will make mistakes. You are likely to structure your application
poorly. At a minimum you will find there is a much better way of achieving the
same result. If you have based your work on unit tests you will have a
straight-jacket tying your to your code making the effort to heavily refactor it
much greater than it needs to be. Tests will need to be moved, rewritten, thrown
away and renamed. However, when you use integration tests you are only verifying
the behaviour of the whole system not its moving parts. This means you can
replace the internals of your application entirely and your integration tests
will not need to be changed and will ensure you have a system that still behaves
the same as it did before.&lt;/p&gt;

&lt;p&gt;Am I saying that you should never use unit tests in a new language? Of course
not. However, you should chose the right level of test to match your level of
understanding. Once your application’s structure has ossified it makes sense to
exercise the complex parts directly through unit tests. The second you make that
shift to using using tests you are setting your structure in stone so you want
to make sure that does not happen before it has to.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=4763E-an5MI:fm4cHf64Xks:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=4763E-an5MI:fm4cHf64Xks:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=4763E-an5MI:fm4cHf64Xks:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=4763E-an5MI:fm4cHf64Xks:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=4763E-an5MI:fm4cHf64Xks:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=4763E-an5MI:fm4cHf64Xks:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=4763E-an5MI:fm4cHf64Xks:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/4763E-an5MI" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2010/11/integration-tests-new-language/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Simple tools and fundamental principles]]></title>
    <link href="http://feedproxy.google.com/~r/RobustSoftware/~3/349rl1butN0/" />
    <updated>2010-11-18T00:00:00+00:00</updated>
    <id>http://gshutler.com/2010/11/simple-tools-fundamental-principles</id>
    <content type="html">&lt;p&gt;We are doing an experiment in using Ruby for an internal project at work. I am
the most experienced in Ruby so I’m leading the choices of gems and so forth and
the team questioning those choices has lead to a bit of a personal epiphany.&lt;/p&gt;

&lt;p&gt;I know that for a long while I have preferred simple tools and components.
Simple tools tend to be more enabling as they give you control over precisely
what is happening in your application whilst managing the boilerplate code for
you. I’ve also focused my personal development and reading on fundamental
principles to give me a solid grounding in concepts that can then be applied to
any situation or language.&lt;/p&gt;

&lt;p&gt;These opinions are being reflected in the tools I use for my Ruby development
and therefore what I am encouraging the other developers at my company to use:
&lt;a href="http://www.sinatrarb.com/"&gt;Sinatra&lt;/a&gt; and &lt;a href="http://sequel.rubyforge.org/"&gt;Sequel&lt;/a&gt;.
I enjoy the power you get from both to exploit all that is available from HTTP
and SQL. I find it much easier to apply my knowledge of HTTP gained through
reading about REST, though I doubt I’ll ever write a truly RESTful application.
Things such as returning the correct HTTP codes and headers to enable client and
proxy caching are ridiculously easy to do when compared to ASP.NET MVC. Being
able to leverage my knowledge of SQL allows me to fine tune that vital query yet
Sequel will still give me back easily consumable objects.&lt;/p&gt;

&lt;p&gt;Choosing these simple tools for our task will help the team to learn Ruby and
build an application, the real point of the task, rather than spending a large
period of time learning how to use a more powerful framework such as Rails. The
simpler tools should also let them exercise more of their existing knowledge,
again allowing them to focus on learning the Ruby language rather than being
distracted by Ruby frameworks.&lt;/p&gt;

&lt;p&gt;As for me, I’m happy I’ve chosen this method of working and learning. I think it
will stand me in good stead for working in any language I may want or need to
learn in the future. It will be interesting to hear what the team thinks of this
approach as we proceed.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=349rl1butN0:4t0Y3Enmnlw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=349rl1butN0:4t0Y3Enmnlw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=349rl1butN0:4t0Y3Enmnlw:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=349rl1butN0:4t0Y3Enmnlw:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?i=349rl1butN0:4t0Y3Enmnlw:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=349rl1butN0:4t0Y3Enmnlw:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/RobustSoftware?a=349rl1butN0:4t0Y3Enmnlw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/RobustSoftware?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/RobustSoftware/~4/349rl1butN0" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://gshutler.com/2010/11/simple-tools-fundamental-principles/</feedburner:origLink></entry>
  
</feed>
