<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
    <title>Extra Cheese</title>
    <link href="http://blog.extracheese.org/atom.xml" rel="self"/>
    <link href="http://blog.extracheese.org/"/>
    <updated>2011-04-30T13:19:15-07:00</updated>
    <id>http://blog.extracheese.org/</id>
    <author>
        <name>Gary Bernhardt</name>
        <email>gary.bernhardt@gmail.com</email>
    </author>
   
    
    <entry>
        <title>PeepCode Play by Play Screencast</title>
        <link href="http://blog.extracheese.org/2011/01/peepcode-play-by-play-screencast.html"/>
        <updated>2011-01-05T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2011/01/peepcode-play-by-play-screencast</id>
        <content type="html">&lt;p&gt;Back in July, Geoffrey Grosenbach and I got together and programmed for a few hours, recording the session as a screencast. We hacked on a particular problem using Ruby, RSpec, Vim, Git, and the Unix shell, focusing the whole time on why I did things the way I did. Geoffrey recently published that screencast &lt;a href="http://peepcode.com/products/play-by-play-bernhardt"&gt;at PeepCode&lt;/a&gt; with his usual editing magic, adding annotations and additional explanations to our programming session.&lt;/p&gt;

&lt;p&gt;Along the way, we hit many diverse topics ranging from the high level, like the four rules of simple design, all the way down to the lowest level, like why it's better to say &lt;code&gt;Vjd&lt;/code&gt; in Vim than &lt;code&gt;2dd&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://peepcode.com/products/play-by-play-bernhardt"&gt;The screencast's page&lt;/a&gt; on PeepCode has a preview video and some other preview content. The screencast itself isn't free, but it's also fifteen times the length of my average screencast and professionally edited. As always, feedback is appreciated.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Rebase Is Safe</title>
        <link href="http://blog.extracheese.org/2010/12/rebase-is-safe.html"/>
        <updated>2010-12-09T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2010/12/rebase-is-safe</id>
        <content type="html">&lt;p&gt;A falsehood about Git is spreading: that &lt;code&gt;git rebase&lt;/code&gt; isn't safe. Not the kind of unsafe where you rebase pushed changes and everyone gets a nasty merge bubble. That's a real, well-known danger, and it's accepted that you just don't do it.&lt;/p&gt;

&lt;p&gt;This falsehood is that &lt;code&gt;git rebase&lt;/code&gt; inherently destablizes your history, potentially introducing changesets that don't compile or don't pass the tests, and that this is a serious problem. The main consequence is that &lt;code&gt;git bisect&lt;/code&gt; stops working: if you have revisions with broken tests, your bisect will skip them if you're very careful, or give you false positives if you're not.&lt;/p&gt;

&lt;p&gt;That certainly sounds bad. But if the problem is that you don't know whether the tests pass for the newly rebased commits, why not run the tests? Like this, for example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(set -e;
  git rev-list --reverse origin/master..master |
    while read rev; do
      echo "Checking out: $(git log --oneline -1 $rev)";
      git checkout -q $rev;
      python runtests.py;
    done)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This isn't hard&amp;mdash;it's just a while loop at the shell. It checks out every revision between origin/master and master, running the tests for each revision. Running it on a couple revisions of &lt;a href="https://github.com/garybernhardt/expecter"&gt;Expecter Gadget&lt;/a&gt; gives this output:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Checking out: 4f56581 Split tests into many files
....................
-------------------------------------------------
Ran 20 tests in 0.019s

OK
Checking out: 44de2c2 pyflakes
....................
-------------------------------------------------
Ran 20 tests in 0.019s

OK
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This only took 1.1 seconds. If it's slow, that's because your unit tests are slow (not Git's fault). Of course, test slowness will also slow down your bisects, your CI, your inner-loop development workflow, and lots of other stuff. Rebase isn't the bottleneck there.&lt;/p&gt;

&lt;p&gt;The command will stop whenever a test fails&amp;mdash;that's what the &lt;code&gt;set -e&lt;/code&gt; at the top is for. So, if any revision doesn't pass the tests, you'll see the test output, the command will stop, and you'll be left with that revision checked out. You'll probably want to &lt;code&gt;git checkout master&lt;/code&gt; and do an interactive rebase to fix that revision. This rarely happens to me in practice; the rebases almost always work without modification, but checking is still important because you really don't want to break the history.&lt;/p&gt;

&lt;p&gt;You might worry that the checkouts will smash stuff in the working copy or otherwise cause chaos. Again, &lt;code&gt;set -e&lt;/code&gt; saves us. If the working copy is dirty, the first &lt;code&gt;git checkout&lt;/code&gt; will fail and the subshell will exit:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Checking out: 4f56581 Split tests into many files
error: You have local changes [...]; cannot switch branches.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I've used this on multiple projects with great success, both by myself and on small teams of around six people. It originally came from a similar command for running tests over all revisions in a Mercurial patch queue, which is just another way to rebase commits, lest the Mercurial users think that this issue is Git-specific.&lt;/p&gt;

&lt;p&gt;In the past, I haven't even bothered turning the command into a script. I have a huge Zsh history size (100,000 commands) and would just hit &lt;code&gt;^R&lt;/code&gt; to do a reverse history search for "rev-list". However, I've just &lt;a href="https://github.com/garybernhardt/dotfiles/blob/master/bin/run-command-on-git-revisions"&gt;scriptified&lt;/a&gt; the command in my dotfiles repository, so you can use it easily in your own projects.&lt;/p&gt;

&lt;p&gt;The lesson here is that you need to engage deeply with your tools: understand Git, but also understand Unix. Learn how to use them well (for Unix examples, see my screencasts "&lt;a href="/2010/06/screencast-python-to-shell-to-ruby.html"&gt;Python to Shell to Ruby&lt;/a&gt;" and "&lt;a href="/2010/04/a-raw-view-into-my-unix-hackery.html"&gt;A Raw View into My Unix Hackery&lt;/a&gt;"), and learn the fundamental models of both (for a Unix example, see "&lt;a href="/2010/05/the-tar-pipe.html"&gt;The Tar Pipe&lt;/a&gt;"), and especially learn how to use them together. Don't learn only one, or learn them both halfway, and then blame the tools when you have problems. Programming is hard, but let's not go shopping.&lt;/p&gt;

&lt;p&gt;(If you enjoyed this, you may also like "&lt;a href="/2010/05/why-i-switched-to-git-from-mercurial.html"&gt;Why I Switched to Git From Mercurial&lt;/a&gt;".)&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>An Aside About Shell Conditionals</title>
        <link href="http://blog.extracheese.org/2010/11/an-aside-about-shell-conditionals.html"/>
        <updated>2010-11-21T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2010/11/an-aside-about-shell-conditionals</id>
        <content type="html">&lt;p&gt;The &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; shell operator is used in two common contexts. First, to chain commands like &lt;code&gt;;&lt;/code&gt; does, but stop on failure. For example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ mkdir foo &amp;amp;&amp;amp; cd foo &amp;amp;&amp;amp; echo 'done!' &amp;amp;&amp;amp; cd ..
done!
$ mkdir foo &amp;amp;&amp;amp; cd foo &amp;amp;&amp;amp; echo 'done!' &amp;amp;&amp;amp; cd ..
mkdir: foo: File exists
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The directory existed on the second try, so &lt;code&gt;mkdir&lt;/code&gt; set &lt;code&gt;$?&lt;/code&gt;, the error code, to 1. An error code of 0 is success; non-zero is failure. The &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; operator saw that &lt;code&gt;$?&lt;/code&gt; was non-zero, meaning that the &lt;code&gt;mkdir&lt;/code&gt; failed, so it exited.&lt;/p&gt;

&lt;p&gt;The second common application of &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; is within comparisons: you can use it in conditionals as a logical "and" operator. It acts as you'd expect it to act in any other programming language:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ if [ 1 = 1 ] &amp;amp;&amp;amp; true; then echo ok; fi  
ok
$ if [ 1 = 100 ] &amp;amp;&amp;amp; true; then echo ok; fi
$ if [ 1 = 1 ] &amp;amp;&amp;amp; false; then echo ok; fi
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Above, we saw that the &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; operator continues executing only if the previous command sets &lt;code&gt;$?&lt;/code&gt; to 0, the success value. Which means...&lt;/p&gt;

&lt;p&gt;In shell conditionals, the true things are 0 and the false things are 1! I know &amp;ndash; it's Wrong, but it also makes everything Just Work.&lt;/p&gt;

&lt;p&gt;Oh... and one more thing. &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;false&lt;/code&gt;, and &lt;code&gt;[&lt;/code&gt; are all programs:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ which true false [
/usr/bin/true
/usr/bin/false
/bin/[
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Noodle on &lt;em&gt;that&lt;/em&gt; for a while! Which program knows what &lt;code&gt;=&lt;/code&gt; means? Why is the &lt;code&gt;]&lt;/code&gt; there when using a conditional at the shell? (Isn't it just a meaningless argument passed to &lt;code&gt;[&lt;/code&gt;?)&lt;/p&gt;

&lt;p&gt;(Seriously &amp;ndash; think about these questions before proceeding.)&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;I'm not going to answer them, but I will show you this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ /bin/[ 1 = 1; echo $?
0
$ /bin/[ 1 = 2; echo $?
1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(This post started as an aside in &lt;a href="/2010/05/the-tar-pipe.html"&gt;The Tar Pipe&lt;/a&gt; that didn't make the cut. See that post for more Unix bits.)&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Screencast: Custom Vim Refactorings</title>
        <link href="http://blog.extracheese.org/2010/11/screencast-custom-vim-refactorings.html"/>
        <updated>2010-11-19T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2010/11/screencast-custom-vim-refactorings</id>
        <content type="html">&lt;p&gt;This lightning talk was given at &lt;a href="http://edendevelopment.co.uk/"&gt;Eden Development&lt;/a&gt; in Winchester, UK. I create an automated "inline variable" refactoring from scratch in Vim by recording interactive commands, then working them into a mostly-intelligible function. It shows off some of the deeper power of Vim: commands are text and text is commands. This parallels the Lisp philosophy of "code is data and data is code".&lt;/p&gt;

&lt;p&gt;&lt;object width="400" height="300"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=17007435&amp;amp;server=vimeo.com&amp;amp;show_title=0&amp;amp;show_byline=0&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1&amp;amp;autoplay=0&amp;amp;loop=0" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=17007435&amp;amp;server=vimeo.com&amp;amp;show_title=0&amp;amp;show_byline=0&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1&amp;amp;autoplay=0&amp;amp;loop=0" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="300"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Screencast: Python to Shell to Ruby</title>
        <link href="http://blog.extracheese.org/2010/06/screencast-python-to-shell-to-ruby.html"/>
        <updated>2010-06-30T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2010/06/screencast-python-to-shell-to-ruby</id>
        <content type="html">&lt;p&gt;This is a quick screencast where I do some Unix shell hacking. The problem: I needed to get my Google Reader subscription list into a Rails app's database. The first solution that came to mind was to parse the Google OPML file with Python, then use shell scripting to generate Ruby code using the raw Python data structure.&lt;/p&gt;

&lt;p&gt;The whole thing is less than seven minutes long and spans three languages. I also find a brand new bug in the terminal. Software is frustrating!&lt;/p&gt;

&lt;p&gt;&lt;object width="400" height="225"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=12998998&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=12998998&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="225"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;p&gt;&lt;a href="http://vimeo.com/12998998"&gt;Screencast: Python to Shell to Ruby&lt;/a&gt; from &lt;a href="http://vimeo.com/garybernhardt"&gt;Gary Bernhardt&lt;/a&gt; on &lt;a href="http://vimeo.com"&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;&lt;/p&gt;

&lt;p&gt;(If you enjoyed this, you might enjoy (&lt;a href="/2010/04/a-raw-view-into-my-unix-hackery.html"&gt;"A Raw View into My Unix Hackery&lt;/a&gt;" and my "&lt;a href="/2010/01/string-calculator-kata-in-python.html"&gt;String Calculator Kata in Python&lt;/a&gt;".)&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Specificity, Faking It, and Making It</title>
        <link href="http://blog.extracheese.org/2010/05/specificity-faking-it-and-making-it.html"/>
        <updated>2010-05-31T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2010/05/specificity-faking-it-and-making-it</id>
        <content type="html">&lt;p&gt;I recently &lt;a href="http://twitter.com/garybernhardt/status/12185737727"&gt;asked&lt;/a&gt; this question on Twitter:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;When I've faked it, but not yet made it, is it legit to push and pull specificity around?&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Clearly, this makes sense to almost no one. In the spirit of &lt;a href="/2010/05/the-tar-pipe.html"&gt;The Tar Pipe&lt;/a&gt;, I present a step-by-step explanation of what this question means:&lt;/p&gt;

&lt;h3&gt;Faking It and Making It&lt;/h3&gt;

&lt;p&gt;Faking it is when, during &lt;a href="http://blog.extracheese.org/2009/11/how_i_started_tdd.html"&gt;TDD&lt;/a&gt;, you write something stupid to make a test pass. When you write the first fibonacci sequence test, for &lt;code&gt;fib(0) == 0&lt;/code&gt;, the body of your &lt;code&gt;fib&lt;/code&gt; function will say &lt;code&gt;return 0&lt;/code&gt;. That's faking it.&lt;/p&gt;

&lt;p&gt;You continue to write tests for functionality, then fake it to make them pass. But after each red-green cycle comes the required refactoring stage. As soon as your tests pass, you &lt;em&gt;must&lt;/em&gt; remove any duplication. Don't blame me for this; it's one of the &lt;a href="http://www.jbrains.ca/permalink/276"&gt;four elements of simple design&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Often, removing duplication means combining two special cases into a general case. For example, two special-cased checks for &lt;code&gt;fib(0) == 0&lt;/code&gt; and &lt;code&gt;fib(1) == 1&lt;/code&gt; can be collapsed into a single statement that passes both tests: &lt;code&gt;return n&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After enough write-test-then-fake-it-then-refactor iterations, the class will arrive at a true implementation of its desired behavior. That's making it: there's no longer any faking. Or, in some cases, what looked like faking turned out to be the basis case of the class's behavior. In either case, the behavior has been made.&lt;/p&gt;

&lt;h3&gt;Defining Specificity&lt;/h3&gt;

&lt;p&gt;Details about how a class does its job are specificity. For example, rule 1 in &lt;a href="http://en.wikipedia.org/wiki/Conway's_game_of_life"&gt;Conway's game of life&lt;/a&gt; is "any live cell with fewer than two live neighbors dies, as if caused by underpopulation." You could write your game of life evolution function with that in mind:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;evolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cells&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;neighborhood&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;live_cell_count&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;die&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;That's some serious specificity, though: the evolution algorithm gets very specific about the rules of evolution. A lower-specificity version of the function wouldn't talk about numbers at all:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;evolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cells&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cell_should_die&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;die&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The responsibility of knowing the specific rules has been moved out into a &lt;code&gt;rules&lt;/code&gt; object, allowing this function to focus on its core responsibility of applying the rules to the world.&lt;/p&gt;

&lt;p&gt;However, there's still more specificity that can be pulled out. Right now, the function asks the rules whether each cell should live, then conditionally tells the cell to die based on the answer. It would be better to tell the rules engine to do its thing directly, rather than asking:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;evolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cells&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The specificity about the relationship between rules and cells has been removed, making the function more abstract. Equivalently, the &lt;a href="http://en.wikipedia.org/wiki/Fan-out"&gt;fan-out&lt;/a&gt; of &lt;code&gt;evolve&lt;/code&gt; has decreased by one. Or, the coupling between &lt;code&gt;evolve&lt;/code&gt; and &lt;code&gt;rules&lt;/code&gt; has decreased from data coupling to message coupling (message coupling is weaker). Or, mumble mumble &lt;a href="http://mwrc2009.confreaks.com/14-mar-2009-18-10-the-building-blocks-of-modularity-jim-weirich.html"&gt;connascence&lt;/a&gt; mumble.&lt;/p&gt;

&lt;h3&gt;Pushing and Pulling Specificity&lt;/h3&gt;

&lt;p&gt;During &lt;a href="http://www.coderetreat.com/"&gt;code retreats&lt;/a&gt;, &lt;a href="http://www.coreyhaines.com/"&gt;Corey Haines&lt;/a&gt; likes to challenge attendees to move specificity out into the test. This is what he means: move low-level details out of your system and into your test, making the system more abstract.&lt;/p&gt;

&lt;p&gt;In our example above, we moved specificity about how cells live and die out of the production code. Depending on how we made that change, that specificity might continue to exist, but get moved into the test code instead. For example, in Ruby, the test code could pass a block that implements the actual steps. Ultimately, those details will go in a lower-level class that the current one depends on.&lt;/p&gt;

&lt;p&gt;Pushing and pulling specificity is about moving implementation details around. Many people, when told about &lt;a href="/2009/10/my-personal-failures-in-test-isolation.html"&gt;isolated unit testing&lt;/a&gt;, complain that it will lead to rigid, unchangeable definitions of the implementation. That's not the case, and this is one of the reasons &amp;ndash; a TDDer practicing isolationism will learn how to move specificity out of necessity.&lt;/p&gt;

&lt;p&gt;(I've written and deleted several paragraphs about the differences between pushing and pulling specificity, but haven't come up with anything I'm comfortable saying in public. You'll have to come up with your own ideas about the important differences, if any, between pushing and pulling.)&lt;/p&gt;

&lt;h3&gt;The Question at Hand&lt;/h3&gt;

&lt;p&gt;Now, we can get back to my original question:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;When I've faked it, but not yet made it, is it legit to push and pull specificity around?&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;In other words, when I've not yet TDDed the full system &amp;ndash; when it still contains faked-out behavior &amp;ndash; is it safe, desirable, morally acceptable, etc., etc. to push and pull specificity to and from other classes?&lt;/p&gt;

&lt;p&gt;Well, probably. But that's really not the point, is it?&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>The Tar Pipe</title>
        <link href="http://blog.extracheese.org/2010/05/the-tar-pipe.html"/>
        <updated>2010-05-17T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2010/05/the-tar-pipe</id>
        <content type="html">&lt;p&gt;This is a tar pipe:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;src &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; tar -cf - .&lt;span class="o"&gt;)&lt;/span&gt; | &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;dest &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; tar -xpf -&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;It basically means "copy the &lt;code&gt;src&lt;/code&gt; directory to &lt;code&gt;dst&lt;/code&gt;, preserving permissions and other special stuff." It does this by firing up two tars &amp;ndash; one tarring up &lt;code&gt;src&lt;/code&gt;, the other untarring to &lt;code&gt;dst&lt;/code&gt;, and wiring them together.&lt;/p&gt;

&lt;p&gt;You can learn a whole lot about Unix from that one little command.&lt;/p&gt;

&lt;h3&gt;The Subshell&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;(&lt;/code&gt; starts a subshell. This is actually spawning a process using &lt;code&gt;fork(2)&lt;/code&gt; &amp;ndash; everything inside the parentheses is in a separate instance of bash. The subshell, by virtue of being a separate process, is a natural namespacing mechanism: the parent bash won't see the child's changes to variables and &amp;ndash; important for our case &amp;ndash; the working directory. This is why we used a subshell: it isolates the &lt;code&gt;cd&lt;/code&gt; to only the subshell doing the tar.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;cd&lt;/code&gt; changes the subshell process's working directory to &lt;code&gt;src&lt;/code&gt;. After that comes &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;. Logically, this means "do the next thing only if the previous one worked; otherwise, fail." Under the hood, it's testing &lt;code&gt;$?&lt;/code&gt;, the previous command's exit code variable, and failing if it's not 0. The weird thing about &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; is that it means "only continue if the return code was 0", which is the opposite of what you'd expect from a real programming language. But Unix commands (and most C functions, for that matter) return 0 on success, so it all works out.&lt;/p&gt;

&lt;h3&gt;The Tar&lt;/h3&gt;

&lt;p&gt;Now that we've &lt;code&gt;cd&lt;/code&gt;ed to &lt;code&gt;src&lt;/code&gt;, we start a tar. &lt;code&gt;-c&lt;/code&gt; means "create a tarball", as opposed to extracting or listing. &lt;code&gt;-f&lt;/code&gt; tells tar what file to create, and it's getting an argument of &lt;code&gt;-&lt;/code&gt;, which is the Unix convention for &lt;code&gt;stdout&lt;/code&gt;. The final argument, &lt;code&gt;.&lt;/code&gt;, means "the current directory", so the whole command together means "tar up the current directory and dump the tar data to &lt;code&gt;stdout&lt;/code&gt;."&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Stdout&lt;/code&gt; is one of three special file descriptors in Unix: &lt;code&gt;stdin&lt;/code&gt;, &lt;code&gt;stdout&lt;/code&gt;, and &lt;code&gt;stderr&lt;/code&gt;. At a terminal, the keys you type are going into the shell's &lt;code&gt;stdin&lt;/code&gt;, and the output it shows you is coming from &lt;code&gt;stdout&lt;/code&gt;. &lt;code&gt;Stderr&lt;/code&gt; is basically another &lt;code&gt;stdout&lt;/code&gt;, but used for errors. If our tar command failed, the error messages would show up on our terminal via &lt;code&gt;stderr&lt;/code&gt; even though the &lt;code&gt;stdout&lt;/code&gt; is being piped away.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;stdin&lt;/code&gt;, &lt;code&gt;stdout&lt;/code&gt;, and &lt;code&gt;stderr&lt;/code&gt; are files 0, 1, and 2. Always. This is why you append &lt;code&gt;2&amp;gt;&amp;amp;1&lt;/code&gt; to a command to say "combine the &lt;code&gt;stdout&lt;/code&gt; and &lt;code&gt;stderr&lt;/code&gt; streams": it means "send &lt;code&gt;stderr&lt;/code&gt; (descriptor 2) to &lt;code&gt;stdout&lt;/code&gt; (descriptor 1)".&lt;/p&gt;

&lt;h3&gt;The Pipe&lt;/h3&gt;

&lt;p&gt;The original command contained two subshells. We'll get to the second later, but what we care about now is that they're joined with a &lt;code&gt;|&lt;/code&gt;. This makes bash create a pipe using &lt;code&gt;pipe(2)&lt;/code&gt;. A pipe is a unidirectional file-like object: it has a reading end and a writing end.&lt;/p&gt;

&lt;p&gt;Bash &lt;code&gt;fork(2)&lt;/code&gt;s, making a copy of its own running process. Through a series of changes, this newly forked bash process will become the tar process that will feed the pipe.&lt;/p&gt;

&lt;p&gt;When piping commands together, the &lt;code&gt;stdin&lt;/code&gt; and &lt;code&gt;stdout&lt;/code&gt; file descriptors are used, but they write to and read from the pipe instead of the terminal. Bash uses &lt;code&gt;dup2(2)&lt;/code&gt; to duplicate the writing end of the pipe to &lt;code&gt;stdout&lt;/code&gt;. This means that any data the newly-forked process writes will go into the pipe. Under the covers, &lt;code&gt;dup2(2)&lt;/code&gt; is saying "forget about what used to be at file descriptor 1; make this other file descriptor the new 1."&lt;/p&gt;

&lt;p&gt;At this point the process is &lt;em&gt;still&lt;/em&gt; bash. It has to &lt;code&gt;exec(3)&lt;/code&gt; the tar binary before it's ready to do real work. This replaces the running bash process with a copy of tar, but doesn't close the file descriptors. There's now a running tar process with its &lt;code&gt;stdout&lt;/code&gt; glued to the writing end of the pipe.&lt;/p&gt;

&lt;h3&gt;Moving Data&lt;/h3&gt;

&lt;p&gt;After bash &lt;code&gt;exec(3)s&lt;/code&gt; tar, the tar process looks at its arguments and sees that it's supposed to be tarring up the current directory (which is &lt;code&gt;src&lt;/code&gt; because the subshell it came from &lt;code&gt;cd&lt;/code&gt;ed to it), and that it's supposed to emit the tarred data on &lt;code&gt;stdout&lt;/code&gt; (which is the writing end of the pipe that bash set up). It starts reading files, generating tar data, and spewing it to &lt;code&gt;stdout&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But wait &amp;ndash; the reading tar isn't even there yet! Doesn't matter. Both sides of the pipe existed from the time it was created, and pipes are buffered. The writing tar will be allowed to shove data into the pipe until it's full. Eventually, when the pipe is full, the &lt;code&gt;write(2)&lt;/code&gt; will block. The kernel's CPU scheduler will kick in, notice that bash is waiting for the CPU, and context switch to it.&lt;/p&gt;

&lt;h3&gt;The Reading&lt;/h3&gt;

&lt;p&gt;Bash starts to execute the other side of the pipe. It &lt;code&gt;cd&lt;/code&gt;s into the &lt;code&gt;dest&lt;/code&gt; directory and starts a separate tar process with &lt;code&gt;-xpf -&lt;/code&gt;. &lt;code&gt;-x&lt;/code&gt; means "extract", &lt;code&gt;-p&lt;/code&gt; means "preserve permissions" &amp;ndash; usually a good thing &amp;ndash; and &lt;code&gt;-f -&lt;/code&gt; now means "read from &lt;code&gt;stdin&lt;/code&gt;". You know, I never really thought about that until just now: sometimes &lt;code&gt;-&lt;/code&gt; means &lt;code&gt;stdin&lt;/code&gt;; sometimes it means &lt;code&gt;stdout&lt;/code&gt;. It was so natural that I never considered it. Anyway...&lt;/p&gt;

&lt;p&gt;The second tar process is started in basically the same way as the first. It &lt;code&gt;fork(2)&lt;/code&gt;s, sets its &lt;code&gt;stdin&lt;/code&gt; to the reading side of the pipe (using the &lt;code&gt;dup2(2)&lt;/code&gt; trick again), and execs tar. Both ends of the pipe are now connected. The writing tar has its &lt;code&gt;stdout&lt;/code&gt; hooked to the writing end of the pipe, and the reading tar has its &lt;code&gt;stdin&lt;/code&gt; hooked to the reading end.&lt;/p&gt;

&lt;p&gt;The parent bash process executes &lt;code&gt;wait(2)&lt;/code&gt;s on the subshell bash processes, which will block until the subprocesses finish. The subshell bash processes also execute &lt;code&gt;wait(2)&lt;/code&gt;s on their forked tar processes. Because the bashes are all blocked, a context switch happens and the newly-spawned reading tar process gets the CPU.&lt;/p&gt;

&lt;p&gt;The reading tar process, being freshly forked and execed, starts up, processes its arguments, and sees that it's supposed to read from &lt;code&gt;stdin&lt;/code&gt; (which is the pipe, not that it cares). The blob of data that the writing tar wrote into the pipe's buffer is sitting there, so the reading tar pulls it out and starts to decode it. There may be enough data that it can actually reconstruct a file or two. But pretty soon, it's going to exhaust the buffer.&lt;/p&gt;

&lt;h3&gt;The Context Switch&lt;/h3&gt;

&lt;p&gt;The reading tar will never know when the pipe's buffer is empty; it just keeps calling &lt;code&gt;read(2)&lt;/code&gt;. At the beginning, &lt;code&gt;read(2)&lt;/code&gt; will keep returning the data that the writer wrote. Eventually, it'll empty the pipe's buffer and the &lt;code&gt;read(2)&lt;/code&gt; will block. The kernel's scheduler will kick in again and switch back to the writer. It gets woken up, the &lt;code&gt;write(2)&lt;/code&gt; call that was blocked completes, and the writer continues filling the pipe until it's full again.&lt;/p&gt;

&lt;p&gt;This repeats again and again: the writing tar writes until the pipe is full, the reading tar reads until it's empty, on and on.&lt;/p&gt;

&lt;h3&gt;The Exhausted Pipe&lt;/h3&gt;

&lt;p&gt;Eventually, the writing tar will finish tarring up everything and sending it over the pipe. When this happens, it'll clean up and exit. Exiting implicitly closes its &lt;code&gt;stdout&lt;/code&gt;, which means the writing end of the pipe closes. The reading tar, who's blocking on the empty pipe, sees its call to &lt;code&gt;read(2)&lt;/code&gt; return 0, which means it's reached the end of the file.&lt;/p&gt;

&lt;p&gt;Since the tar stream has ended, the reader cleans up and exits as well. The subshell processes exit because they've finished their commands. Finally, the parent bash process's two &lt;code&gt;wait(2)&lt;/code&gt; calls return. The prompt comes back. From beginning to end, about 10ms have elapsed.&lt;/p&gt;

&lt;p&gt;And that's Unix!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Notes&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I don't know how Bash actually implements any of this; I've just made up a conveniently simple implementation. The same goes for tar.&lt;/li&gt;
&lt;li&gt;This is all from memory; the only things I looked up were man page numbers. Caveat emptor.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;-f&lt;/code&gt; arguments to tar aren't actually needed, but are illustrative.&lt;/li&gt;
&lt;li&gt;I assume the tar file format doesn't indicate when the file ends, so the reader must wait for &lt;code&gt;read(2)&lt;/code&gt; to return 0. I doubt this is actually the case.&lt;/li&gt;
&lt;li&gt;I drastically simplify the CPU scheduling, process management, and execution order, approximating them for simplicity.&lt;/li&gt;
&lt;li&gt;Tar pipes are mostly obsolete, but far too awesome to be forgotten!&lt;/li&gt;
&lt;/ul&gt;

</content>
    </entry>
    
    <entry>
        <title>Why I Switched to Git From Mercurial</title>
        <link href="http://blog.extracheese.org/2010/05/why-i-switched-to-git-from-mercurial.html"/>
        <updated>2010-05-15T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2010/05/why-i-switched-to-git-from-mercurial</id>
        <content type="html">&lt;p&gt;I used Mercurial for three years, but started switching to Git about a year ago. I now grudgingly recommend Git to anyone who intends to be a full-time programmer. Git's interface is bad in many ways, which is the main complaint about it, and it's a legitimate one. It's just an interface, though, and this is a tool you're going to use all day, every day, in a wide variety of situations.&lt;/p&gt;

&lt;p&gt;Here are all of the ways that Mercurial has harmed me, or that I've seen it harm others, and the ways in which Git does good where Mercurial does evil:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One&lt;/strong&gt;: Mercurial is bad at handling large amounts of data. A friend accidentally committed a couple GB of data into a Mercurial repository. It became completely broken, to the point where most commands would die because they ran out of memory. Git has no problem with large data. It's awesome to be able to put, say, an entire home directory or ports install under version control without fear. (I recently put a multi-gigabyte Mac Ports install under version control with Git without even thinking about it.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two&lt;/strong&gt;: Mercurial's repository model is clunky and stays hidden in the background (this is a bad thing; don't let anyone tell you otherwise). If you have a Mercurial repository whose size is dominated by a single, 20 MB directory, and you then rename that directory, your repository just doubled to 40 MB. This has limited my ability to manage real-life Mercurial repositories. Git's repository model is so good that I only hesitate slightly when calling it perfect. It allows me to think about what's going on in the repository with an ease that I never had with Mercurial, despite using it much more than Git.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three&lt;/strong&gt;: Mercurial is not safe. Both systems ship with many commands that change history, but Git's data model is such that even a "delete" isn't really a delete. Destructive commands just create new nodes in the history graph, then adjust the branch to point at them. Whenever this happens, the old branch HEAD is still accessible using the reflog. That's awesome, and it alone would bring me to Git.&lt;/p&gt;

&lt;p&gt;Mercurial's answer to this is weak: destructive commands shove a bundle file into a subdirectory of the Mercurial repository; you have to manually manipulate it if you want to get the data back. Except some of the destructive commands don't dump the bundle files, which has made me &lt;a href="http://twitter.com/garybernhardt/status/9368728335"&gt;lose actual data&lt;/a&gt; in the past. Even for the commands that do dump the files, keeping track of them, and which applies where, becomes difficult fast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tl;dr&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mercurial made my repositories huge for no reason.&lt;/li&gt;
&lt;li&gt;Mercurial broke when my friend put lots of data in it.&lt;/li&gt;
&lt;li&gt;Mercurial lost my data when I did a destructive command.&lt;/li&gt;
&lt;li&gt;In a year of Git, it's never done anything nearly as bad.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I'm sorry for recommending software with a confusing interface. But you'll be spending a lot of time with it; it's worth getting over the initial hurdle of confusion.&lt;/p&gt;

&lt;p&gt;...until something better comes along, of course.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>My Startup Talk From NWPD 2010</title>
        <link href="http://blog.extracheese.org/2010/05/talk-video-a-brief-history-of-bitbacker-a-startup.html"/>
        <updated>2010-05-06T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2010/05/talk-video-a-brief-history-of-bitbacker-a-startup</id>
        <content type="html">&lt;p&gt;A few months ago, I posted one of my talks from Northwest Python Day 2010: &lt;a href="/2010/02/python-vs-ruby-a-battle-to-the-death.html"&gt;Python vs. Ruby: A Battle to The Death&lt;/a&gt;. This is the other talk I gave, "A Brief History of BitBacker, A Startup", which shares its name with a &lt;a href="/2009/07/a-brief-history-of-bitbacker-a-startup.html"&gt;blog post&lt;/a&gt; from 2009. This new talk is mostly about the technical details, whereas the original blog post was a higher-level chronology about what we did from founding in 2006 until the doors closed in 2009.&lt;/p&gt;

&lt;p&gt;Like the Python vs. Ruby talk, this one hits many different topics: a lot of testing (of course), a smattering of technical lessons learned, and a couple of business lessons. There's one very notable artifact in the talk: I show BitBacker's 510 unit tests executing in about a second. I also show one of the acceptance tests executing &amp;ndash; firing up the full suite of back-end components as well as the app itself, then driving the whole system programmatically. I've talked about these things to various people, but never shown them in public. For this talk, I went through the trouble of recording videos of BitBacker's tests in action, so now you know that I'm not making things up!&lt;/p&gt;

&lt;p&gt;&lt;object width="400" height="225"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=11486084&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=11486084&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="225"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;p&gt;&lt;a href="http://vimeo.com/11486084"&gt;A Brief History of BitBacker, A Startup&lt;/a&gt; from &lt;a href="http://vimeo.com/garybernhardt"&gt;Gary Bernhardt&lt;/a&gt; on &lt;a href="http://vimeo.com"&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;&lt;/p&gt;

&lt;p&gt;Here are a few clarifications on things that I say in the talk:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://bitbucket.org/garybernhardt/dingus"&gt;Dingus&lt;/a&gt; is a test double library for Python. Technically, dinguses are both stubs and test spies. Many people would call it a mocking library, but that's not quite correct.&lt;/li&gt;
&lt;li&gt;London-style TDD is a style that uses test doubles extensively, with mocks or spies verifying interactions. Classes are generally TDDed in isolation, interacting only with test doubles &amp;ndash; never real collaborators with their own behavior. It sounds crazy; it's not.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.coreyhaines.com"&gt;Corey Haines&lt;/a&gt; gave me much of my early coaching on testing. We'd talk about it once every month or two; other than that, I read a lot and just kept hurting myself accidentally until I understood where the pain was coming from.&lt;/li&gt;
&lt;li&gt;I do know that a third of 17,000 is not 4,000!&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I hope you enjoy the talk. If you have feedback, you can find my contact info at the bottom of this page. I'd appreciate hearing it!&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>A Raw View into My Unix Hackery</title>
        <link href="http://blog.extracheese.org/2010/04/a-raw-view-into-my-unix-hackery.html"/>
        <updated>2010-04-27T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2010/04/a-raw-view-into-my-unix-hackery</id>
        <content type="html">&lt;p&gt;I just converted my blog from PyBlosxom to Jekyll. After the conversion, I wanted to make sure that no incoming links were broken. I recorded the following screencast of me answering that question.&lt;/p&gt;

&lt;p&gt;This wasn't rehearsed; I didn't even spend time thinking about how I'd solve the problem ahead of time. I was also sort of run down while recording it; I'd been hacking away at this stuff for many hours by the time I recorded. Still, it gives you a glimpse of what it looks like when I puzzle through a problem at the Unix command line, something that people seem to be interested in.&lt;/p&gt;

&lt;p&gt;&lt;object width="400" height="225"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=11202537&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=11202537&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="225"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;p&gt;&lt;a href="http://vimeo.com/11202537"&gt;A Raw View into My Unix Hackery&lt;/a&gt; from &lt;a href="http://vimeo.com/garybernhardt"&gt;Gary Bernhardt&lt;/a&gt; on &lt;a href="http://vimeo.com"&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;&lt;/p&gt;

&lt;p&gt;At the end, I come up with a pretty long command. I did some further tweaking after the screencast ended; the final command is below. Although I've formatted it on multiple lines here, keep in mind that this was written at the console without newlines or regard for quality or brevity, so it's not pretty. See the video to learn how such things are born.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;cat logs/apache/access_blog* |
grep &lt;span class="s1"&gt;&amp;#39; 200 &amp;#39;&lt;/span&gt; |
grep -v &lt;span class="s1"&gt;&amp;#39;&amp;quot;http://blog.extracheese.org/[^&amp;quot;]&amp;#39;&lt;/span&gt; |
grep -v &lt;span class="s1"&gt;&amp;#39;Feedfetcher-Google&amp;#39;&lt;/span&gt; |
grep -v &lt;span class="s1"&gt;&amp;#39;Googlebot&amp;#39;&lt;/span&gt; |
grep -v &lt;span class="s1"&gt;&amp;#39;my6sense&amp;#39;&lt;/span&gt; |
grep -v &lt;span class="s1"&gt;&amp;#39;search.msn.com&amp;#39;&lt;/span&gt; |
grep -v &lt;span class="s1"&gt;&amp;#39;scoutjet&amp;#39;&lt;/span&gt; |
grep -v &lt;span class="s1"&gt;&amp;#39;betaBot&amp;#39;&lt;/span&gt; |
grep -v &lt;span class="s1"&gt;&amp;#39;Yahoo! Slurp&amp;#39;&lt;/span&gt; |
grep -v &lt;span class="s1"&gt;&amp;#39;aggregator:Spinn3r&amp;#39;&lt;/span&gt; |
grep -v &lt;span class="s1"&gt;&amp;#39;FeedBurner&amp;#39;&lt;/span&gt; |
grep -v &lt;span class="s1"&gt;&amp;#39;Planet Python&amp;#39;&lt;/span&gt; |
grep -v &lt;span class="s1"&gt;&amp;#39;FeedBurner/1.0&amp;#39;&lt;/span&gt; |
grep -v 80legs.com |
grep -v Yandex |
grep -v &lt;span class="s1"&gt;&amp;#39;.NET CLR&amp;#39;&lt;/span&gt; |
grep -v &lt;span class="s1"&gt;&amp;#39;seoprofiler&amp;#39;&lt;/span&gt; |
grep -v &lt;span class="s1"&gt;&amp;#39;urdland&amp;#39;&lt;/span&gt; |
grep -v &lt;span class="s1"&gt;&amp;#39;Speedy Spider&amp;#39;&lt;/span&gt; |
grep -v &lt;span class="s1"&gt;&amp;#39;Ask Jeeves&amp;#39;&lt;/span&gt; |
cut -d &lt;span class="s1"&gt;&amp;#39;&amp;quot;&amp;#39;&lt;/span&gt; -f 2 |
cut -d &lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt; -f 2 |
awk &lt;span class="s1"&gt;&amp;#39;{urls[$1]++} END {for (url in urls) print urls[url], url}&amp;#39;&lt;/span&gt; |
sort -nr |
grep -v &lt;span class="s1"&gt;&amp;#39;index.php&amp;#39;&lt;/span&gt; |
grep -v &lt;span class="s1"&gt;&amp;#39;widgetType&amp;#39;&lt;/span&gt; |
cut -d &lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt; -f 2 |
cut -d &lt;span class="s1"&gt;&amp;#39;?&amp;#39;&lt;/span&gt; -f 1 |
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;url; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="k"&gt;    &lt;/span&gt;curl &lt;span class="s2"&gt;&amp;quot;http://localhost:4000$url&amp;quot;&lt;/span&gt; 2&amp;gt;&amp;amp;1 |
    grep -i &lt;span class="s1"&gt;&amp;#39;&amp;lt;h1&amp;gt;not found&amp;lt;/h1&amp;gt;&amp;#39;&lt;/span&gt; &amp;gt; /dev/null;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; -eq 0 &lt;span class="o"&gt;]&lt;/span&gt;; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="k"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$url&lt;/span&gt;;
    &lt;span class="k"&gt;fi&lt;/span&gt;;
&lt;span class="k"&gt;done&lt;/span&gt; |
grep -v &lt;span class="s1"&gt;&amp;#39;\.css$&amp;#39;&lt;/span&gt; |
grep -v &lt;span class="s1"&gt;&amp;#39;\.js$&amp;#39;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Happily, I only had to redirect the RSS feed URLs. Everything else that I actually cared about worked the first time!&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Python vs. Ruby: A Battle to The Death</title>
        <link href="http://blog.extracheese.org/2010/02/python-vs-ruby-a-battle-to-the-death.html"/>
        <updated>2010-02-15T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2010/02/python-vs-ruby-a-battle-to-the-death</id>
        <content type="html">&lt;p&gt;At &lt;a href="http://www.seapig.org/NWPD10"&gt;Northwest Python Day 2010&lt;/a&gt; on January 30th, I gave two talks; this is the second, which was the last talk of the day.  I tuned it to its audience and time slot, making it biased toward an audience that already knows Python, as well as being lighter than originally intended. I still quite like the result.&lt;/p&gt;

&lt;p&gt;During my talk, I mention &lt;a href="http://www.infoq.com/presentations/braithwaite-rewrite-ruby"&gt;Ruby.rewrite(Ruby)&lt;/a&gt;, a talk by Reg Braithwithe. I recommend it. It also happens to contain my favorite quote from any talk, ever, which I will not repeat here. You'll just have to guess!&lt;/p&gt;

&lt;p&gt;I've removed a few small bits from the audio &amp;ndash; one grossly incorrect statement, one slide that could be misinterpreted in ways I want to avoid, and a couple of audience questions that I basically had no answer for.  None of these edits affects the content of the talk in my opinion.&lt;/p&gt;

&lt;p&gt;The talk is dense and necessarily glosses over a lot of subtleties. I talk about the Zen of Python, monkey patching (several times), the Ruby community's reckless hastiness, the syntax of RSpec and cucumber, beauty and ugliness in languages and testing tools, the complexity of the languages' grammars, syntactic vs. semantic complexity, the relative taste of grasshoppers and tree bark, etc., etc. There's way too much here to give anything a fair treatment. I hope that you'll keep this in mind while watching, avoid interpreting the talk as a claim to absolute truth, and simply enjoy it for what it is.&lt;/p&gt;

&lt;p&gt;Shortly after the event, Geoffrey Grosenbach posted &lt;a href="http://blog.peepcode.com/tutorials/2010/what-pythonistas-think-of-ruby"&gt;his thoughts&lt;/a&gt;. You should take a look for another viewpoint.&lt;/p&gt;

&lt;p&gt;I'll be at PyCon later this week, by the way, so feel free to commiserate with, or rant at, me. With that, here's the talk:&lt;/p&gt;

&lt;p&gt;&lt;object width="400" height="225"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=9471538&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=9471538&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="225"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;p&gt;&lt;a href="http://vimeo.com/9471538"&gt;Python vs. Ruby: A Battle to The Death&lt;/a&gt; from &lt;a href="http://vimeo.com/user1043515"&gt;Gary Bernhardt&lt;/a&gt; on &lt;a href="http://vimeo.com"&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Functional and Non-Functional Testing</title>
        <link href="http://blog.extracheese.org/2010/01/functional-and-non-functional-testing.html"/>
        <updated>2010-01-21T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2010/01/functional-and-non-functional-testing</id>
        <content type="html">&lt;p&gt;Different types of complexity interact with test suites in different ways.  Consider &lt;a href="/2009/07/a-brief-history-of-bitbacker-a-startup.html"&gt;BitBacker&lt;/a&gt;, an online backup product that I worked on for three years. It had very few functional requirements. At its core, it only had to let the user choose files, back them up, and restore them. Almost all of the complexity was non-functional: it had to look good and be easy to use, of course, but it also had to be fast and secure. I spent most of my development effort on the "fast" and "secure" parts.&lt;/p&gt;

&lt;p&gt;(Note that when I say "functional" here, I'm talking about requirements for a system's behavior. This has nothing to do with functional programming.)&lt;/p&gt;

&lt;p&gt;In this type of app, where there are so few functional requirements, functional test fragility is less of a problem. In a recent discussion with &lt;a href="http://www.wavethenavel.com"&gt;Jonathan Penn&lt;/a&gt;, I mentioned that the backup and restore functionality were tested at the unit, subsystem, and full-stack levels: three different levels of tests, all testing the same thing. He asked me whether this made refactoring difficult. It didn't.&lt;/p&gt;

&lt;p&gt;BitBacker's functional requirements were never going to change. When the user backed up and then restored files, they had to be identical to the originals. That's all. It took 17,000 lines of code to make that happen efficiently and securely, but the surface area of the user-facing problem is tiny.&lt;/p&gt;

&lt;p&gt;I didn't know this at the time. If I was building a business app instead of a backup system, I probably would've ended up with a similar test suite, and in that situation it would've been a burden. Fortunately, I got lucky, and I've learned this lesson by retrospecting about my luck rather than retrospecting about some pain that I felt.&lt;/p&gt;

&lt;p&gt;What about automated non-functional tests? The topic is murky in general, and I only know how to test small subsets of the non-functional requirement space. I don't know how to automate testing of user experience, for example.&lt;/p&gt;

&lt;p&gt;I have done automated performance testing, however. At one point, I wrote tests for BitBacker that ran backups across a wide range of file counts and asserted that the backup time grew linearly with the number of files.  That's clearly a non-functional test, but how fragile is it?&lt;/p&gt;

&lt;p&gt;It's very fragile, of course, unless you run it on massive file counts that would've taken far too long for my patience at the time. I left the file counts small, so it ran fast but broke constantly, which eventually led me to remove it.&lt;/p&gt;

&lt;p&gt;I replaced the test with a system that could kick off various predefined processes ("do an empty backup", "back up 1,000 files", etc.), graphing the runtimes and memory footprints across revisions in version control.  One look at those performance graphs would show whether, and where, there was a problem.  This gave me a different kind of feedback: instead of defining "success" and "failure", it would alert me to a change, which I could then investigate on my own.&lt;/p&gt;

&lt;p&gt;I suspect that this is a fundamental property of non-functional testing.  Trying to fully automate it and boil it down to a set of pass/fail assertions, while sometimes possible, seems prone to fragility. It may be that non-functional testing is best achieved by dashboard apps, like my performance-over-revisions graph, or an app that renders every page in a user flow automatically and highlights recent changes in appearance.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Test Double Injection Inversion</title>
        <link href="http://blog.extracheese.org/2010/01/test-double-injection-inversion.html"/>
        <updated>2010-01-19T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2010/01/test-double-injection-inversion</id>
        <content type="html">&lt;p&gt;In &lt;a href="http://blog.objectmentor.com/articles/2010/01/17/dependency-injection-inversion"&gt;Dependency Injection Inversion&lt;/a&gt;, Uncle Bob wonderfully explains the difference between Dependency Injection and Dependency Injection &lt;em&gt;Frameworks&lt;/em&gt;, a topic I've done a lot of thinking about recently. You should go read his post right now if you haven't yet.&lt;/p&gt;

&lt;p&gt;At the end, he provides the test code below as an example of testing some dependency-injected Java code:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BillingServiceTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="n"&gt;LogSpy&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

  &lt;span class="nd"&gt;@Before&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;LogSpy&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@Test&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;approval&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;BillingService&lt;/span&gt; &lt;span class="n"&gt;bs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;BillingService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Approver&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;processCharge&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9000&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Bob&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Transaction by Bob for 9000 approved&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogged&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@Test&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;denial&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;BillingService&lt;/span&gt; &lt;span class="n"&gt;bs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;BillingService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Denier&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;processCharge&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9000&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Bob&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Transaction by Bob for 9000 denied&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogged&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Approver&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="n"&gt;CreditCardProcessor&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;approve&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Denier&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="n"&gt;CreditCardProcessor&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;approve&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LogSpy&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="n"&gt;TransactionLog&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;logged&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;logged&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getLogged&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;logged&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;It's perfectly fine Java code, and it wonderfully demonstrates the power of injection. After the code, Uncle bob says:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;It would have been tragic to use a mocking framework for such a simple set of tests.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;In Java, I agree completely. In a more modern language, I &lt;em&gt;disagree&lt;/em&gt; completely!  I've translated his example to Python using my &lt;a href="http://bitbucket.org/garybernhardt/dingus/"&gt;Dingus&lt;/a&gt; test double library to illustrate the simplicity that doubles can provide:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BillingServiceTest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dingus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_approval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;approver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dingus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;approve__returns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;bs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BillingService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;approver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process_charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Bob&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;&amp;#39;log&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;&amp;#39;Transaction by Bob for 9000 approved&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;once&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_denial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;denier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dingus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;approve__returns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;bs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BillingService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;approver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process_charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Bob&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;&amp;#39;log&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;&amp;#39;Transaction by Bob for 9000 denied&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;once&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;In a real system, I'd factor these tests slightly differently; I've left them as close to Bob's as possible. This is 13 ELOC vs. Bob's 38 &amp;ndash; only about a third as much code! Some of the difference is in his testing library's ceremony, but most of it is in his test doubles. For example, he says:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Approver&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="n"&gt;CreditCardProcessor&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;approve&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;That is a lot of code! All it really says is "the approve method always returns true", with the rest being a complex dance around Java's rigidity.  This is a liability for programmers working in such languages, as well as a learning barrier for new testers. In my Python version, the following takes the place of the Approver class, as well as its instantiation:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;approver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dingus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;approve__returns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;That line of code is so close to "the approve method always returns true" that I can't imagine it being any clearer.  Of course, if the magic double underscores turn you off, you can also say:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;approver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dingus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;approve&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;returner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;blockquote&gt;&lt;h4&gt;Digression&lt;/h4&gt;

&lt;p&gt;I'd love to hear what you think about those two alternate forms. I want to deprecate one, but I don't know which.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;I fear that statements like Uncle Bob's about test doubles may lead newer programmers, and static-only programmers, astray. His advice is wonderful, but only in certain domains. Like so many things in software, doubling is far easier when the shackles of Javaesque type systems are removed. And, if you worry that the complexity is simply moved into the test double library, fear not: &lt;a href="http://bitbucket.org/garybernhardt/dingus/src/tip/dingus.py"&gt;Dingus&lt;/a&gt; is currently 193 ELOC long, including plenty of features not mentioned here!&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>String Calculator Kata in Python</title>
        <link href="http://blog.extracheese.org/2010/01/string-calculator-kata-in-python.html"/>
        <updated>2010-01-06T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2010/01/string-calculator-kata-in-python</id>
        <content type="html">&lt;p&gt;For those who aren't familiar, katas are those impressive sequences of movements that you've surely seen martial arts guys perform. Code katas are the same idea applied to writing code: you solve a problem many times, mastering the movements, and then perform it for others.&lt;/p&gt;

&lt;p&gt;My friend &lt;a href="http://www.coreyhaines.com"&gt;Corey Haines&lt;/a&gt; has worked with some other people to run a &lt;a href="http://www.katacasts.com"&gt;Katacast&lt;/a&gt; site dedicated to code katas, posting them roughly once per week. Recently, there's been a string of solutions to the same problem, with my Python version as this week's entry.&lt;/p&gt;

&lt;p&gt;Briefly, here's the problem I solve. I have numbers coming in as a string, separated by commas or newlines. My job is to add those numbers and return the sum. There are two complications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the first line of the string is of the form "//&lt;em&gt;", then * is also a possible delimiter. This works for any string, not just &lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Negative numbers must be rejected.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;You can see the kata and read my brief commentary on the &lt;a href="http://katas.softwarecraftsmanship.org/?p=128"&gt;Katacasts&lt;/a&gt; site, or go straight to &lt;a href="http://vimeo.com/8569257"&gt;Vimeo&lt;/a&gt; to watch it. It's only 4:32 long, so it's not a big commitment.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://vimeo.com/8569257"&gt;&lt;img src="/images/string_calculator_python_katacast_thumbnail.png" alt="string calculator screencast" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://vimeo.com/8569257"&gt;String Calculator Kata in Python&lt;/a&gt; from &lt;a href="http://vimeo.com/user1043515"&gt;Gary Bernhardt&lt;/a&gt; on &lt;a href="http://vimeo.com"&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My screencasts always prompt questions about my Vim configuration, so take a look at my &lt;a href="http://bitbucket.org/garybernhardt/dotfiles"&gt;dotfiles repo&lt;/a&gt; if you're interested. For this kata, I'm slowing down intentionally &amp;ndash; typing slower and inserting small, regular pauses so the viewer has time to look around a bit. I may post a "hard mode" version at full speed if people show interest.&lt;/p&gt;

&lt;p&gt;Comments are encouraged, of course &amp;ndash; the purpose of a kata is improvement!&lt;/p&gt;

&lt;p&gt;If you like this, by the way, you may also enjoy the &lt;a href="/2009/11/refactoring_a_cyclomatic_complexity_script.html"&gt;refactoring&lt;/a&gt; screencast I posted recently.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>On Abstraction</title>
        <link href="http://blog.extracheese.org/2009/12/on_abstraction.html"/>
        <updated>2009-12-16T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2009/12/on_abstraction</id>
        <content type="html">&lt;p&gt;Some people seem to consider &lt;em&gt;abstraction&lt;/em&gt; a bad word. I think that this is misguided and impedes progress &amp;ndash; all software is abstraction.  Understanding what our abstractions mean, and what makes them good or bad, is the core of design.&lt;/p&gt;

&lt;p&gt;For now, let's define abstractions as concepts; nothing more. If it's a concept in your head, it's an abstraction. (I've tried to define the word more fully about ten times, deleting each definition in turn.)&lt;/p&gt;

&lt;p&gt;The interesting part of abstractions is their violation.  First, the textual definition of an abstraction &amp;ndash; a class, for example &amp;ndash; can violate &lt;em&gt;itself&lt;/em&gt;. This happens when a class presents information at more than one level of abstraction.  Here's Grady Booch, from "Object Oriented Analysis and Design":&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;[The] class Dog is functionally cohesive if its semantics embrace the behavior of a dog, the whole dog, and nothing but the dog."&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;It's a wonderfully terse explanation, but doesn't go far enough for our purposes because it doesn't address relationships.&lt;/p&gt;

&lt;blockquote&gt;&lt;h4&gt;Example&lt;/h4&gt;

&lt;p&gt;A Person class can have a first_name field. But should Person also have a set of address fields like street and zip_code? Probably not. These fields are part of an Address, which is a concept that exists independent of Person.  Moving them into an Address class reifies this natural abstraction in our code, making it mirror the way the ideas are structured in our brains.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;This is sort of a restatement of the Single Responsibility Principle (SRP), which is sort of a restatement of the principle of cohesion. We have many tools for thinking about this idea because it's important.&lt;/p&gt;

&lt;p&gt;Abstractions can also be violated from &lt;em&gt;outside&lt;/em&gt;. If an object exposes a set of fields to me, I should avoid looking into those fields' structure.  In other words, I must respect the abstraction provided by the object.  If I feel the need to violate the abstraction, I need to reconsider how to modify the boundaries to match that need, rather than violating the boundaries by crossing them.&lt;/p&gt;

&lt;p&gt;This is the moment when design happens: I can take the path of short-term gain by reaching into my collaborators' collaborators, or I can take the path of long-term gain by refactoring my design to match the conceptual model.&lt;/p&gt;

&lt;blockquote&gt;&lt;h4&gt;Example&lt;/h4&gt;

&lt;p&gt;Suppose I have a Person and need to tell the SnailMailer to send him mail. The SnailMailer, as currently designed, takes a street, a zip_code, etc. I could pull the data out of the address fields, like person.address.zip_code, then pass them to the SnailMailer. But in doing that, I would violate the Person abstraction.&lt;/p&gt;

&lt;p&gt;Instead, I should have stepped back and thought about the contract of the SnailMailer. It would be better to pass in the Person's Address instead of its components. That way, I rely only on the Person abstraction (it has an Address) and the SnailMailer abstraction (it sends to addresses). I remove my dependency on the structure of a Person's Address (street, zip, etc.) and I remove my dependency on the SnailMailer's expectations about address fields (street, zip, etc.) The SnailMailer can decide how to deal with those.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;This is sort of a restatement of the Law of Demeter, which is sort of a restatement of part of the principle of coupling. These are symmetric with the definition side of abstraction in a pleasing way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;My abstraction vs. your use of it;&lt;/li&gt;
&lt;li&gt;Single Responsibility Principle vs. Law of Demeter;&lt;/li&gt;
&lt;li&gt;Cohesion vs. Coupling.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Most of the design principles we talk about regularly, like those listed above, are syntactic &amp;ndash; they are properties of the text of the code.  But syntax is only a means; the thing that really matters is that the semantic model of the code mirror the semantic model in our brains.  Thinking about (or being preached to about) the design principles in isolation can make them feel arbitrary; it's much better to view them in the light of abstraction integrity.&lt;/p&gt;

&lt;p&gt;Abstraction is important! The result of programming isn't simply a computation; it's also a set of ideas made concrete in a programming language. Nothing can beat the long-term business value of ideas expressed clearly in code.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Refactoring A Cyclomatic Complexity Script</title>
        <link href="http://blog.extracheese.org/2009/11/refactoring_a_cyclomatic_complexity_script.html"/>
        <updated>2009-11-16T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2009/11/refactoring_a_cyclomatic_complexity_script</id>
        <content type="html">&lt;p&gt;I recently &lt;a href="/2009/10/cyclomatic-complexity-in-vim-first-steps.html"&gt;blogged&lt;/a&gt; about the cyclomatic complexity script I wrote that highlights Python code based on its complexity. The script itself is written in Python, but the code was rushed together. I decided to do some cleanup, recording the process as an example of refactoring.&lt;/p&gt;

&lt;p&gt;You can see a lossless copy of the video by logging into Vimeo and clicking "Download Quicktime version" at the bottom right of the page. I recommend it; MPEG artifacting is no fun.&lt;/p&gt;

&lt;p&gt;&lt;object width="400" height="635"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=7642937&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=7642937&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="635"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;p&gt;&lt;a href="http://vimeo.com/7642937"&gt;Refactoring A Cyclomatic Complexity Module&lt;/a&gt; from &lt;a href="http://vimeo.com/user1043515"&gt;Gary Bernhardt&lt;/a&gt; on &lt;a href="http://vimeo.com"&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;&lt;/p&gt;

&lt;p&gt;I make one notable mistake &amp;ndash; I leave an unneeded reference to "code_or_node" lying around. Just imagine that I delete it at the very end. :)&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>The Limits of TDD</title>
        <link href="http://blog.extracheese.org/2009/11/the_limits_of_tdd.html"/>
        <updated>2009-11-09T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2009/11/the_limits_of_tdd</id>
        <content type="html">&lt;p&gt;My &lt;a href="/2009/11/how_i_started_tdd.html"&gt;last post&lt;/a&gt; about TDD generated some great responses, some of which were skeptical. A few common complaints about TDD were brought up, and posed with civility, so I'd like to address them.&lt;/p&gt;

&lt;h3&gt;Complaint: You weren't stupid enough&lt;/h3&gt;

&lt;p&gt;When TDDing Fibonacci, we could get to a point where we have this function (and I did write exactly this code in my last post):&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;But why should we write that? Why not this instead?&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;This comes down to how we define "simple". In TDD, we make tests pass by making the simplest possible change.  So, which of the above two is simpler?&lt;/p&gt;

&lt;p&gt;Defining that word is our job; TDD as a process says nothing about it.  The definition is a &lt;em&gt;huge&lt;/em&gt; variable and, in my experience, it's the primary axis along which our skill as TDDers grows once we reach minimal competence.  Note that we still have to define "simple" even if we're not doing TDD, but we won't have the test-driving pressure forcing the definition to be refined.&lt;/p&gt;

&lt;p&gt;Regardless of how "simple" is defined, we must eventually accept that an arbitrarily long list is not the simplest thing. At that point, we refactor.  Depending on the definition of simple, it may take seven tests to get to the final refactor instead of five. So what? Two TDDers need not generate the same tests, and this isn't a problem at all.&lt;/p&gt;

&lt;h3&gt;Complaint: TDDed tests are prescriptive&lt;/h3&gt;

&lt;p&gt;This is a complaint that TDDed code does exactly what the tests say it should do, so there might be bugs. If I write the wrong test, the reasoning goes, then it will drive me to write the wrong code.&lt;/p&gt;

&lt;p&gt;When would we write the wrong test? Only when we misunderstand the problem. If we misunderstand the problem, and we go straight to the code, then we're be encoding our incorrect understanding &lt;em&gt;directly in the code&lt;/em&gt;. That's bad. By writing the tests first, we have some extra protection against misunderstanding: every assumption about what the system &lt;em&gt;should&lt;/em&gt; do is encoded as a test, and each test has a good name.&lt;/p&gt;

&lt;p&gt;Often, this will point out our confusion during the TDD process &amp;ndash; we'll find that we want to write a test whose name contradicts another test's name. Even if we translate our misunderstanding into a bug, however, good test names make it easy to revisit our assumptions later. A subtle, five-character change to the code may have been driven by a sixty-character test name, which will be easier to understand.&lt;/p&gt;

&lt;h3&gt;Complaint: Choosing tests is hard&lt;/h3&gt;

&lt;p&gt;When TDDing Fibonacci, I tested fib(0) first. Why did I test fib(1) next instead of fib(37) or fib(51)?&lt;/p&gt;

&lt;p&gt;Because it was obvious! The problem domain of a unit test is necessarily small, so it's usually clear what the next step is.  If the next step isn't clear, it probably means that the unit under test is too large (making it hard to think about extending it for another case), or that we don't understand the problem well enough (making it hard to think about what the code should do at all). In either case, TDD has just helped us: it's either pointed out a bad design, which we should fix, or it's pointed a gap in our knowledge about the problem, in which case we should put the keyboard away and fill that gap.&lt;/p&gt;

&lt;h3&gt;Complaint: The code you TDDed was bad&lt;/h3&gt;

&lt;p&gt;The particular code I came up with in my last blog post was a slow, recursive Fibonacci solution. Two people mentioned this in the comments.&lt;/p&gt;

&lt;p&gt;TDD doesn't solve problems like "my run time is superlinear" or "my database loads aren't eager enough."  It's not &lt;em&gt;supposed&lt;/em&gt; to solve those problems!  TDD frees us to solve those hard problems well by (1) pushing us toward a good, decoupled design and (2) providing us with large, fast test suites.&lt;/p&gt;

&lt;h3&gt;Complaint: TDD requires too much typing&lt;/h3&gt;

&lt;p&gt;This one has the easiest answer of all: &lt;a href="http://anarchycreek.com/2009/05/26/how-tdd-and-pairing-increase-production/"&gt;typing is not the bottleneck&lt;/a&gt;.  Just think about it for a minute. Go back and look at how many lines of code you actually generated yesterday. How long would it take you to type it all in one long burst? A few minutes?  Seriously, typing is not the bottleneck.&lt;/p&gt;

&lt;h3&gt;TDD is not magic&lt;/h3&gt;

&lt;p&gt;Let's recap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complaint: You weren't stupid enough.&lt;/li&gt;
&lt;li&gt;Response: There's more than one legitimate definition of "stupid".&lt;/li&gt;
&lt;/ul&gt;


&lt;!-- --&gt;


&lt;ul&gt;
&lt;li&gt;Complaint: TDDed tests are prescriptive.&lt;/li&gt;
&lt;li&gt;Response: This is a feature. Stating our assumptions up front exposes misunderstandings.&lt;/li&gt;
&lt;/ul&gt;


&lt;!-- --&gt;


&lt;ul&gt;
&lt;li&gt;Complaint: Choosing tests is hard.&lt;/li&gt;
&lt;li&gt;Response: This is also a feature. It tells us that our design is bad or that we don't understand the problem.&lt;/li&gt;
&lt;/ul&gt;


&lt;!-- --&gt;


&lt;ul&gt;
&lt;li&gt;Complaint: The code you TDDed was bad!&lt;/li&gt;
&lt;li&gt;Response: TDD does not free us from thinking. TDD is not magic.&lt;/li&gt;
&lt;/ul&gt;


&lt;!-- --&gt;


&lt;ul&gt;
&lt;li&gt;Complaint: It's too much typing.&lt;/li&gt;
&lt;li&gt;Response: Typing is not the bottleneck.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Many complaints about TDD are complaints that it &lt;em&gt;doesn't&lt;/em&gt; solve some problem.  These are not problems with TDD &amp;ndash; it's not supposed to solve every problem!&lt;/p&gt;

&lt;p&gt;Dynamic languages don't make coffee, continuous integration doesn't shine shoes, and TDD doesn't make code scale. It's simply the basis of a solid, disciplined process for building software &amp;ndash; a beginning, not an end.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>How I Started TDD</title>
        <link href="http://blog.extracheese.org/2009/11/how_i_started_tdd.html"/>
        <updated>2009-11-04T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2009/11/how_i_started_tdd</id>
        <content type="html">&lt;p&gt;This story is about the first code I ever wrote with proper TDD.  I'd been doing test-first for several months, but I didn't understand the design aspect. Fortunately, &lt;a href="http://www.coreyhaines.com"&gt;Corey Haines&lt;/a&gt; wanted to learn Python, and I wanted to learn TDD, so we paired up at a Coding Dojo.  It went something like this. [1]&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Corey&lt;/em&gt;: Let's write a test.&lt;/p&gt;&lt;/blockquote&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_fib_of_0_is_0&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;




&lt;p class="error"&gt;1 test failed; 0 tests passed.&lt;/p&gt;


&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Corey&lt;/em&gt;: Now let's make it pass.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Me&lt;/em&gt;: Well, we could iterate...&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Corey&lt;/em&gt;: Why?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Me&lt;/em&gt;: Because it's fibonacci...&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Corey&lt;/em&gt;: The test says it returns zero!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Me&lt;/em&gt;: Oh. Well, OK.&lt;/p&gt;&lt;/blockquote&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;




&lt;p class="success"&gt;1 test passed.&lt;/p&gt;


&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Corey&lt;/em&gt;: Let's write another test.&lt;/p&gt;&lt;/blockquote&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_fib_of_1_is_1&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;




&lt;p class="error"&gt;1 test failed; 1 test passed.&lt;/p&gt;


&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Corey&lt;/em&gt;: Now let's make it pass.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Me&lt;/em&gt;: OK, we need to recursively...&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;I stop myself. I know what this got me last time.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Me&lt;/em&gt;: We can check for which input we got.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Corey&lt;/em&gt;: We don't even need that.&lt;/p&gt;&lt;/blockquote&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;




&lt;p class="success"&gt;2 tests passed.&lt;/p&gt;


&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Corey&lt;/em&gt;: Let's write another test.&lt;/p&gt;&lt;/blockquote&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_fib_of_2_is_1&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;




&lt;p class="error"&gt;1 test failed; 2 tests passed.&lt;/p&gt;


&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Corey&lt;/em&gt;: Now let's make it pass&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;I pause while I find the correct answer.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Me&lt;/em&gt;: Only the zero case is different.&lt;/p&gt;&lt;/blockquote&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;




&lt;p class="success"&gt;3 tests passed.&lt;/p&gt;


&lt;p&gt;(I consider the implications of this. "Only the zero case is different." This is an inductive system, so it needs a basis case. Zero is only half of the basis case of a fibonacci sequence, but I never had to think about a basis case or recursion to write this code. The tests showed me what the code needed to do.)&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Corey&lt;/em&gt;: Let's write another test.&lt;/p&gt;&lt;/blockquote&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_fib_of_3_is_2&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;




&lt;p class="error"&gt;1 test failed; 3 tests passed.&lt;/p&gt;


&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Me&lt;/em&gt;: Another if?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Corey&lt;/em&gt;: Another if.&lt;/p&gt;&lt;/blockquote&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;




&lt;p class="success"&gt;4 tests passed.&lt;/p&gt;


&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Corey&lt;/em&gt;: Refactor!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Me&lt;/em&gt;: I don't know...&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;My brain hurts for a moment.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;




&lt;p class="success"&gt;4 tests passed.&lt;/p&gt;


&lt;p&gt;The full basis case is in place and we don't even need recursion yet.  I'm surprised by how many cases we've written without needing recursion or iteration.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Corey&lt;/em&gt;: Another test.&lt;/p&gt;&lt;/blockquote&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_fib_of_4_is_3&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;




&lt;p class="success"&gt;5 tests passed.&lt;/p&gt;


&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Me&lt;/em&gt;: It passed without changes. Is that OK?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Corey&lt;/em&gt;: Another test!&lt;/p&gt;&lt;/blockquote&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_fib_of_5_is_5&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;




&lt;p class="error"&gt;1 test failed; 5 tests passed.&lt;/p&gt;


&lt;p&gt;I think I can handle this now.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;




&lt;p class="success"&gt;6 tests passed.&lt;/p&gt;


&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Corey&lt;/em&gt;: Refactor!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Me&lt;/em&gt;: Combine them into... recursion?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Corey&lt;/em&gt;: Combine them into recursion.&lt;/p&gt;&lt;/blockquote&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;




&lt;p class="success"&gt;6 tests passed.&lt;/p&gt;


&lt;p&gt;This isn't a perfect example of TDD, but that's not the point. The first thing you need to understand is the rough process: write the smallest failing test you can; then write the smallest code to make it pass; then refactor without changing behavior.&lt;/p&gt;

&lt;p&gt;After getting this lesson from Corey, I went off and TDDed a couple thousand lines of code with almost no outside feedback.  I was doing it very poorly, and often became frustrated, but in retrospect it was still the best code I'd ever written.&lt;/p&gt;

&lt;p&gt;It takes years to learn how to do this well, and consistently, across a wide variety of situations. I've been doing it for two years, and I still &lt;a href="/2009/10/my-personal-failures-in-test-isolation.html"&gt;have non-trivial problems&lt;/a&gt;, but I can almost always move forward confidently.&lt;/p&gt;

&lt;p&gt;Building software without TDD was crushingly stressful, but I couldn't see it at the time.  It was only shown to me when I started working one test at a time, one line of code at a time, with verification that the entire system is working in less than two seconds.&lt;/p&gt;

&lt;p&gt;([1] In reality, the Coding Dojo probably went only vaguely like this, and this isn't even the problem we solved, but that's not the point.  This is what the first true TDD session always looks like.)&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>The Value of Continuous Processes</title>
        <link href="http://blog.extracheese.org/2009/10/the-value-of-continuous-processes.html"/>
        <updated>2009-10-29T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2009/10/the-value-of-continuous-processes</id>
        <content type="html">&lt;p&gt;Consider the construction of software:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bad: create a big design, then turn it into code.&lt;/li&gt;
&lt;li&gt;Better: create a small design, then turn it into code, then stop and ponder it.&lt;/li&gt;
&lt;li&gt;Best [1]: design software in the shortest iterations possible &amp;ndash; on the order of minutes &amp;ndash; with failing tests as your guide.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Consider the construction of tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bad: write a lot of code, then test it before a release.&lt;/li&gt;
&lt;li&gt;Better: write some code, then test it at the end of the (1- or 2-week) iteration.&lt;/li&gt;
&lt;li&gt;Best: write code and tests together, ensuring that all tests pass at all times.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Consider the construction of companies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bad: have an idea, write a business plan, get funded, hire a team of 10, build the product, release it.&lt;/li&gt;
&lt;li&gt;Better: have an idea, build the simplest version that could possibly work, validate it in the market.&lt;/li&gt;
&lt;li&gt;Best: have an idea, validate it in the market &lt;a href="http://www.docleyblog.com/2009/10/the-tim-ferris-way-of-testing-ideas-and-how-i-did-it/"&gt;without writing any code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Consider the construction of products:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bad: collect requirements, generate specifications, build software, release it when it's "done".&lt;/li&gt;
&lt;li&gt;Better: talk to customer, generate story cards, build software, release it every two weeks.&lt;/li&gt;
&lt;li&gt;Best: ???&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;One of these things is not like the others. The first three processes above deliver value faster, more consistently, and with less risk as they become continuous. Why shouldn't this be true of the entire product cycle?&lt;/p&gt;

&lt;p&gt;This is why I'm excited about (1) &lt;a href="http://blog.crisp.se/henrikkniberg/2009/04/03/1238795520000.html"&gt;Kanban&lt;/a&gt; and (2) &lt;a href="http://saucelabs.com/blog/index.php/2009/09/continuous-deployment%E2%80%94the-video/"&gt;continuous deployment&lt;/a&gt;. I think you should be too. Consider it carefully, because so far the score card shows that processes become more effective as the cycles get tighter, and we surely have a lot farther to go.&lt;/p&gt;

&lt;p&gt;[1]: "best" is defined here as "best that I know of."&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>My Personal Failures in Test Isolation</title>
        <link href="http://blog.extracheese.org/2009/10/my-personal-failures-in-test-isolation.html"/>
        <updated>2009-10-28T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2009/10/my-personal-failures-in-test-isolation</id>
        <content type="html">&lt;p&gt;My &lt;a href="http://www.sdtconf.com/wiki/tiki-index.php?page=ATestDoubleDilemma"&gt;position paper&lt;/a&gt; for &lt;a href="http://www.sdtconf.com"&gt;SDTConf&lt;/a&gt; was about test doubles and my problems with refactoring around fully isolated tests.&lt;/p&gt;

&lt;blockquote&gt;&lt;h4&gt;Digression: Isolation&lt;/h4&gt;

&lt;p&gt;There are many colloquial definitions of "unit test". When I use the term, I'm almost always talking about a test that executes code in exactly one production class. If it collaborates, it collaborates only with test doubles like mocks, stubs, and fakes.  Every test's world contains 30 or so lines of production code. If you've not heard of this, it probably sounds crazy. It's not.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="http://blog.thecodewhisperer.com"&gt;J. B. Rainsberger&lt;/a&gt; found my position paper and &lt;a href="http://blog.thecodewhisperer.com/post/206216489/a-comment-on-a-test-double-dilemma"&gt;responded&lt;/a&gt; to it. He quotes me:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;In my TDD practice with test doubles I’ve found that, now that all code is 100% isolated, it’s almost impossible to refactor across classes with confidence unless I totally rewrite them.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;J. B. replies, in part:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;I interpret his comment as though that disappoints him. I invite Gary, and you, to consider an alternative interpretation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rewriting classes, rather than refactoring them, shows good compliance with the Open/Closed Principle, which encourages me.&lt;/li&gt;
&lt;li&gt;Needing to refactor across multiple classes, as opposed to re-implementing everything behind a given interface, probably indicates a layering problem, which I’d expect to notice with duplication in the isolated tests. That encourages me, because I like it when my tests expose design flaws to me.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h3&gt;Abstraction Errors&lt;/h3&gt;

&lt;p&gt;J. B. is absolutely correct: this is about layers, and my failure to layer my software correctly. I've only been doing TDD for two or three years, and I still make non-trivial, multiple-class-spanning abstraction errors. The layering errors creep in across many tests and I just don't see them early enough. I can feel myself slowly getting better at this, but it's taking a while.&lt;/p&gt;

&lt;p&gt;The problem is that I often notice the problem after it already exists, and my dilemma is "how do I fix it?" If I try to replace the hard dependency with an abstraction while avoiding a rewrite of the class, I lose confidence in my tests.&lt;/p&gt;

&lt;p&gt;My concern is that that isolation doesn't work well unless you never make certain classes of errors.  There's too much coupling in this skill set!  It doesn't have a reasonable entry point; you have to be a relentless jerk &amp;ndash; as I clearly am &amp;ndash; to break in.  I want to ease people into isolation without telling them "just wait five years and you'll be fine."&lt;/p&gt;

&lt;h3&gt;Vertical Changes in Semantics&lt;/h3&gt;

&lt;p&gt;I have a closely-related example that isn't a refactoring, but displays exactly this problem. I wrote &lt;a href="http://bitbucket.org/garybernhardt/mote/"&gt;Mote&lt;/a&gt;, a test runner for Python. The original design had the suite class collecting all of the tests, then handing them off to the result printer.&lt;/p&gt;

&lt;p&gt;I wanted to replace this with a pure pull process, where the printer pulls one example at a time through the entire stack, with the goal being to output the cases as they're evaluated rather than all at once. But the "push" concept pervaded most of the core classes! A context, upon being created, would recursively create its child contexts and examples. A suite, upon being created, would create its contexts.  The isolated tests made this change &lt;em&gt;hard&lt;/em&gt;! I had to rewrite many core classes to maintain confidence, and I actually never finished because it sent me into a horrible death spiral of self-doubt about isolation!&lt;/p&gt;

&lt;blockquote&gt;&lt;h4&gt;Digression: Mote&lt;/h4&gt;

&lt;p&gt;Mote has &lt;em&gt;many&lt;/em&gt; more problems than this, and I consider its internals one of my greatest TDD failures. This is sort of depressing, since I failed to effectively TDD a tool that I was writing to help me do TDD. I think I know why the design ended up so bad, but that's a topic for another blog post entirely.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;I have a vague notion of the answer to this push-pull problem. The suite was directly instantiating contexts, which were directly instantiating examples. While I was writing it, I could &lt;em&gt;feel&lt;/em&gt; that it was wrong.  Usually, when that feeling crops up, I know how to improve the design accordingly. In this case, for some reason, I didn't, and I pushed forward because I wanted Mote to be in a working state for my own personal use.  Now I have the same problem I've had with refactorings: I've introduced a suboptimal design and I have to improve it, but I'll end up rewriting 100% of the code to change 10%.&lt;/p&gt;

&lt;p&gt;I don't need to be sold on isolation or abstraction; I'm already sold, as evidenced by having written a &lt;a href="/2009/04/dingus-screencast-a-mock-stub-library-with-automatic-isolation.html"&gt;mock library&lt;/a&gt; that forcefully isolates your system under test. I'm not even looking for answers to refactoring and vertical-change problems: I know that these classes of problems grow out of a lack of design skills, I know which design skills those are, and I know how to improve them.&lt;/p&gt;

&lt;p&gt;What I'm now looking for is how to grow these skills in a person from the ground up. What if there &lt;em&gt;is&lt;/em&gt; a way to do those nasty, vertical refactors with higher confidence? Maybe not full confidence, but enough to prevent the horrible death spiral of self-doubt?  As things stand, it's very hard to help other people learn these techniques and it just &lt;em&gt;bothers&lt;/em&gt; me.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Cyclomatic Complexity in Vim: First Steps</title>
        <link href="http://blog.extracheese.org/2009/10/cyclomatic-complexity-in-vim-first-steps.html"/>
        <updated>2009-10-25T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2009/10/cyclomatic-complexity-in-vim-first-steps</id>
        <content type="html">&lt;p&gt;I've been kicking around the idea of integrating complexity analysis with vim.  Tonight, I spent a couple hours getting a proof of concept working (for Python code, naturally). Each line gets highlighted based on the complexity of the function: green for low, yellow for medium, red for high.&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/vim_complexity.png" alt="screenshot of vim complexity plugin" /&gt;&lt;/p&gt;

&lt;p&gt;A static image doesn't really do it justice &amp;ndash; the highlighting updates whenever you write a file. There's a &lt;a href="http://vimeo.com/7259161"&gt;15 second video&lt;/a&gt; of me demoing it that's much better than the picture above.&lt;/p&gt;

&lt;p&gt;This is based on &lt;a href="http://www.traceback.org"&gt;Dave Stanek&lt;/a&gt;'s &lt;a href="http://www.traceback.org/2008/03/31/measuring-cyclomatic-complexity-of-python-code"&gt;cyclomatic complexity code&lt;/a&gt;. It's hacked together and the complexity algorithm seems slightly wrong (unless I'm remembering it incorrectly). However, it does successfully compute the complexity and highlight the code, which is good enough for a blog post. ;)&lt;/p&gt;

&lt;p&gt;If you're feeling adventurous, you can find the source code &lt;a href="http://bitbucket.org/garybernhardt/pycomplexity"&gt;on BitBucket&lt;/a&gt;.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Dingus 0.2 Is Released</title>
        <link href="http://blog.extracheese.org/2009/10/dingus-0.2-is-released.html"/>
        <updated>2009-10-24T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2009/10/dingus-0.2-is-released</id>
        <content type="html">&lt;p&gt;Finally, Dingus 0.2 is released. Dingus is a super-permissive record-then-assert mock/stub library that started life as a fork of &lt;a href="http://www.voidspace.org.uk/"&gt;Michael Foord&lt;/a&gt;'s &lt;a href="http://www.voidspace.org.uk/python/mock/"&gt;mock&lt;/a&gt; library. See &lt;a href="/2009/04/dingus-screencast-a-mock-stub-library-with-automatic-isolation.html"&gt;the original Dingus screencast&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;There aren't any huge changes in this release. The biggest changes are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DingusTestCase has an "exclude" argument to allow you to avoid stubbing some module attributes.&lt;/li&gt;
&lt;li&gt;The "one" method is now named "once". E.g., &lt;code&gt;my_dingus.calls('foo').once()"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;There's a much better introductory-level example included.&lt;/li&gt;
&lt;li&gt;Dinguses are now picklable.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;You can download Dingus at &lt;a href="http://pypi.python.org/pypi/dingus"&gt;the package index&lt;/a&gt; and the source code on &lt;a href="http://bitbucket.org/garybernhardt/dingus"&gt;BitBucket&lt;/a&gt;.  Enjoy!&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>A Brief History of BitBacker, A Startup</title>
        <link href="http://blog.extracheese.org/2009/07/a-brief-history-of-bitbacker-a-startup.html"/>
        <updated>2009-07-23T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2009/07/a-brief-history-of-bitbacker-a-startup</id>
        <content type="html">&lt;p&gt;BitBacker was a startup that I founded with two other people in March, 2006. It died for many reasons, including disagreements between the founders, changes in the target market, and changes in the economy as a whole. I never talked about it directly on this blog, but here's a summary of what happened in its three year history:&lt;/p&gt;

&lt;h3&gt;March 2006&lt;/h3&gt;

&lt;p&gt;We (Nick Barendt, Michael Branicky, and I) decide to build an online backup product. We're unimpressed with the existing solutions – we want something that can be installed and forgotten about. I'll do most of the development, Nick will do contracting with other companies to get us going, and Mike, busy enough as a tenured professor, will be in an advisory role.&lt;/p&gt;

&lt;p&gt;We begin building a "test app" to analyze data churn rates on peoples' hard drives. This is a sort of feasability study, as well as a source of data to help us create the product.  I'm still finishing undergrad, so I'm only working part-time.&lt;/p&gt;

&lt;h3&gt;June 2006 (3 months after founding)&lt;/h3&gt;

&lt;p&gt;I finish undergrad. We realize that building the test app isn't worth the time: once we get it working, we'd need to get it installed on a lot of machines to make it worth the effort. We aren't confident that we can convince random people to install an application that doesn't help them in any way. We'd rather just build a simple, first-draft backup app. The test app did have some value as a learning experience, though – it had to do a lot of the crawling and hashing that BitBacker would eventually need.&lt;/p&gt;

&lt;p&gt;With the test app retired, we start work on BitBacker itself immediately.&lt;/p&gt;

&lt;h3&gt;August 2006 (5 months after founding)&lt;/h3&gt;

&lt;p&gt;Apple announces that Mac OS X Leopard will include Time Machine, a system for doing transparent backups to an external USB drive. This is scary, but we decide to postpone our worrying until it's released. Besides, Leopard is a year off, and we'll surely have a product out by then.&lt;/p&gt;

&lt;h3&gt;Late 2006 to early 2007 (about 9 months after founding)&lt;/h3&gt;

&lt;p&gt;I learn how to do backups well, mostly by trial and error. A lot of infrastructure shows up around the concept of a snapshot, which captures the state of the entire file system at a single point in time. BitBacker supports many snapshots, so you can step back in time and see what your folders and files looked like yesterday, or two weeks ago, or last January (much like Time Machine, but without the fancy UI).&lt;/p&gt;

&lt;p&gt;At this point, BitBacker only has a web interface. You run a little app that has the web server in it, then point your browser at that local server. We realize that this isn't going to fly in the long term, but haven't decided what to do about it yet.&lt;/p&gt;

&lt;h3&gt;Spring 2007 (one year after founding)&lt;/h3&gt;

&lt;p&gt;We decide what we're going to do about the UI. I spend a couple weeks learning Cocoa and rebuild the whole client UI in it . At this point, we're committed to supporting only Macs, at least for the initial release.  Cocoa makes me realize that, contrary to past experience, building a GUI doesn't have to be excruciating. This is a nice surprise. The early versions of BitBacker are rather silly, but that's because I'm still learning to think like a person who hasn't spent thousands of hours with this software already.&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/early-bb-ui.png" alt="Early BitBacker UI" /&gt;&lt;/p&gt;

&lt;p&gt;(Early BitBacker UI)&lt;/p&gt;

&lt;p&gt;The first alpha is released. It runs live across the internet, but is only used by the founders. Everything is written in Python, including the entire Cocoa GUI.&lt;/p&gt;

&lt;h3&gt;July 2007 (one year, four months after founding)&lt;/h3&gt;

&lt;p&gt;We get a real, live office at 1677 E 40th St, Cleveland, OH. The BitBacker sign is still hanging outside the building today. We suddenly own a lot of Ikea furniture.&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/office.jpg" alt="Sign outside the BitBacker office" /&gt;&lt;/p&gt;

&lt;p&gt;Sign outside the BitBacker office&lt;/p&gt;

&lt;p&gt;Until now, we've been working from our homes and various coffee shops.  Having an actual, physical office raises expectations, as well as our burn rate.&lt;/p&gt;

&lt;h3&gt;Summer and Fall 2007 (about 1.5 years after founding)&lt;/h3&gt;

&lt;p&gt;The private beta begins. More users are added and I do a lot of technical waffling – having external users has made me afraid of making serious changes.  The few problems that are found by users cripple my progress as I agonize over whether I've actually fixed them. Commits happen just as often as ever, but they aren't making many user-visible changes.&lt;/p&gt;

&lt;p&gt;At this point in the project, I'm unhappy with the state of the code. I'm more disciplined than I was when we started the company in 2006, and I spend much of this period bringing the code up to my new standards.&lt;/p&gt;

&lt;h3&gt;October 2007 (1 year, 7 months after founding)&lt;/h3&gt;

&lt;p&gt;Mac OS X Leopard is released, including Time Machine, and we still don't have a released product.  Because Time Machine requires a physical external drive, it achieves limited market penetration. We are relieved.  However, the online backup offerings available for Mac OS X are improving.  Mozy is getting quite popular, which is scary. Surely we will have a release soon!&lt;/p&gt;

&lt;h3&gt;January 2008 (1 year, 10 months after founding)&lt;/h3&gt;

&lt;p&gt;I take over as the organizer of &lt;a href="http://groups.google.com/group/clepy"&gt;clepy&lt;/a&gt;, the Cleveland Python user group, and BitBacker hosts it for the next fifteen months consecutively. Beer is now allowed at clepy meetings, which seems to improve attendance.&lt;/p&gt;

&lt;h3&gt;February 2008 (1 year, 11 months after founding)&lt;/h3&gt;

&lt;p&gt;We remove support for multiple machines being backed up with one account.  This is a major turning point for me – I'm learning to throw huge chunks of a system away if they don't help the user.&lt;/p&gt;

&lt;h3&gt;March 2008 (2 years after founding)&lt;/h3&gt;

&lt;p&gt;We finally add the ability to make asynchronous S3 requests. Before this, the server had to make one S3 request at a time. When a snapshot is completed, the server potentially needs to make thousands of S3 requests quickly, so this is very important for our performance.&lt;/p&gt;

&lt;p&gt;Python doesn't have an asynchronous HTTP library (&lt;a href="http://twistedmatrix.com"&gt;Twisted&lt;/a&gt; doesn't count, at least as of March 2008).  I write my own by hacking subclasses of httplib.HTTPConnection and httplib.HTTPResponse to use fake sockets (subclasses of [StringIO])(http://docs.python.org/library/stringio.html)) instead of real ones. I use [asyncore])(http://docs.python.org/library/asyncore.html) to do the asynchronous communication, with the hacked httplib subclasses doing their HTTP work before and after the actual communication.&lt;/p&gt;

&lt;p&gt;We release version 1.3.0, doubling our user base.&lt;/p&gt;

&lt;h3&gt;May 2008 (2 years, 2 months after founding)&lt;/h3&gt;

&lt;p&gt;One of our users backs up around 100 GB, roughly five times more than we'd ever tested up to this point. The client application has some performance problems, and the server takes so long that the client times out (both fixed in later versions), but the backups do complete successfully.&lt;/p&gt;

&lt;h3&gt;August 2008 (2 years, 5 months after founding)&lt;/h3&gt;

&lt;p&gt;We finally port to Leopard, which has a totally different Python-to-Objective-C bridge. The previous version of PyObjC builds apps that work in Leopard, but those apps can only be built on a development machine running Tiger. This means that I'm still using Tiger in August 2008, a year after Leopard was released.  Porting to the new version of PyObjC takes about a week and is quite painful. After we finish, I'm afraid that we might've missed some corner cases. I'm really hurting for a comprehensive suite of automated full-stack tests.&lt;/p&gt;

&lt;h3&gt;September 2008 (2 years, 6 months after founding)&lt;/h3&gt;

&lt;p&gt;Until this point, I've been doing huge manual tests before release. Unit and system tests have been automated since the beginning of the project, but there have never been automated tests that actually drive the UI. I begin writing end-to-end ("acceptance") tests using &lt;a href="http://appscript.sourceforge.net/"&gt;appscript&lt;/a&gt;, which is a Python interface to the event system underlying AppleScript. These tests start the servers, build the app, launch it, and use it as the user would – clicking buttons, selecting menu items, etc. This reduces my manual test process from eight hours to one.  It takes about a week and a half to implement all of the tests.&lt;/p&gt;

&lt;h3&gt;Fall 2008 (about 2.5 years after founding)&lt;/h3&gt;

&lt;p&gt;We release versions 1.4.0 and 1.4.1, which move BitBacker into the OS X menu bar. Before this, it was a normal application, showing up in the Dock and the Cmd-Tab list. At this point, I realize that building the whole client as a single application was a terrible idea due to the way that OS X "UI Element" applications work, but I can't justify taking the time to fix it. These versions also add the ability to exclude folders from backups and run at startup. We're feeling pretty comfortable about charging money for the app now.&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/final-bb-ui.png" alt="Final BitBacker UI (in menu bar)" /&gt;&lt;/p&gt;

&lt;p&gt;Final BitBacker UI (in menu bar)&lt;/p&gt;

&lt;h3&gt;November 2008 (2 years, 8 months after founding)&lt;/h3&gt;

&lt;p&gt;We start looking at subscription payment processing options, quickly realizing that it's much more complex than we realized. We waffle on it.&lt;/p&gt;

&lt;h3&gt;December 2008 (2 years, 9 months after founding)&lt;/h3&gt;

&lt;p&gt;A beta user's hard drive dies. He uses BitBacker to restore 17 GB of lost data. It's the first time that a large restore has been done in the wild.  There is much rejoicing.&lt;/p&gt;

&lt;h3&gt;February 2009 (2 years, 11 months after founding)&lt;/h3&gt;

&lt;p&gt;The open beta begins – beta keys are no longer required for registration.  Storage quotas are added (all existing users get 250 GB; new users get 2 GB). Email address confirmation is added. Legal agreements are added. A few people we don't know sign up and try the app.&lt;/p&gt;

&lt;h3&gt;March 2, 2009 (3 years after founding)&lt;/h3&gt;

&lt;p&gt;I get sick for several days and, when I get better, I find that I'm disappointed.  This makes me realize that I'm not at all happy with my day-to-day life. I talk to Nick about it; we agree that we're both frustrated with the state of the company and disconcerted by the huge change in the competitive landscape since we started.  We decide to stop working on BitBacker immediately.  The last Clepy meeting at our office is on that night: March 2nd, 2009. We immediately begin selling our office furniture and hardware.&lt;/p&gt;

&lt;h3&gt;March 8, 2009 (3 years after founding)&lt;/h3&gt;

&lt;p&gt;We send an email to all existing beta users telling them that we're retiring the product and that the servers will go down on March 20th, 2009.&lt;/p&gt;

&lt;h3&gt;March 24, 2009 (3 years after founding)&lt;/h3&gt;

&lt;p&gt;The servers go down forever at 6:46 PM. Their uptime was 369 days. May they rest in peace... or, more likely, immediately be provisioned for some other Amazon EC2 customer. ;)&lt;/p&gt;

&lt;h3&gt;Lessons Learned&lt;/h3&gt;

&lt;p&gt;Here are the two biggest lessons I've learned from BitBacker:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Talk about equity early and often. Be frank about your expectations. Never say "we'll work it out later."&lt;/li&gt;
&lt;li&gt;Release as early as you can, even if it doesn't do much.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;This advice can be found in a thousand places, but it bears repeating. I failed to do these things even though I'd heard them so many times before.  Seriously: release early, even if the thing doesn't do much.  You'll always think it's not ready, but that's because you live with it every day. If it could possibly have any use to other people, &lt;em&gt;release it&lt;/em&gt;!&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Software Craftsmanship: Geographical Distribution</title>
        <link href="http://blog.extracheese.org/2009/06/software-craftsmanship-geographical-distribution.html"/>
        <updated>2009-06-23T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2009/06/software-craftsmanship-geographical-distribution</id>
        <content type="html">&lt;p&gt;I often compute statistics from publicly available data, tell myself I'll blog about them, and then never do. For once, I'm actually doing it!&lt;/p&gt;

&lt;p&gt;The graph below shows how many &lt;a href="http://manifesto.softwarecraftsmanship.org"&gt;software craftsmanship manifesto&lt;/a&gt; signatories US states have relative to their total population.  Only states with at least five signatories are included.&lt;/p&gt;

&lt;p&gt;I'm about to move from Cleveland to Seattle &amp;ndash; #4 to #1. Not bad!&lt;/p&gt;

&lt;p&gt;Have a look at the &lt;a href="/2009/06/craftsmanship-stats.py"&gt;source code&lt;/a&gt; if you'd like. (It was hacked together; please judge it gently.)&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/craftsmanship-distribution.png" alt="software craftsmanship distribution" /&gt;&lt;/p&gt;

&lt;p&gt;UPDATE: In the comments, Joel Helbling suggested a Google Charts map. Here's one with states colored from red to green for lowest to highest signatories per million residents (&lt;a href="http://chart.apis.google.com/chart?cht=t&amp;amp;chtm=usa&amp;amp;chs=400x200&amp;amp;chco=ffffff,ff0000,00FF00&amp;amp;chld=WACOIAILOHMIMAMNUTTXNEPAWIOKVAORCTAZTNFLNCMDCAMONYGAINKYNJ&amp;amp;chd=t:100.0,88.3933237452,81.2610253731,80.6236322365,77.1442125485,62.9023916643,55.3350362068,49.1978886887,46.9284573088,45.9251760908,43.2030456545,41.263907039,38.789755993,38.781925391,38.0169619352,37.2705900564,33.009489513,31.6092555868,30.9939994565,30.8282741479,29.2411433071,22.7947005905,22.7089426339,21.7227228245,19.7661672671,18.5615704491,18.1242451026,18.0476159409,10.3529678061"&gt;source&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/craftsmanship-distribution-map.png" alt="craftsmanship distribution" /&gt;&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Dingus Screencast: A mock/stub library with automatic isolation</title>
        <link href="http://blog.extracheese.org/2009/04/dingus-screencast-a-mock-stub-library-with-automatic-isolation.html"/>
        <updated>2009-04-01T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2009/04/dingus-screencast-a-mock-stub-library-with-automatic-isolation</id>
        <content type="html">&lt;p&gt;Dingus is a mocking/stubbing library I've been working on for about a year. It grew out of a now-defunct project's test suite, and I've used it in about 3,500 lines of unit test code. It does two things that are pretty novel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A dingus allows you to do almost anything to it, including nesting accesses arbitrarily deep. If you have a dingus &lt;code&gt;d&lt;/code&gt; you can say &lt;code&gt;99 * (d.foo.bar.baz() ** 'hello')[15]&lt;/code&gt; and you'll just get another dingus out. This lets you use dinguses to replace dependencies in legacy code without thinking about what interface they must conform to.&lt;/li&gt;
&lt;li&gt;If you want it to, Dingus can automatically replace your dependencies with dinguses. You just tell it what class you're testing, and it will replace everything else in that class's module with a Dingus. This fully isolates your code under test without requiring any work from you.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;The second point above is probably a bit hard to think about from such a short description, so I've created a screencast to show it off. I TDD up a bit of code in the screencast, but it is &lt;em&gt;not&lt;/em&gt; intended to be a good example of TDD or test design. I use a lot of stubbing and interaction assertions because I'm trying to show off Dingus' features.  In real-world code, you should avoid assertions about object interaction as much as you can.&lt;/p&gt;

&lt;p&gt;If you'd like to watch the screencast, it's &lt;a href="http://vimeo.com/3949077"&gt;available&lt;/a&gt; on Vimeo, but the version on the page is low quality. If you log in to Vimeo, there's a download link in the bottom right of the page to see it at full quality.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>From Twitter: Version Control</title>
        <link href="http://blog.extracheese.org/2009/02/from-twitter-version-control.html"/>
        <updated>2009-02-25T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2009/02/from-twitter-version-control</id>
        <content type="html">&lt;p&gt;This blog has been quiet lately, due at least partly to Twitter. In an effort to get it going again, I'd like to start posting some summaries of my Twitter output. This will &lt;em&gt;not&lt;/em&gt; be a daily Twitter bridge or anything like that. These are hand-selected from my entire tweet history. This first batch is on version control.&lt;/p&gt;

&lt;p&gt;All links have been inserted after the fact, but everything else remains unchanged and in roughly chronological order. Indented entries are continuations of the thoughts in their parents.&lt;/p&gt;

&lt;p&gt;For more of my ranting, you can &lt;a href="http://twitter.com/garybernhardt"&gt;follow me&lt;/a&gt; on Twitter.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To everyone who lost power due to the storm: you are now advocates of distributed version control and distributed ticketing. ;)&lt;/li&gt;
&lt;li&gt;Recommending git as an intro to DSCM is like recommending C++ as an intro to OO. Just thought I'd throw that out there...

&lt;ul&gt;
&lt;li&gt;Good OO &lt;em&gt;can&lt;/em&gt; be done in C++, but it takes a lot of learning and is prone to error. The same goes for DSCM and Git. ;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The way most people use version control is downright offensive. Trailing whitespace changes in diffs? Seriously? Grow some discipline.&lt;/li&gt;
&lt;li&gt;Workflow using patch queues: Spike a feature (patch 1), then replace the spiked classes with TDDed ones (patches 2..n), then fold patches.&lt;/li&gt;
&lt;li&gt;That workflow gives you: easy spike-to-TDD transition; everything nicely versioned; a single changeset at the end; no history rewriting.&lt;/li&gt;
&lt;li&gt;Git was designed by insane space aliens. Whether this is a good or bad thing is a personal preference.&lt;/li&gt;
&lt;li&gt;One day I will write an editor-VCS that stores all files as the list of vim commands originally used to create them. &amp;lt;0.95 ;)&amp;gt;&lt;/li&gt;
&lt;li&gt;The funny thing about rebase, patch queues, and multiple heads: Once you truly understand one of them, you understand all of them.&lt;/li&gt;
&lt;li&gt;Using a DVCS has made me worry a lot about repo size, which I shouldn't have to worry about. I never would've expected this problem.&lt;/li&gt;
&lt;li&gt;Wish list: Fancy VCS: When I refactor a test, automatically check that the new one would've failed at the point where I originally TDDed it.&lt;/li&gt;
&lt;li&gt;"This is more complex than OpenGL!" - &lt;a href="http://twitter.com/jleedev"&gt;@jleedev&lt;/a&gt;, about five words into my explanation of Mercurial patch queues.&lt;/li&gt;
&lt;/ul&gt;

</content>
    </entry>
    
    <entry>
        <title>Processes spawn faster than threads?</title>
        <link href="http://blog.extracheese.org/2008/05/processes-spawn-faster-than-threads.html"/>
        <updated>2008-05-30T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2008/05/processes-spawn-faster-than-threads</id>
        <content type="html">&lt;p&gt;In general, processes take longer to start than threads.  This makes sense if you think about it &amp;ndash; a thread lives within the memory space of its parent process, so it takes less work to set one up.  (This is a gross oversimplification, but to be honest I find the details of process management incredibly uninteresting in 2008.)  I assumed that this difference would hold for the Python processing module.  Apparently it doesn't, at least on Mac OS X.  Surprise!&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;Spawning&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="n"&gt;children&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;Thread&lt;/span&gt; &lt;span class="n"&gt;took&lt;/span&gt; &lt;span class="mf"&gt;1.04&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;Spawning&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="n"&gt;children&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;Process&lt;/span&gt; &lt;span class="n"&gt;took&lt;/span&gt; &lt;span class="mf"&gt;0.60&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The above result is for starting and joining the children serially.  I get the same results in all of these variations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Starting them all at once, then joining them all at once.&lt;/li&gt;
&lt;li&gt;Using 10 children or 1,000 children.&lt;/li&gt;
&lt;li&gt;Having each child sleep for one second (to ensure that they're all actually alive at the same time).&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I don't know whether this is due to goodness in OS X, or processing, or fork(), or just Unix in general.  In any case, it's very good news.  I'd dismissed processing for use on the client side of &lt;a href="http://bitbacker.com"&gt;BitBacker&lt;/a&gt; because "process management is hard and they're too heavyweight."  Clearly at least one of those complaints is invalid; maybe the other is as well.  It would be a wonderful relief if I could use processes.  I'm going to need parallelization of one form or another soon, and I'm definitely not going to start sprinkling threads around.  Only madness lies down that path.&lt;/p&gt;&lt;/p&gt;

&lt;p&gt;Here's the code that generated those results, in case you're interested:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;threading&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;processing&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;threading&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;processing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Spawning 100 children with &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; took &lt;/span&gt;&lt;span class="si"&gt;%.2f&lt;/span&gt;&lt;span class="s"&gt;s&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;



</content>
    </entry>
    
    <entry>
        <title>Shell Meme Wins</title>
        <link href="http://blog.extracheese.org/2008/04/shell-meme-wins.html"/>
        <updated>2008-04-11T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2008/04/shell-meme-wins</id>
        <content type="html">&lt;p&gt;My most common shell commands:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;171 hg
144 fg
77 rm
71 ls
38 cd
28 vi
24 nosetests
17 killall
15 tissue
15 python
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I tend to keep Vim open for a long time, running many commands from within it.  That's why I don't have lots of task switching like &lt;a href="http://exilejedi.livejournal.com/198371.html"&gt;Mike&lt;/a&gt;.  I learned Emacs before Vim, so blame it on that.  I also usually run tests from within Vim; otherwise nosetests would definitely be #1.&lt;/p&gt;

&lt;p&gt;Tissue is a ticketing system I've been working on.  It's super simple &amp;ndash; the whole ticket database is stored in a single plaintext file.  The idea is to fit in with DVCSes like Mercurial better.  Having a single monolithic Trac instance breaks down when you have dozens of repositories, each of which may have certain tickets fixed or not.  By storing the ticket database in a plaintext file within the repository, you get (1) explicit ties between code fixes and ticket changes, and (2) free merging of modified tickets when the corresponding code merge happens.  I'm about to switch &lt;a href="http://bitbacker.com"&gt;BitBacker&lt;/a&gt; from Trac to this, so it will hopefully get released some time.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Human-Readable Encryption Keys</title>
        <link href="http://blog.extracheese.org/2007/12/human-readable-encryption-keys.html"/>
        <updated>2007-12-28T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2007/12/human-readable-encryption-keys</id>
        <content type="html">&lt;p&gt;For &lt;a href="http://bitbacker.com"&gt;BitBacker&lt;/a&gt;, we use 128-bit AES encryption, which means our keys are really long and annoying &amp;ndash; 32 characters long when printed in hex.  And not only do the users sometimes have to type them in, but they have to write them down on paper.  (We can't store the key on our servers because then we'd be able to read the user's files; and we obviously can't trust it to their hard drive because that's what we're backing up.)&lt;/p&gt;

&lt;p&gt;Somehow, we have to present these random 128-bit keys to the user, and I think I've found a pretty good way.  We use RFC 1751, which defines a "Convention for Human-Readable 128-bit Keys" &amp;ndash;  basically just a mapping of blocks of bits to strings of English words.  Here's an example in Python using the RFC 1751 module in &lt;a href="http://www.amk.ca/python/code/crypto"&gt;PyCrypto&lt;/a&gt;:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urandom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Generate 16 random bytes (128 bits)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bin_to_hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Show the key in hex (32 characters)&lt;/span&gt;
&lt;span class="s"&gt;&amp;#39;61aa60e43a5e7fdb4b86a4897b52a0dc&amp;#39;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RFC1751&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key_to_english&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="c"&gt;# Show the pass phrase version of the key&lt;/span&gt;
&lt;span class="s"&gt;&amp;#39;BUSY BARN RUB DOLE TAUT TOOK ALTO PRY KIT WALL MUG CURT&amp;#39;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;# The transformation is always reversible&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bin_to_hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RFC1751&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;english_to_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="s"&gt;&amp;#39;61aa60e43a5e7fdb4b86a4897b52a0dc&amp;#39;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The keys are still &lt;em&gt;very&lt;/em&gt; long, of course, and this is unavoidable for our application.  But when translated to words, I think it's easier to write them down or type them in without making a mistake.  The image below shows BitBacker giving me a pass phrase.  (This feature hasn't even gone into beta yet &amp;ndash; it's little more than a mockup.  So please don't judge it too harshly!)&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/BitBacker-pass-phrase.png" alt="Screen shot of BitBacker's pass phrase handling" /&gt;&lt;/p&gt;

&lt;p&gt;When the user clicks "Continue" here, BitBacker actually makes him re-enter the generated pass phrase he wrote down.  To be honest, BitBacker's pass phrase handling is quite annoying.  But that's a heck of a lot better than losing your pass phrase, which would make your backups inaccessible!  This is the one place in all of BitBacker that isn't optimized for "least user annoyance".  Encryption keys are just &lt;em&gt;way&lt;/em&gt; too important to mess around with, and I think that most existing software is far too lax with them (including BitBacker's competitors).&lt;/p&gt;

&lt;p&gt;(This was derived from a comment I left on Jeff Atwood's "&lt;a href="http://www.codinghorror.com/blog/archives/001021.html?r=19656"&gt;Software Registration Keys&lt;/a&gt;" post.)&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>My blog woes have been soothed</title>
        <link href="http://blog.extracheese.org/2007/12/my-blog-woes-have-been-soothed.html"/>
        <updated>2007-12-22T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2007/12/my-blog-woes-have-been-soothed</id>
        <content type="html">&lt;p&gt;It seems I've mostly solved my &lt;a href="/2007/12/blog-woes.html"&gt;blog woes&lt;/a&gt;.  I got some quite helpful replies (still visible on &lt;a href="https://www.blogger.com/comment.g?blogID=6753084968628985846&amp;amp;amp;postID=908827919673100974"&gt;Blogger&lt;/a&gt;, although the comments didn't come over to my new blog).  I also got emails from &lt;a href="http://www.bluesock.org/~willg/"&gt;Will Guaraldi&lt;/a&gt; about &lt;a href="http://pyblosxom.sourceforge.net/"&gt;PyBlosxom&lt;/a&gt;, and from &lt;a href="http://www.daltonlp.com/"&gt;Lloyd Dalton&lt;/a&gt; about &lt;a href="http://www.daltonlp.com/blog_my"&gt;blog_my&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I took at least a brief look at each system mentioned in the comments and emails, but I decided on PyBlosxom.  If you're reading this in a web browser, what you're seeing is PyBlosxom rendering a theme I ported from Tumblr, with all of my old Blogger blog's content imported.  Quite frankensteinian indeed, as far as blogs go.&lt;/p&gt;

&lt;p&gt;It turns out that my impression of PyBlosxom's size when I wrote my "blog woes" post was a bit off &amp;ndash; I didn't realize just how little functionality resides in the core.  It's pretty slim, but with a decent selection of plugins.  I only needed &lt;a href="http://pyblosxom.sourceforge.net/blog/registry/meta/tags"&gt;tags&lt;/a&gt;, &lt;a href="http://pyblosxom.sourceforge.net/blog/registry/archives/wbgarchives"&gt;wbgarchives&lt;/a&gt;, and &lt;a href="http://pyblosxom.sourceforge.net/blog/registry/date/metadate"&gt;metadate&lt;/a&gt;, but there are plenty more for those who want more features.  With the tag and metadate plugins, I managed to keep my blog posts in almost exactly the format I've always used, so that was nice.&lt;/p&gt;

&lt;p&gt;PyBlosxom nicely solves my biggest concern, which I didn't explicitly state in my original post: I want to keep all of the files related to my blog in a &lt;a href="http://www.selenic.com/mercurial"&gt;Mercurial&lt;/a&gt; repository.  I've succeeded in that &amp;ndash; my entire blog is in Mercurial now.  That includes configuration files, the .htaccess file, the template, the entries, and even the queue of unfinished entries.  If I ever need to, I should be able to move the blog to another host in a matter of minutes.  Not that I ever intend to leave &lt;a href="http://www.webfaction.com/?affiliate=grb"&gt;WebFaction&lt;/a&gt; (note: that's an affiliate link), which is where it's happily hosted now.&lt;/p&gt;

&lt;p&gt;With that all out of the way, hopefully I can quit the detestable practice of metablogging, which I'd managed to avoid for my entire first year.  Thanks to everyone who made a suggestion, and special thanks to the PyBlosxom developers.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Blog woes</title>
        <link href="http://blog.extracheese.org/2007/12/blog-woes.html"/>
        <updated>2007-12-19T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2007/12/blog-woes</id>
        <content type="html">&lt;p&gt;Last night, I switched my blog to tumblr; this morning, I switched it back to Blogger.  Over the last two days, I've spent a ridiculous amount of time and effort trying to make the switch without breaking any links.  I'll spare the details, but it involved a lot of mod_rewrite, a PHP script written by &lt;a href="http://henrik.nyh.se/2007/06/tumblr-in-a-subdirectory"&gt;Henrik Nyh&lt;/a&gt; that proxies all requests to tumblr, and a huge list of URLs mapping the old Blogger ones to the new tumblr ones.&lt;/p&gt;

&lt;p&gt;I found the proxy-with-a-PHP-script thing distasteful, but the lack of decent tag support was the thing that ultimately made me give up.  I have a Python-specific feed that gets aggregated by &lt;a href="http://www.planetpython.org"&gt;the unofficial planet python&lt;/a&gt;, so whatever I switch to needs to be able to generate tag-specific feeds.  Tumblr does let you add tags to your posts, but it doesn't seem to actually do anything with them.&lt;/p&gt;

&lt;p&gt;So, I'm in search of blogging software.  I want something that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is written in Python or Ruby.  Python because I know it very well; Ruby because I want to know it better.&lt;/li&gt;
&lt;li&gt;Is simple.  I'm not interested in anything built in Django or TurboGears or Rails.  Preferably, it would be something that runs on my local machine, generating the static files that make up the live blog.&lt;/li&gt;
&lt;li&gt;Allows custom URLs but has sane defaults.  (I.e., no exposed serial integer keys.)&lt;/li&gt;
&lt;li&gt;Supports tags and can generate subfeeds based on them, as well as human-readable post lists that are filtered by them.  (For example, with my current blog you can look at &lt;a href="/search/label/python"&gt;only Python posts&lt;/a&gt; if you want to.)&lt;/li&gt;
&lt;li&gt;Does not involve a database.  (Not even SQLite.)&lt;/li&gt;
&lt;li&gt;Reads the posts out of plain HTML files, which I will write by hand.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I've looked around for something like this, but everything seems to be either big (e.g., Pyblosxom and Typo) or someone's weekend project that never got touched again.  I'm sure that the bigger ones are quite good at what they do, but I just want something that takes my plaintext files and generates an appropriate URL structure.  It can do it offline or online &amp;nbsp; I don't care &amp;nbsp; but it's got to be simple and require no complicated installation or configuration.&lt;/p&gt;

&lt;p&gt;So, any ideas, or am I starting a new project?&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Tubes by proxy</title>
        <link href="http://blog.extracheese.org/2007/10/tubes-by-proxy.html"/>
        <updated>2007-10-09T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2007/10/tubes-by-proxy</id>
        <content type="html">&lt;p&gt;On Friday, I am bored and want video games.  But no Internet service!  How to download new games to Wii?!&lt;/p&gt;

&lt;p&gt;Assets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One &lt;a href="http://en.wikipedia.org/wiki/Vx8300"&gt;VX8300&lt;/a&gt; cellular telephone.&lt;/li&gt;
&lt;li&gt;One Macintosh Book Professional.&lt;/li&gt;
&lt;li&gt;One Nintendo Wii.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Solution:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Connect VX8300 to MacBook via Bluetooth.&lt;/li&gt;
&lt;li&gt;Configure MacBook to use VX8300 &lt;a href="http://www.appleology.com/2006/09/27/setup-bluetooth-dun-with-your-verizon-mobile-and-your-mac/"&gt;as a modem&lt;/a&gt; for free.  This is an undocumented Verizon feature!&lt;/li&gt;
&lt;li&gt;Put MacBook in ad-hoc wireless mode.&lt;/li&gt;
&lt;li&gt;Enable internet sharing on MacBook.&lt;/li&gt;
&lt;li&gt;Inform Wii of wireless network.&lt;/li&gt;
&lt;li&gt;Access Wii Store!&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Full data path: Internet -&gt; Verizon Wireless -&gt; CDMA 2000 (1x) -&gt; VX8300 -&gt; Bluetooth -&gt; MacBook -&gt; 802.11g -&gt; Wii.&lt;/p&gt;

&lt;p&gt;Operation was a success.  &lt;a href="http://www.gamefaqs.com/console/snes/game/563530.html"&gt;Breath of Fire II&lt;/a&gt; acquired.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Undeletable zombie files on OS X</title>
        <link href="http://blog.extracheese.org/2007/09/undeletable-zombie-files-on-os-x.html"/>
        <updated>2007-09-13T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2007/09/undeletable-zombie-files-on-os-x</id>
        <content type="html">&lt;p&gt;Somehow, one of &lt;a href="http://www.bitbacker.com"&gt;BitBacker&lt;/a&gt;'s system tests occasionally creates an undeletable file.  It happens within a test that generates random filenames, then tries to back up and restore them.  Unfortunately, there are apparently some character sequences that make HFS+ choke.  I've only had this happen twice ever &amp;ndash; once on my 17" MacBook Pro, and just now on my brand new 15" MacBook Pro.  Here's what the file looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/7135305@N02/1374499748/"&gt;&lt;img src="http://farm2.static.flickr.com/1275/1374499748_b5f55a07d6_o.png" alt="A broken file" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It can't be deleted from Finder:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/7135305@N02/1374499728/"&gt;&lt;img src="http://farm2.static.flickr.com/1399/1374499728_73f476e9d5_o.png" alt="Trying to delete a broken file from Finder" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It can't be deleted from the console:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/7135305@N02/1374499742/"&gt;&lt;img src="http://farm2.static.flickr.com/1397/1374499742_05f2be7c24_o.png" alt="Trying to delete a broken file from the console" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And it can't even be "ls"ed:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/7135305@N02/1374514026/"&gt;&lt;img src="http://farm2.static.flickr.com/1321/1374514026_0f614c527a_o.png" alt="Trying to ls a broken file" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If I manually move the containing folder to the trash and try to empty it, I get this, which is even more ridiculous than the rest:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/7135305@N02/1374529726/"&gt;&lt;img src="http://farm2.static.flickr.com/1125/1374529726_03b35b822f_o.png" alt="Trying to delete a broken file from the trash" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've googled for undeletable files on OS X, as well as for the specific error messages Finder throws, and tried every suggested fix I found.  And, of course, I've tried to delete the file from within Python:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; os.unlink(os.listdir('.')[0])
Traceback (most recent call last):
  File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
  OSError: [Errno 2] No such file or directory:
  '\\xe4\\xad\\xa9\\xe1\\x84\\x84\\xe1\\x85\\xaf\\[...truncated]'
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Dear Apple: please replace HFS+.  Seriously, it sucks.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Globals and cargo culting</title>
        <link href="http://blog.extracheese.org/2007/09/globals-and-cargo-culting.html"/>
        <updated>2007-09-07T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2007/09/globals-and-cargo-culting</id>
        <content type="html">&lt;p&gt;&lt;a href="http://blog.tplus1.com"&gt;Matt Wilson&lt;/a&gt; &lt;a href="http://blog.tplus1.com/index.php/2007/08/24/when-to-use-globals"&gt;wants&lt;/a&gt; a module's functions to log to one logger, but he can't change their interface and he doesn't want to use a global variable.  This is the kind of thing that decorators are very good at, for better or worse.  Here's a decorator that will do the job:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;with_logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_singleton_logger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;new_fn&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;And here's how to use it to define a function that gets a logger instance without the caller passing it in:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;@with_logger&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;x + y = &lt;/span&gt;&lt;span class="si"&gt;%i&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;WARNING&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;This seems to be exactly what Matt was looking for: there are no globals, a logger gets injected, and the function's interface hasn't changed.  But is it a good idea?&lt;/p&gt;

&lt;p&gt;No, it's a ridiculous idea!  It's just a reimplementation of global variables!  All I've done is come up with a complicated scheme for injecting a single logger instance into every function in the module.  But that's exactly what a global does!  This is something that programmers have done over and over again in the name of OO.  Everyone &lt;em&gt;wants&lt;/em&gt; globals, but they go to great lengths to hide it.&lt;/p&gt;

&lt;p&gt;Here are three possible ways to solve the original logger problem:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use a singleton, and have each function retrieve the instance that way.&lt;/li&gt;
&lt;li&gt;Use a decorator that injects the instance into the function's argument list each time.  In with_logger, I combined this with a singleton.&lt;/li&gt;
&lt;li&gt;Just use a global.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;If you choose (1) or (2), the joke's on you.  You're still using a global, but now you have &lt;a href="http://regex.info/blog/2006-09-15/247"&gt;two problems&lt;/a&gt;: global state &lt;em&gt;and&lt;/em&gt; a complicated method for managing it.  There's no need for that, because we already have a simple method for injecting instances into a module's functions: globals!&lt;/p&gt;

&lt;p&gt;Of course, sometimes singletons or decorators like with_logging make sense, but only when they actually &lt;em&gt;do something&lt;/em&gt;.  If all they do is allow multiple functions to access a single long-living instance, they're dangerous and needlessly complex.  In almost all cases, singleton and related techniques are nothing more than &lt;a href="http://en.wikipedia.org/wiki/Cargo_cult_programming"&gt;cargo cult programming&lt;/a&gt;.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>When JSON isn't JSON</title>
        <link href="http://blog.extracheese.org/2007/07/when-json-isnt-json.html"/>
        <updated>2007-07-23T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2007/07/when-json-isnt-json</id>
        <content type="html">&lt;p&gt;JSON is so simple that you can specify it on an index card, but we &lt;em&gt;still&lt;/em&gt; can't get it right.  For example, here's what happens when &lt;a href="http://undefined.org/python/"&gt;simplejson&lt;/a&gt; and &lt;a href="http://cheeseshop.python.org/pypi/python-cjson"&gt;python-cjson&lt;/a&gt; talk about slashes:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;# simplejson correctly decodes cjson&amp;#39;s data&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;simplejson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cjson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="c"&gt;# cjson fails to decode simplejson&amp;#39;s data&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;cjson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;simplejson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
\&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;In this case, the problem is that cjson doesn't handle backslashes correctly.  There are two ways to say "/" in JSON: "/" and "\/".  When encoding, simplejson always escapes slashes, but cjson never does:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;simplejson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;\/&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;cjson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The reverse is also true: simplejson knows how to decode "\/", but cjson decodes it incorrectly:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;simplejson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;quot;\/&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;cjson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;quot;\/&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
\&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;So there you go: simplejson and cjson don't interoperate.  This bit me when I tried to move BitBacker from simplejson to cjson for performance reasons.  The live alpha server had a few thousand records encoded with simplejson, all of which included slashes.  When I switched to cjson, everything broke because every "/foo/bar" entry in the database came back as "\/foo\/bar".&lt;/p&gt;

&lt;p&gt;As far as I'm concerned, this problem with JSON is actually an argument &lt;em&gt;for&lt;/em&gt; simple data formats like JSON.  If we can't get full interoperability between something as stupidly simple as JSON, how did anyone ever expect &lt;a href="http://www.innoq.com/resources/ws-standards-poster"&gt;WS-*&lt;/a&gt; to work?&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Adobe, master of the painful install</title>
        <link href="http://blog.extracheese.org/2007/05/adobe-master-of-the-painful-install.html"/>
        <updated>2007-05-27T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2007/05/adobe-master-of-the-painful-install</id>
        <content type="html">&lt;p&gt;After using Macs for about two years, I've become accustomed to the painless installation process there: open the dmg, drag the app to the Applications folder, and you're done.  Unfortunately, I recently had to install Acrobat Reader to electronically sign some insurance documents.  They have managed to come up with a downright pathological installation process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download AdbeRdr80_DLM_en_US_i386.dmg.&lt;/li&gt;
&lt;li&gt;Open the dmg.&lt;/li&gt;
&lt;li&gt;DownloadReader.pkg is inside; open it.&lt;/li&gt;
&lt;li&gt;Click through some stuff.&lt;/li&gt;
&lt;li&gt;Wait for the "Adobe Reader Download Manager" to download&lt;/li&gt;
&lt;li&gt;&lt;em&gt;another&lt;/em&gt; 23 MB dmg.&lt;/li&gt;
&lt;li&gt;The "Download Manager" has silently quit (what?), but the dmg is now mounted.  Open "Adobe Reader 8 Installer.app" and wait for it to finish.&lt;/li&gt;
&lt;li&gt;The "Installer" has silently quit (what?), but it must be done, because Acrobat is now running.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;This is amazing.  When I saw the very first pkg file, I was annoyed: I hate
installers and they're rare in the Mac world.  As I kept installing and
installing, it was almost surreal.  In the above list, there are three times
where something is being installed.  Working back from Acrobat itself,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;"Adobe Reader 8 Installer.app" installs "Adobe reader.app", so it's the installer.&lt;/li&gt;
&lt;li&gt;The "Download Manager" installs that, so it's the installer installer.&lt;/li&gt;
&lt;li&gt;DownloadReader.pkg installed that, so it's the installer installer installer.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;So, while most Mac programs have shed the archaic notion of an installer altogether, Adobe Acrobat Reader has a program that installs a program that installs a program that installs the software you actually need.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Texturing and programming</title>
        <link href="http://blog.extracheese.org/2007/05/texturing-and-programming.html"/>
        <updated>2007-05-22T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2007/05/texturing-and-programming</id>
        <content type="html">&lt;p&gt;Flayra just posted a &lt;a href="http://www.unknownworlds.com/blog/2007/05/natural_selection_2_texturing.html"&gt;video&lt;/a&gt; of one of the Natural Selection 2 artists texturing a 3D model.  I've done a bit of modeling and texturing (poorly, of course), so it's awesome to watch someone who's really good.  It also reminded me of how I work on &lt;a href="/2007/02/introducing-another-wildly-ambitious-database-project.html"&gt;RESTdb&lt;/a&gt;, though.  I always have my heavily modified &lt;a href="http://jeffwinkler.net/2006/04/27/keeping-your-nose-green/"&gt;nosy&lt;/a&gt; running in its own terminal window, and it gives me constant feedback on the effects of every little change I make.&lt;/p&gt;

&lt;p&gt;In the video, it's obvious that constant feedback is critical to the process.  If the artist tried to create the entire texture without seeing it applied to the mesh, the process would take much longer, yield poorer results, or probably both.  As far as I'm concerned, the same applies to programming: if you use a bit of TDD, along with a continuous test runner like nosy, the programming process becomes more organic and approachable.  It becomes easier to break the problem down into pieces that can be tackled easily, and you have a better sense of the scope of your changes.&lt;/p&gt;

&lt;p&gt;If you haven't tried nosy yet, do it!  It's very easy &amp;ndash; just download nosy.py, stick it in your project's directory, and run it.  It's only about 25 lines long, but it can have a huge impact on how you work.  If you're still not convinced, check out &lt;a href="http://jeffwinkler.net/"&gt;Jeff Winkler&lt;/a&gt;'s &lt;a href="http://showmedo.com/videos/video?name=UsingNoseyForPythonTesting_jeffW&amp;amp;fromSeriesID=7"&gt;screencast&lt;/a&gt;.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Are your tests lying to you?</title>
        <link href="http://blog.extracheese.org/2007/04/are-your-tests-lying-to-you.html"/>
        <updated>2007-04-07T00:00:00-07:00</updated>
        <id>http://blog.extracheese.org/2007/04/are-your-tests-lying-to-you</id>
        <content type="html">&lt;p&gt;If you've written a test for a module, and the module is changed in the future, there are three things that can happen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The test keeps passing because nothing is broken.  (Good.)&lt;/li&gt;
&lt;li&gt;The test fails because something is wrong.  (Great &amp;ndash; this is the test's job!)&lt;/li&gt;
&lt;li&gt;The test keeps passing, but it silently stops testing the thing it claims to (BAD, BAD, BAD!).&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Scenario 3 above is very dangerous, and it's a major problem in testing.  What you have in that situation is a lying test: it says "I'm testing feature x," but actually passes without doing so.  In other words, you have a test that no longer warns you if you break something.&lt;/p&gt;

&lt;p&gt;If you've not been bitten by this, it might not be an obvious problem.  To make it a little more clear, let's look at a toy example (in Python, of course!)  Here's a silly WebClient class and its test.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WebClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;An HTTP client that supports both SSL and plain connections&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;use_ssl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c"&gt;# Hand any request off to external functions&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;use_ssl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;get_with_ssl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;get_without_ssl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_web_client&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c"&gt;# Make sure everything works with normal HTTP&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected_data&lt;/span&gt; &lt;span class="c"&gt;#defined elsewhere&lt;/span&gt;

    &lt;span class="c"&gt;# Make sure everything works with SSL as well&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;use_ssl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected_data&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;This works fine &amp;ndash; the test passes and it tests what it claims to.  But what
happens if someone renames the use_ssl attribute later?&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WebClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;using_ssl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c"&gt;# Hand any request off to external functions&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;using_ssl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;get_with_ssl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;get_without_ssl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Take a look back at the test.  It's no longer testing what it claims to, because "use_ssl" no longer means anything to WebClient.  The test still passes, though &amp;ndash; it's just that neither of the two get() calls actually uses SSL.&lt;/p&gt;

&lt;p&gt;This is a serious problem &amp;ndash; you need to be able to trust your tests, but for all you know your tests are giving you false positives.  The question, then, is how can we detect this type of mistake?  Well, there is a simple method that will catch at least some of them.  What you need is a meta-test: a test that ensures that the tests aren't lying to you.  It's really not that bad; here's the pseudocode:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;suite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt; &lt;span class="n"&gt;isn&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;t an assertion:&lt;/span&gt;
        &lt;span class="n"&gt;remove&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;but&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;make&lt;/span&gt; &lt;span class="n"&gt;sure&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;fails&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Basically, this meta-test is ensuring that every line of code in the test is required: removing any line should cause the test to fail.  This sounds complicated, but it only has to be implemented once.  Once it exists as a &lt;a href="http://somethingaboutorange.com/mrl/projects/nose/"&gt;nose&lt;/a&gt; plugin, for example, you can use it without writing any extra code.&lt;/p&gt;

&lt;p&gt;Let's look at how this would affect the example.  Here's the testing code again:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_web_client&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c"&gt;# Make sure everything works with normal HTTP&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected_data&lt;/span&gt; &lt;span class="c"&gt;#defined elsewhere&lt;/span&gt;

    &lt;span class="c"&gt;# Make sure everything works with SSL as well&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;use_ssl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected_data&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The meta-test will step through, removing each relevant line and making sure that the test fails.  The only executable lines that aren't assertions are 3 and 7.  When it removes line 3, the test will fail because "client" won't be defined.  So that iteration of the meta-test passes.  When it removes line 7, &lt;em&gt;the test will still pass&lt;/em&gt;.  Because the test passes with a line removed, the meta-test will fail.  The meta-test has detected the fact that line 7 isn't necessary, which is a red flag that says "this test might lie to you later!"&lt;/p&gt;

&lt;p&gt;It's important to note that the meta-test will fail even when the test is working.  It really is a &lt;em&gt;meta&lt;/em&gt;-test: it's only testing the test.  This is a good thing.  It tells you when you've written a crappy test &amp;ndash; a test that isn't paying enough attention.&lt;/p&gt;

&lt;p&gt;Let's return to the example and try to fix it.  To make the meta-test pass again, the test could be changed to be more sensitive to WebClient's state:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_web_client&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c"&gt;# Make sure everything works with normal HTTP&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected_data&lt;/span&gt; &lt;span class="c"&gt;#defined elsewhere&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;use_ssl&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="c"&gt;# Make sure everything works with SSL as well&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;use_ssl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected_data&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;use_ssl&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Now the meta-test passes, and the original test_web_client is more resilient to silent failures.  If someone renames WebClient's use_ssl attribute, the test won't silently stop testing like it did before.  Instead, line 5 will raise an exception and the test will fail.&lt;/p&gt;

&lt;p&gt;Of course, this isn't foolproof.  If you added line 10 but not line 5, you wouldn't be doing yourself any good (figuring out why is left as an exercise for the reader :).  The meta-test would still pass, though, and you would still have a test that may lie to you in the future.  So this meta-testing method isn't a magic bullet that will force you to write good tests.  For a careful tester, though, it throws up a red flag for tests that might be susceptible to very subtle errors.&lt;/p&gt;

&lt;p&gt;(Nitpicker's corner: Yes, the problem in this test was caused by questionable design in WebClient itself.  Using an instance variable to control a class's behavior in this way is error-prone to begin with.  This testing problem also arises in much more subtle situations, though; I have the scars to prove it.)&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Zero to Slashdot in Three Days</title>
        <link href="http://blog.extracheese.org/2007/03/zero-to-slashdot-in-three-days.html"/>
        <updated>2007-03-06T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2007/03/zero-to-slashdot-in-three-days</id>
        <content type="html">&lt;h3&gt;The Genesis&lt;/h3&gt;

&lt;p&gt;A few days before PyCon, &lt;a href="http://blog.case.edu/bmb12/"&gt;Brian&lt;/a&gt; suggested that we build a web app in one night.  It took a little longer than that to polish it up, but we launched &lt;a href="http://www.sucks-rocks.com/"&gt;sucks-rocks.com&lt;/a&gt; on Tuesday.  Since then, it's had over 40,000 page views and been slashdotted (OK...  it was the Japanese Slashdot, but it's still &lt;em&gt;a&lt;/em&gt; Slashdot.)&lt;/p&gt;

&lt;p&gt;Sucks/rocks rates the terms you enter by doing web searches and counting results.  For example, if you search for "Windows sucks" using Google, you'll get many more results than for "Windows rocks".  The opposite is true for FreeBSD.  From this, we can infer that people probably like FreeBSD more than Windows.  The actual searches that are done by sucks/rocks are more complex than this, but they follow a similar pattern.&lt;/p&gt;

&lt;h3&gt;The Search Engine Arms Race&lt;/h3&gt;

&lt;p&gt;Once we started getting a lot of traffic, it was very hard for us to keep sucks/rocks going because we kept running out of searches.  Here are the search APIs we used, in the order that we added them:&lt;/p&gt;

&lt;p&gt;
    &lt;table&gt;
        &lt;tr&gt;
            &lt;td&gt;&lt;b&gt;Search Engine&lt;/b&gt;&lt;/th&gt;
            &lt;td&gt;&lt;b&gt;Queries/Day&lt;/b&gt;&lt;/td&gt;
            &lt;td&gt;&lt;b&gt;Interface&lt;/b&gt;&lt;/td&gt;
            &lt;td&gt;&lt;b&gt;Suckiness of Results&lt;/b&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Google&lt;/td&gt;
            &lt;td&gt;1,000&lt;/td&gt;
            &lt;td&gt;SOAP&lt;/td&gt;
            &lt;td&gt;Low&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Yahoo&lt;/td&gt;
            &lt;td&gt;5,000&lt;/td&gt;
            &lt;td&gt;REST&lt;/td&gt;
            &lt;td&gt;Pretty low&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;live.com&lt;/td&gt;
            &lt;td&gt;10,000&lt;/td&gt;
            &lt;td&gt;SOAP&lt;/td&gt;
            &lt;td&gt;IMMEASURABLY HIGH!&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/table&gt;
&lt;/p&gt;


&lt;p&gt;We started with Google, but ran out of queries before we even launched.  We then used Yahoo, but ran out when 100shiki.com &lt;a href="http://www.100shiki.com/archives/2007/03/sucksrocks.html"&gt;linked&lt;/a&gt; to us, forcing me to add support for live.com.  Unfortunately, live.com's search results are terrible. &lt;strong&gt;Terrible!&lt;/strong&gt; If you &lt;a href="http://www.sucks-rocks.com/"&gt;search&lt;/a&gt; sucks/rocks for "lord of the rings", you'll get a "?" back.  This means that the engine whose results are cached (which is live.com, of course) reported that there were 0 "total results available".  Great.&lt;/p&gt;

&lt;p&gt;Now we have a cache of almost 60,000 searches, most of which are from live.com.  Many of those are totally wrong, of course.  My next task is to add a background thread that slowly replaces all of the cached live.com results with Yahoo results.&lt;/p&gt;

&lt;h3&gt;The Code&lt;/h3&gt;

&lt;p&gt;Sucks/rocks runs on top of &lt;a href="http://webpy.org"&gt;web.py&lt;/a&gt;, but only uses it for URL dispatching.  &lt;a href="http://pythonpaste.org/"&gt;Paste&lt;/a&gt; does the HTTP serving, with &lt;a href="http://www.webfaction.com?affiliate=grb"&gt;WebFaction&lt;/a&gt;'s Apache instance on the front end (disclaimer: the WebFaction link is an affiliate link).  This simple setup handled about a million HTTP requests in four days, using less than 5% of the CPU almost all of the time (except when it was at the top of slashdot.jp).&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/7135305@N02/410706954/"&gt;&lt;img src="http://farm1.static.flickr.com/152/410706954_5c5a81ade5_o.png" alt="Traffic Statistics" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Easy Come, Easy Go&lt;/h3&gt;

&lt;p&gt;With our slashdotting over, We've gone from 10,000+ pageviews per day to about 1,000.  Slashdot giveth, and Slashdot taketh away.  That's OK, because I need some time to push all of the crappy live.com results out of the cache anyway.&lt;/p&gt;

&lt;p&gt;(Brian has also posted about sucks/rocks: &lt;a href="http://blog.case.edu/bmb12/2007/02/sucksrocks_does_it_suck"&gt;1&lt;/a&gt;, &lt;a href="http://blog.case.edu/bmb12/2007/03/japan_loves_sucksrocks"&gt;2&lt;/a&gt;.)&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>PyCon 2007: The Untold Stories</title>
        <link href="http://blog.extracheese.org/2007/02/pycon-2007-the-untold-stories.html"/>
        <updated>2007-02-27T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2007/02/pycon-2007-the-untold-stories</id>
        <content type="html">&lt;p&gt;Most of the PyCon &lt;a href="http://www.technorati.com/search/%22pycon+2007%22"&gt;posts&lt;/a&gt; are about the sessions, so here are some of the interesting things I did outside of the scheduled talks.  I have pictures for many of them thanks to &lt;a href="http://exilejedi.livejournal.com"&gt;Mike Pirnat&lt;/a&gt;'s diligent
photography.&lt;/p&gt;

&lt;h3&gt;Pagoda CMS&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://blog.case.edu/bmb12"&gt;Brian&lt;/a&gt;, &lt;a href="http://blog.case.edu/csh11"&gt;Chris&lt;/a&gt;, and &lt;a href="http://www.iancharnas.com"&gt;Ian&lt;/a&gt; demoed &lt;a href="http://www.pagodacms.org"&gt;Pagoda&lt;/a&gt;, their upcoming open-source CMS.  It's very user-centric, and they're spending a lot of effort on the user experience.  Even though I don't use CMSes, I'm excited about this project because I'm so sick of crappy UIs.  Peoples' responses seemed positive, but I think some people were disappointed that Pagoda takes the easy-to-use approach rather than the kitchen-sink approach.  That's ok; that's why we have Zope &amp;ndash; the kitchen sink is there for the taking!&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/mikepirnat/404128129/"&gt;&lt;img src="http://farm1.static.flickr.com/188/404128129_d23e46ed47_m.jpg" alt="Pagoda CMS Open Space" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Python Is Basically DOS, Right?&lt;/h3&gt;

&lt;p&gt;I headed up to my room to grab my hoodie, and on the way back I was in the elevator with a 40ish couple.  They asked me what this conference was about; I told them it was about Python, which is a programming language.  The guy asked me whether "that's anything like DOS".  It was kind of funny, but mostly just jarring.  After being in close quarters with lots of smart programmers for 2 days, it was weird to suddenly talk to someone whose computer experience apparently began and ended around 1990.&lt;/p&gt;

&lt;h3&gt;The Mysterious Ellipsis&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://www.traceback.org"&gt;Dave&lt;/a&gt;, &lt;a href="http://exilejedi.livejournal.com"&gt;Mike&lt;/a&gt;, and I were at the hotel's bar, and the topic of Python's ellipsis operator ("...") came up some how.  From the grammar in the &lt;a href="http://docs.python.org/ref/slicings.html"&gt;slicing docs&lt;/a&gt;, we could tell that the ellipsis could appear in slices, but we couldn't trick Python into taking it without throwing an exception.  I figured it out later &amp;ndash; in a slice, the "..." token is just translated into an "Ellipsis" object:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getitem__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;    
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nb"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="bp"&gt;Ellipsis&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;Ellipsis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Apparently, it's mostly used for numeric stuff like &lt;a href="http://numpy.scipy.org/"&gt;Numpy&lt;/a&gt;.  I definitely understand Python's slicing much better after that confusing night.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/mikepirnat/401971587"&gt;&lt;img src="http://farm1.static.flickr.com/165/401971587_2ec1d0a791_m.jpg" alt="Gary and Dave Hacking" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Mischief on The Open Space Board&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://blog.case.edu/bmb12/"&gt;Brian&lt;/a&gt; and &lt;a href="http://blog.case.edu/csh11/"&gt;Chris&lt;/a&gt; posted a "Python in The Adult Entertainment Industry" card on the open spaces board with my name on it.  It was up for about 20 minutes before Brian pointed it out to me and I took it down.  Hopefully, I escaped without too many prominent Python hackers associating me with pornography.&lt;/p&gt;

&lt;p&gt;I have to wonder whether anyone saw that card and was actually interested in going to the session.  Maybe Chris and Brian's silliness prompted an interesting discussion of Python and porn somewhere...&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/7135305@N02/405265587/"&gt;&lt;img src="http://farm1.static.flickr.com/139/405265587_35f9b3ce30_m.jpg" alt="Mischief on The Open Space Board" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;RESTDB&lt;/h3&gt;

&lt;p&gt;The open space I actually did lead was on "REST, Databases, and RESTful Databases" rather than pornography.  Unfortunately, I dove into explaining &lt;a href="/2007/02/introducing-another-wildly-ambitious-database-project.html"&gt;RESTDB&lt;/a&gt; right at the start.  It turned out that not everyone was familiar with REST, or convinced of its usefulness, or both.  So, we ended up talking about REST for the second half.  I think the session would've been more useful to everyone involved if we'd discussed REST first, then moved on to RESTful databases.  I'm not sure how much everyone else got out of it, but I learned a lot about how to explain what RESTDB is and why we might want it.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/mikepirnat/404139318/"&gt;&lt;img src="http://farm1.static.flickr.com/173/404139318_c77eff3d79_m.jpg" alt="RESTDB Open Space" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Django vs. The World&lt;/h3&gt;

&lt;p&gt;I didn't fly back until Monday, so I was still there on Sunday night.  Most of the people who were still there were staying for the sprints, so the conference area was pretty quiet as everyone quietly hacked away (with the exception of the Wii room).&lt;/p&gt;

&lt;p&gt;I was on my way back to the "quiet room", which was full of Django guys.  On my way there, a big group of people appeared and asked where the Django guys were.  I pointed them towards the quiet room and joined them on their way there.  The group was made up of &lt;a href="http://www.turbogears.org/"&gt;TurboGears&lt;/a&gt; guys, &lt;a href="http://pylonshq.com/"&gt;Pylons&lt;/a&gt; guys, &lt;a href="http://pythonpaste.org/"&gt;Paste&lt;/a&gt; guys, and some that I didn't recognize.  They busted into the Django room and caused some friendly commotion, with one notable result being &lt;a href="http://blog.ianbicking.org/python-packaging.html"&gt;this post&lt;/a&gt; on Ian Bicking's blog.  I'm pretty sure that EWT's bathtub full of alcohol (pictured) was a factor in this incident.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/mikepirnat/404132325/"&gt;&lt;img src="http://farm1.static.flickr.com/160/404132325_a547a518df_m.jpg" alt="Lots of Beer" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Magic URL Mapping&lt;/h3&gt;

&lt;p&gt;After the ruckus in the quiet room ended, I hacked up some crazy URL mapping code based on an &lt;a href="http://blog.case.edu/bmb12/2005/12/python_web_framework_experiments_day_1"&gt;experiment&lt;/a&gt; Brian did a while back.  Here's a controller defined using it:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;users&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&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 class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;email&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;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;name&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;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The &lt;em&gt;_/'users'/User&lt;/em&gt; part defines the controller's URL, and &lt;em&gt;User&lt;/em&gt; is actually a RESTDB resource type.  So, for example, if someone requests &lt;em&gt;/users/Bob&lt;/em&gt;, this controller will be invoked and the Bob RESTDB resource will automatically be retrieved and passed in.  This works for multiple records, so you could also have more complex controllers like:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BlogController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;users&amp;#39;&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="s"&gt;&amp;#39;blogs&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Blog&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&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 class="n"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;blog&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;user&lt;/span&gt; &lt;span class="c"&gt;# yep!&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;em&gt;BlogController&lt;/em&gt; would be called for URLs like &lt;em&gt;/users/Bob/blogs/TheBobBlog&lt;/em&gt; and, once again, both Bob and his blog would automatically be pulled out of the database.  Of course, it's fully RESTful (hence the &lt;em&gt;get&lt;/em&gt; method).&lt;/p&gt;

&lt;p&gt;Keep in mind that this is just a silly experiment; please don't freak out because I'm overloading division to produce a URL mapping object that I then subclass.  (Although, to be honest, the code isn't that bad; it's only about 60 lines long.)&lt;/p&gt;

&lt;p&gt;Overall, PyCon was awesome, and I'm really glad I went.  It's going to be in Chicago next year, so I won't have to lose two full days to travel (awesome!)&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Introducing Another Wildly Ambitious Database Project</title>
        <link href="http://blog.extracheese.org/2007/02/introducing-another-wildly-ambitious-database-project.html"/>
        <updated>2007-02-17T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2007/02/introducing-another-wildly-ambitious-database-project</id>
        <content type="html">&lt;p&gt;One week ago today, I started hacking on a new project: a database implemented as a RESTful HTTP service.  &lt;a href="http://blog.case.edu/bmb12/"&gt;Brian&lt;/a&gt; has been pestering me to post about it since before any code was even written, so here we are.&lt;/p&gt;

&lt;p&gt;I've been calling the project RESTDB, but that's only because I haven't
come up with a better name yet.  It's sort of relational, but not quite.
Depending on how you squint, you might think it is.  Likewise, it's not
completely RESTful: it lacks arbitrary POST semantics.  Despite these caveats,
it's quite similar to both RDBMSes and RESTful systems.  Let's have a
look.&lt;/p&gt;

&lt;h3&gt;Defining the Schema&lt;/h3&gt;

&lt;p&gt;We're going to build a multi-user TODO list (basically stolen from &lt;a href="http://exogen.case.edu/turbogears.html"&gt;Brian's TurboGears tutorial&lt;/a&gt;).  As with a traditional RDBMS, the first step is to define our schema:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;lists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;TodoList&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Item&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Obviously, this looks a lot like a &lt;a href="http://www.sqlobject.org/"&gt;SQLObject&lt;/a&gt; table definition.  One thing is very different, though: the way resources are related to each other.  In SQLObject, each TodoList would have a foreign key that points to its User.  In RESTDB, this is inverted: the User contains a list of links that point to TodoLists.  In SQL terms, you can think of this as a list of foreign keys.  This has many serious implications, both for the database's implementation and for how clients interact with it.  In the interest of brevity, I will valiantly gloss over every single one of them for now.&lt;/p&gt;

&lt;h3&gt;The Data&lt;/h3&gt;

&lt;p&gt;Now that we've defined our schema, let's see what's going on from an HTTP perspective.  The definition above will lead to a URL structure like this (with arbitrary example records inserted):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/User
/User/me@example.com
/TodoList
/TodoList/1
/Item
/Item/1
/Item/2
/Item/3
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;These resources' structures are dictated by the resource classes we defined above.  Resources are stored as simple &lt;a href="http://www.json.org/"&gt;JSON&lt;/a&gt; data, so they're human readable even in their raw form.  For example, here's what "/TodoList/1" might look like:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Groceries&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;items&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s"&gt;&amp;#39;/Item/1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;&amp;#39;/Item/2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;&amp;#39;/Item/3&amp;#39;&lt;/span&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;/pre&gt;
&lt;/div&gt;


&lt;p&gt;It's just plain old JSON data, but it follows our schema: there's an ID, a
title, and a list of item links.  You don't even need a database client
program to look at it; just point your web browser at the resource and you'll
get back the JSON representation.  You can download the whole database with
&lt;em&gt;wget&lt;/em&gt; if you want to.&lt;/p&gt;

&lt;h3&gt;The Client&lt;/h3&gt;

&lt;p&gt;Of course, this database isn't designed to be used by humans directly; human readability is just a nice bonus.  wgetting your database is a neat gimmick, but what we really care about manipulating it with code.&lt;/p&gt;

&lt;p&gt;To illustrate how simple the client is, here's the entire client-side definition for our todo list database:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;127.0.0.1:17321&amp;#39;&lt;/span&gt;&lt;span class="p"&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;Resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;User&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;TodoList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;TodoList&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Item&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;That's it: all you have to do is tell it the names of the resources.  Note that this doesn't mean that there aren't constraints on the data &amp;ndash; there are!  Lots of constraints &amp;ndash; all the constraints you care to define!  It's just that they're only on the server side.  If you step out of line, the server will slap you with an "HTTP 400: No Shenanigans Allowed".&lt;/p&gt;

&lt;p&gt;We'll get to the shenanigans in a minute.  First, let's try the client out by creating a user:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;me&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;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;me@example.com&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lists&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;
&lt;span class="s"&gt;u&amp;#39;me@example.com&amp;#39;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lists&lt;/span&gt;
&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;All we do is POST a new user resource with an email address and no todo lists.  This is literally just an HTTP POST to /User.  The database responds with an HTTP "Location" header to tell the client that the new resource is at "/User/me@example.com".&lt;/p&gt;

&lt;p&gt;Now that our user is securely fastened to the database, let's create a todo list with some items:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;# Create a todo list and assign it to the user&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TodoList&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Groceries&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lists&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;# Create some items and add them to the todo list&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Milk&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Eggs&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Bread&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;We POST a new TodoList, just like we POSTed a new user before.  Then we
have to update the user resource to point at the new list.  &lt;em&gt;me.lists&lt;/em&gt; is
just a plain old Python list, so we append the new todo list, then PUT
&lt;em&gt;me&lt;/em&gt; to update the server's copy.  We then repeat the same process to add
items to the todo list.&lt;/p&gt;

&lt;h3&gt;Light's Green; Trap's Clean&lt;/h3&gt;

&lt;p&gt;Now that we've trapped a bunch of data in our database, let's start from scratch and pull the todo list's items back:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;me&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;me@example.com&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lists&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;u&amp;#39;Milk&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;u&amp;#39;Eggs&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;u&amp;#39;Bread&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Awesome.&lt;/p&gt;

&lt;p&gt;But wait, I've conveniently left a loose end untied!  I claimed that shenanigans were strictly forbidden.  So far, we've been acting nice and giving the server exactly what it wants.  Now let's try to feed the server some crap:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;me&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;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;you@example.com&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lists&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BadRequestError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;123&amp;quot;&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;me&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;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;screw_you_server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;EXPLODE!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BadRequestError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Didn&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;t expect field &amp;quot;screw_you_server&amp;quot;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;It's having none of it!  It will snub your stupidly-formed data all day long.  And it's not just simple things like types that are enforced.  You can define regex constraints for your strings, ranges for your numbers, and whatever else you can dream up.  You can suffocate your precious data with constraints.  Your links are guaranteed to reference valid resources; your URLs are guaranteed to match your data; your data is guaranteed to match your schema.&lt;/p&gt;

&lt;p&gt;Everything I've shown here is real, working code.  A few of the things I've mentioned, like link validity constraints, aren't done yet, but they're coming.  Unfortunately, I can't point you at subversion just yet, because I don't have anywhere to host it.  That will hopefully change soon, and you'll be able to prod it for yourself.  For now, you'll have to make do with imagining how awesome it would be to speed up your database by sticking a plain old HTTP caching proxy in front of it.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Unicode Weirdness</title>
        <link href="http://blog.extracheese.org/2007/02/unicode-weirdness.html"/>
        <updated>2007-02-14T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2007/02/unicode-weirdness</id>
        <content type="html">&lt;p&gt;I'm writing some tests to verify that BitBacker doesn't explode if it sees unicode filenames.  For a while, I thought that OS X's terminal wasn't unicode-aware, because non-ASCII unicode characters just showed up as "?":&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;grbmbp:~ grb$ ls z*
z???       z??????    z????????? z???       z???
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then I happened to pipe the ls through a grep, and the unicode characters printed correctly:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;grbmbp:~ grb$ ls z* | grep '.*'
z໐
z두
z툃
z䌨
z冕
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What?  Well, I guess I'll take it...&lt;/p&gt;

&lt;p&gt;While posting this, it got even more fun.  All five of the characters above print normally in the terminal and Finder, but only two print normally in Opera's text edit control.  I wonder how many will show up once this is published.  Can you see them in your RSS reader and/or browser?&lt;/p&gt;

&lt;p&gt;Update: After publishing, I viewed the page in Safari and the characters displayed exactly like they did in the terminal and Finder.  So at least Opera didn't mangle the bytes.  However, Firefox draws the first three incorrectly (the same three that Opera couldn't draw at all).  Unsurprisingly, IE7 can't draw any of them.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Mutable State Is Manual Memory Management</title>
        <link href="http://blog.extracheese.org/2007/02/mutable-state-is-manual-memory-management.html"/>
        <updated>2007-02-07T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2007/02/mutable-state-is-manual-memory-management</id>
        <content type="html">&lt;p&gt;&lt;a href="http://sequence.complete.org"&gt;The Haskell Sequence&lt;/a&gt; linked to my &lt;a href="/2007/01/c-30-looks-promising.html"&gt;post on C#&lt;/a&gt; (although they obviously don't share my opinions on it).  I'd never read that site before, and I came across this "quote of the week" while glancing over it:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;"Mutable state is actually another form of manual memory management: every time you over-write a value you are making a decision that the old value is now garbage, regardless of what other part of the program might have been using it."&lt;/p&gt;

&lt;p&gt;(Paul Johnson via &lt;a href="http://sequence.complete.org/"&gt;The Haskell Sequence&lt;/a&gt;)&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;My first thought upon reading this was "why the hell didn't I think of that?"  It's obvious in retrospect, and is a wonderfully concise explanation of why side effects are a bad idea.  I always try to limit my use of side effects, but I never had a simple way to explain to myself (or others) why that's a good thing.  Thanks, Paul.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Measure the Goodness of Your Code</title>
        <link href="http://blog.extracheese.org/2007/02/measure-the-goodness-of-your-code.html"/>
        <updated>2007-02-06T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2007/02/measure-the-goodness-of-your-code</id>
        <content type="html">&lt;p&gt;Because I want to keep my code short, I like to see statistics on my sandbox before committing to subversion.  At first, I just did things like &lt;code&gt;svn diff | grep '^+' | wc -l&lt;/code&gt;, but that got old fast.  So, I wrote a little script called gn (for "goodness") that computes some simple statistics.  Here's what it says about my sandbox right now:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;grbmbp:~/trunk grb$ gn
591 lines of diff
129 lines added
186 lines removed
-57 lines net change
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I've added 129 lines and removed 186, which is a net change of -57.  Any line that's simply replaced will result in one line removed and one added, for a net change of 0.  The implication here is that the more negative your "net change" is, the better.  (DISCLAIMER: Please don't take this literally and post angry comments.)&lt;/p&gt;

&lt;p&gt;Sometimes I want to know the goodness of a bunch of related changes, so the script can also take any arguments that "svn diff" can take.  It just passes them on, so you can compute statistics across revisions, etc.:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;grbmbp:~/trunk grb$ gn -r420:451 
4246 lines of diff
1099 lines added
1627 lines removed
-528 lines net change
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see, I've been killing a lot of code recently.  The script is below, in case you want to try it for yourself.  I've only tested it on OS X; YMMV.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/usr/bin/python&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;

&lt;span class="n"&gt;svn_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:])&lt;/span&gt;
&lt;span class="n"&gt;pipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;popen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;svn diff &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;svn_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;diff_lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readlines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;# Added lines start with &amp;#39;+&amp;#39; (but not &amp;#39;+++&amp;#39;, because that marks a&lt;/span&gt;
&lt;span class="c"&gt;# new file).The same goes for removed lines, except &amp;#39;-&amp;#39; instead of&lt;/span&gt;
&lt;span class="c"&gt;# &amp;#39;+&amp;#39;.&lt;/span&gt;
&lt;span class="n"&gt;added_lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;diff_lines&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;+&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;+++&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="n"&gt;removed_lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;diff_lines&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;-&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;---&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%i&lt;/span&gt;&lt;span class="s"&gt; lines of diff&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff_lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%i&lt;/span&gt;&lt;span class="s"&gt; lines added&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;added_lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%i&lt;/span&gt;&lt;span class="s"&gt; lines removed&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;removed_lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%+i&lt;/span&gt;&lt;span class="s"&gt; lines net change&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;added_lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;
                                &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;removed_lines&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

</content>
    </entry>
    
    <entry>
        <title>C# 3.0 Looks Promising</title>
        <link href="http://blog.extracheese.org/2007/01/c-30-looks-promising.html"/>
        <updated>2007-01-24T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2007/01/c-30-looks-promising</id>
        <content type="html">&lt;p&gt;At &lt;a href="http://www.codemash.org/"&gt;CodeMash&lt;/a&gt; last week, I learned quite a bit about C#'s new features from &lt;a href="http://weblogs.asp.net/scottgu/"&gt;Scott Guthrie&lt;/a&gt;.  I'm no fan of explicitly typed languages , but C# 3.0 has me very excited.  It's getting a host of new features inspired by other languages, including anonymous types, type inference, and lambda expressions.  There's plenty of &lt;a href="http://blogs.tedneward.com/2005/09/22/Language+Innovation+C+30+Explained.aspx"&gt;discussion&lt;/a&gt; of these around the web, so I'm not going to rehash them all.  I want to highlight the two features that I'm most excited about: type inference and LINQ.&lt;/p&gt;

&lt;h3&gt;Type Inference&lt;/h3&gt;

&lt;p&gt;C# 3.0 adds basic type inference, but it's quite weak and can only infer types on assignment.  Basically, you can do these types of things:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;some_function_returning_a_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;list_of_strings&lt;/span&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 class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Note that the "var" keyword does &lt;em&gt;not&lt;/em&gt; make the variables dynamically typed &amp;ndash; it just tells the compiler to infer their types based on what's being assigned to them.  This is nice, but it's very limited.  You can't, for example, declare a function with a return type of "var" and expect the compiler to figure it out.  Haskell this definitely is not, but it's a huge step forward for C's verbosity-laden children.&lt;/p&gt;

&lt;h3&gt;LINQ&lt;/h3&gt;

&lt;p&gt;Let me get this out of the way: LINQ is awesome and I want it in my language.&lt;/p&gt;

&lt;p&gt;In a nutshell, LINQ allows you to use declarative, SQLesque syntax in your
C# code.  I've been wishing for this feature for a long time, but Python's
list comprehensions have had to hold me over so far.  Here's a very simple
example of what you can do with LINQ:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;low_nums&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;
               &lt;span class="n"&gt;where&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
               &lt;span class="n"&gt;select&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Here's an equivalent example in Python:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;low_nums&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;LINQ is far more powerful than Python's list comprehensions, though.  It has many of the features of SQL: joins, grouping, count() and sum(), etc.  And a single LINQ expression can query a collection of objects, a database, or XML data.  This makes me drool.&lt;/p&gt;

&lt;p&gt;Most of the discussion of LINQ at &lt;a href="http://www.codemash.org"&gt;CodeMash&lt;/a&gt; boiled down to excitement over the idea of writing statically-typed, compiler-checked database queries.  You can probably guess that this is not the reason I'm excited.  For most interesting problems, the limiting factor is not whether you get your queries correct or not; it's whether you can coax your brain into (1) solving the problem and (2) translating the solution into code.  Any feature that simplifies your code makes step (2) easier, and LINQ is definitely such a feature.  LINQ is the only feature of &lt;em&gt;any&lt;/em&gt; explicitly-typed language that I am covetous of.  It really is that awesome, and Microsoft has managed to beat every other language to the punch with this.&lt;/p&gt;

&lt;h3&gt;Back to Reality&lt;/h3&gt;

&lt;p&gt;OK, enough gushing.  I still dislike explicitly typed languages.  These new features certainly aren't going to get me to leave dynamic languages behind &amp;ndash; C# is still incredibly verbose when compared to Python, Ruby, etc.  And, even though many of these features were borrowed from functional and dynamic languages, C# 3.0 is still as statically typed as ever.  However, I have a newfound respect for &lt;a href="http://en.wikipedia.org/wiki/Anders_Hejlsberg"&gt;Anders Hejlsberg&lt;/a&gt; and friends.  If Microsoft manages to drag the hoards of C# programmers toward a more concise coding style, the world will be a much better place.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Why dynamic typing is useful</title>
        <link href="http://blog.extracheese.org/2006/12/why-dynamic-typing-is-useful.html"/>
        <updated>2006-12-23T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2006/12/why-dynamic-typing-is-useful</id>
        <content type="html">&lt;p&gt;Steve Yegge's &lt;a href="http://steve-yegge.blogspot.com/2006/12/parabola.html"&gt;Parabola&lt;/a&gt; has thrown a little more fuel on the static vs. dynamic fire.  The comments so far have been mostly constructive, which is a nice surprise. However, there has been some back-and-forth about dynamic typing, mostly concerning why it's useful and what it actually entails.  In a recent comment, "Matt" said&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;"I don't see how software could possibly attempt to handle anything it did not anticipate, except to gracefully fail, which T.S.  did."&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;I've seen this sentiment before, almost always from people whose programming experience is limited to statically typed languages. If that's your background, then you've not seen the wonderful flexibility that dynamic typing adds. Here's a very simple example written in Python.  Don't worry - Python is called "executable pseudo code" for a reason!&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_many&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;things&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;thing&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;things&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Looking at this, you might expect &lt;em&gt;things&lt;/em&gt; to be a list.  That's possible, but it could also be a set, or an iterator, or even a dictionary (a hash table).  As the programmer, you don't have to worry about it.  As long as Python can figure out how to iterate over your &lt;em&gt;things&lt;/em&gt; argument, everything will just work.&lt;/p&gt;

&lt;p&gt;So, to return to Matt's statement above: this is one way code can handle situations that the programmer never considered.  Maybe the guy who wrote &lt;em&gt;process_many&lt;/em&gt; never even thought that someone might pass in a dictionary.  That doesn't matter; it will work anyway.&lt;/p&gt;

&lt;p&gt;Of course, you can get this effect in Java using interfaces.  The problem with interfaces is that you have to make decisions about them ahead of time.  If you were writing &lt;em&gt;process_many&lt;/em&gt; in Java, you might not consider the case of passing in anything but a list. Then, if someone using your code wanted to pass in a set, iterator, dictionary, etc., they'd be out of luck.&lt;/p&gt;

&lt;p&gt;This is a recurring theme with static vs. dynamic languages. In explicitly typed static languages, you have to spend time figuring out what type every little thing should have.  Then, when you're finally done, there will always be some case you didn't consider, like a hand-written note telling you to call the ticket counter.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Python's default arguments are tricky</title>
        <link href="http://blog.extracheese.org/2006/12/pythons-default-arguments-are-tricky.html"/>
        <updated>2006-12-19T00:00:00-08:00</updated>
        <id>http://blog.extracheese.org/2006/12/pythons-default-arguments-are-tricky</id>
        <content type="html">&lt;p&gt;Default arguments are evaluated at function definition time, so they're persistent across calls. This has some interesting (and confusing) side effects.  An example:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;If you've not tried this before, you probably expect foo to always return ['a']: it should start with an empty list, append 'a' to it, and return.  Here's what it actually does:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;This is because the default value for &lt;em&gt;d&lt;/em&gt; is allocated when the function is created, not when it's called.  Each time the function is called, the value is still hanging around from the last call.  This gets even weirder if you throw threads into the mix.  If two different threads are executing the function at the same time, and one of them changes a default argument, they both will see the change.&lt;/p&gt;

&lt;p&gt;Of course, all of this is only true if the default argument's value is a mutable type.  If we change &lt;em&gt;foo&lt;/em&gt; to be defined as&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;then it will always return 1.  (The difference here is that in &lt;em&gt;foo2&lt;/em&gt;, the variable &lt;em&gt;d&lt;/em&gt; is being reassigned, while in &lt;em&gt;foo&lt;/em&gt; its value was being changed.)&lt;/p&gt;
</content>
    </entry>
    
 
</feed>
