<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>schmichael&#039;s blog</title>
	<atom:link href="http://blog.schmichael.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.schmichael.com</link>
	<description>good good study, day day up</description>
	<lastBuildDate>Mon, 02 Aug 2010 17:28:53 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Less Pagination, More More</title>
		<link>http://blog.schmichael.com/2010/07/16/less-pagination-more-more/</link>
		<comments>http://blog.schmichael.com/2010/07/16/less-pagination-more-more/#comments</comments>
		<pubDate>Sat, 17 Jul 2010 00:30:58 +0000</pubDate>
		<dc:creator>Michael Schurter</dc:creator>
				<category><![CDATA[SQL]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[cassandra]]></category>
		<category><![CDATA[mongodb]]></category>
		<category><![CDATA[urbanairship]]></category>

		<guid isPermaLink="false">http://blog.schmichael.com/?p=827</guid>
		<description><![CDATA[We live in a brave new (to some) world of databases other than a relational database with a SQL interface. Normally end users never notice a difference, but the astute viewer may notice the slow demise of an old friend: &#8230; <a href="http://blog.schmichael.com/2010/07/16/less-pagination-more-more/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>We live in a brave new (to some) world of databases other than a relational database with a SQL interface. Normally end users never notice a difference, but the astute viewer may notice the slow demise of an old friend: pagination.</p>
<p>Traditionally with SQL databases pagination has looked something like this:<br />
<img src="http://schmichael.com/files/pagination.png"/></p>
<p>There are previous and next links as well as links for jumping right to the beginning and end. Pretty boring stuff.</p>
<p>What&#8217;s interesting is that this standard interface is disappearing in favor of something like this:</p>
<p><em>Twitter</em><br />
<img src="http://schmichael.com/files/twitter-more.png"/></p>
<p><em>Facebook</em><br />
<img src="http://schmichael.com/files/facebook-more.png"/></p>
<p>And soon beta testers of <a href="http://blog.urbanairship.com/2010/05/25/android-delivers-push-notifications-are-here-to-stay/">Urban Airship&#8217;s push service for Android</a> will see a More link on the page that lists devices associated with their app:</p>
<p><img src="http://schmichael.com/files/apids-more.png"/></p>
<p>The simplest possible explanation for this dumbing down of pagination is that <strong>count (for total pages) and skip/offset are expensive operations.</strong></p>
<p>Not only are those operations expensive, but in eventually consistent databases, which many modern non-relational databases are, they&#8217;re extremely expensive, if not impossible, to perform.</p>
<p><strong>Cassandra</strong></p>
<p>At Urban Airship we, like Facebook, use <a href="http://cassandra.apache.org/">Cassandra</a>: a distributed column-based database. This deals two deadly blows to traditional pagination:</p>
<ol>
<li>No way to count columns in a row (without reading every column).</li>
<li>No way to skip by numeric offset (so you can&#8217;t say, skip to page 5).</li>
</ol>
<p>In Cassandra columns are ordered, so you start reading from the beginning and read N+1 columns where N is the number of items you&#8217;d like to display. The last column&#8217;s key is then used to determine whether the More link is enabled, and if so, what key to start the next &#8220;page&#8221; at. </p>
<p>Both of those are solvable problems if you really need them, but I would suspect you would end up creating a column count cache as well as some sort of table of contents for the various page offsets. Not what I want to spend my time implementing.</p>
<p>The fact of the matter is that for many use cases, a simple More button works just as well (if not better) than traditional pagination. It&#8217;s also far cheaper to implement, which means more developer time free to work on features and more hardware resources available to push your 140 character insights around the web.</p>
<p><strong>MongoDB</strong></p>
<p>I should note that MongoDB is fairly unique in the non-relational database world as its dynamic querying features include <a href="http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Count">count</a> and <a href="http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bskip%28%29%7D%7D">skip</a> operations. However, as with any database, you&#8217;ll want to make sure these queries hit indexes.</p>
<p>Sadly MongoDB currently doesn&#8217;t have the distributed features necessary to automatically handle data too big for a single server.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.schmichael.com/2010/07/16/less-pagination-more-more/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>New Job, New Blog</title>
		<link>http://blog.schmichael.com/2010/07/09/new-job-new-blog/</link>
		<comments>http://blog.schmichael.com/2010/07/09/new-job-new-blog/#comments</comments>
		<pubDate>Fri, 09 Jul 2010 23:38:01 +0000</pubDate>
		<dc:creator>Michael Schurter</dc:creator>
				<category><![CDATA[Personal]]></category>

		<guid isPermaLink="false">http://blog.schmichael.com/?p=812</guid>
		<description><![CDATA[The title is a bit misleading, but I haven&#8217;t updated my blog in far too long. In April I started working for Urban Airship, and I&#8217;ve been meaning to upgrade my blog and move to it to a shorter URL &#8230; <a href="http://blog.schmichael.com/2010/07/09/new-job-new-blog/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>The title is a bit misleading, but I haven&#8217;t updated my blog in far too long.</p>
<p>In April I started working for <a href="http://urbanairship.com/">Urban Airship</a>, and I&#8217;ve been meaning to upgrade my blog and move to it to a shorter URL for some time. You should be reading this on <a href="http://blog.schmichael.com/">schmichael.com</a> instead of on the old site at <a href="http://michael.susens-schurter.com/blog/">michael.susens-schurter.com</a>. I think I setup the write .htaccess magic to make all of the old links properly redirect to the new domain. Sorry if I broke anything.</p>
<p>For anyone confused by the personal rebranding from &#8220;Michael Susens-Schurter&#8221; to &#8220;schmichael&#8221;, it&#8217;s all because I&#8217;m lazy and hate typing. Also, <a href="http://mtrichardson.com/">there&#8217;s already a Michael at Urban Airship</a>, so I&#8217;m pretty much &#8220;schmichael&#8221; to everyone these days.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.schmichael.com/2010/07/09/new-job-new-blog/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Making Server-Side MongoDB Functions Less Awkward</title>
		<link>http://blog.schmichael.com/2010/01/11/making-server-side-mongodb-functions-less-awkward/</link>
		<comments>http://blog.schmichael.com/2010/01/11/making-server-side-mongodb-functions-less-awkward/#comments</comments>
		<pubDate>Tue, 12 Jan 2010 00:54:19 +0000</pubDate>
		<dc:creator>Michael Schurter</dc:creator>
				<category><![CDATA[Open Source]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[mongodb]]></category>
		<category><![CDATA[pymongo]]></category>

		<guid isPermaLink="false">http://michael.susens-schurter.com/blog/?p=796</guid>
		<description><![CDATA[I&#8217;ve recently switched my project at work to use MongoDB for the user database and a few other datasets. Currently I don&#8217;t use many JavaScript functions, but when I do I like to store them on the server so that &#8230; <a href="http://blog.schmichael.com/2010/01/11/making-server-side-mongodb-functions-less-awkward/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve recently switched my project at work to use MongoDB for the user database and a few other datasets.</p>
<p>Currently I don&#8217;t use many JavaScript functions, but when I do I like to store them on the server so that they&#8217;re accessible when I&#8217;m poking around in a console.</p>
<p>I use something similar to the following function to load all of my JS functions onto the server when my app starts:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>
<span style="color: #ff7700;font-weight:bold;">import</span> pymongo
<span style="color: #ff7700;font-weight:bold;">import</span> pkg_resources
&nbsp;
<span style="color: #808080; font-style: italic;"># Relative to distribution's root</span>
SCRIPT_DIR = <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'model'</span>, <span style="color: #483d8b;">'js'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> init_js<span style="color: black;">&#40;</span>db<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">''</span><span style="color: #483d8b;">'Initializes server-side javascript functions'</span><span style="color: #483d8b;">''</span>
    scripts = <span style="color: #008000;">filter</span><span style="color: black;">&#40;</span>
            <span style="color: #ff7700;font-weight:bold;">lambda</span> f: f.<span style="color: black;">endswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'.js'</span><span style="color: black;">&#41;</span>,
            pkg_resources.<span style="color: black;">resource_listdir</span><span style="color: black;">&#40;</span>__name__, SCRIPT_DIR<span style="color: black;">&#41;</span>
        <span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> script <span style="color: #ff7700;font-weight:bold;">in</span> scripts:
        <span style="color: #808080; font-style: italic;"># Name the function after the script name</span>
        func_name, _ = script.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'.'</span>, <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
        script_path = <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span>SCRIPT_DIR, script<span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;"># Create a pymongo Code object</span>
        <span style="color: #808080; font-style: italic;"># otherwise it will be stored as a string</span>
        <span style="color: #dc143c;">code</span> = pymongo.<span style="color: #dc143c;">code</span>.<span style="color: black;">Code</span><span style="color: black;">&#40;</span>
                pkg_resources.<span style="color: black;">resource_string</span><span style="color: black;">&#40;</span>__name__, script_path<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;"># Upsert the function</span>
        db.<span style="color: black;">system</span>.<span style="color: black;">js</span>.<span style="color: black;">save</span><span style="color: black;">&#40;</span><span style="color: black;">&#123;</span> <span style="color: #483d8b;">'_id'</span>: func_name, <span style="color: #483d8b;">'value'</span>: <span style="color: #dc143c;">code</span>, <span style="color: black;">&#125;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>However, using server-side functions from Python is awkward at best.  Say I have the JavaScript function:</p>
<p><strong>add.js</strong></p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>x<span style="color: #339933;">,</span> y<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000066; font-weight: bold;">return</span> x <span style="color: #339933;">+</span> y<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>To run that function via PyMongo requires wrapping the function call with placeholder parameters in a Code object and passing in values as a dict:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">var1 = <span style="color: #ff4500;">1</span>
var2 = <span style="color: #ff4500;">2</span>
result = db.<span style="color: #008000;">eval</span><span style="color: black;">&#40;</span>pymongo.<span style="color: #dc143c;">code</span>.<span style="color: black;">Code</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'add(a, b)'</span>, <span style="color: black;">&#123;</span><span style="color: #483d8b;">'a'</span>: var1, <span style="color: #483d8b;">'b'</span>: var2,<span style="color: black;">&#125;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">assert</span> result == <span style="color: #ff4500;">3</span></pre></div></div>

<p><strong>Update:</strong> See <a href="http://michael.susens-schurter.com/blog/2010/01/11/making-server-side-mongodb-functions-less-awkward/comment-page-1/#comment-68027">MongoDB dev Mike Dirolf comment</a> to see a much more concise way of executing server-side functions.</p>
<p>Bearable for simple functions, but having to manually map parameters to values is tiresome and error prone with longer function signatures.</p>
<p>What I wanted was something more natural like:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">var1 = <span style="color: #ff4500;">1</span>
var2 = <span style="color: #ff4500;">2</span>
result = db.<span style="color: black;">add</span><span style="color: black;">&#40;</span>var1, var2<span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">assert</span> result == <span style="color: #ff4500;">3</span></pre></div></div>

<p>I use a simple PyMongo Database object wrapper to make my life easier:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">string</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">from</span> pymongo.<span style="color: #dc143c;">code</span> <span style="color: #ff7700;font-weight:bold;">import</span> Code
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> ServerSideFunctions<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, db<span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: black;">db</span> = db
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> func_wrapper<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, func<span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">''</span><span style="color: #483d8b;">'Returns a closure for calling a server-side function.'</span><span style="color: #483d8b;">''</span>
        params = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span> <span style="color: #808080; font-style: italic;"># To keep params ordered</span>
        kwargs = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
        <span style="color: #ff7700;font-weight:bold;">def</span> server_side_func<span style="color: black;">&#40;</span><span style="color: #66cc66;">*</span>args<span style="color: black;">&#41;</span>:
            <span style="color: #483d8b;">''</span><span style="color: #483d8b;">'Calls server side function with positional arguments.'</span><span style="color: #483d8b;">''</span>
            <span style="color: #808080; font-style: italic;"># Could be removed with better param generating logic</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>args<span style="color: black;">&#41;</span> <span style="color: #66cc66;">&gt;</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">string</span>.<span style="color: black;">letters</span><span style="color: black;">&#41;</span>:
                <span style="color: #ff7700;font-weight:bold;">raise</span> <span style="color: #008000;">TypeError</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'%s() takes at most %d arguments (%d given)'</span>
                        <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>func, <span style="color: #008000;">len</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">string</span>.<span style="color: black;">letters</span><span style="color: black;">&#41;</span>, <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>args<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
            <span style="color: #808080; font-style: italic;"># Prepare arguments</span>
            <span style="color: #ff7700;font-weight:bold;">for</span> k, v <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">zip</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">string</span>.<span style="color: black;">letters</span>, args<span style="color: black;">&#41;</span>:
                kwargs<span style="color: black;">&#91;</span>k<span style="color: black;">&#93;</span> = v
                params.<span style="color: black;">append</span><span style="color: black;">&#40;</span>k<span style="color: black;">&#41;</span> 
&nbsp;
            <span style="color: #808080; font-style: italic;"># Prepare code object</span>
            <span style="color: #dc143c;">code</span> = Code<span style="color: black;">&#40;</span><span style="color: #483d8b;">'%s(%s)'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>func, <span style="color: #483d8b;">', '</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span>params<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>, kwargs<span style="color: black;">&#41;</span>
&nbsp;
            <span style="color: #808080; font-style: italic;"># Return result of server-side function</span>
            <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">db</span>.<span style="color: #008000;">eval</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">code</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> server_side_func
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__getattr__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, func<span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">''</span><span style="color: #483d8b;">'Return a closure for calling server-side function named `func`'</span><span style="color: #483d8b;">''</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">func_wrapper</span><span style="color: black;">&#40;</span>func<span style="color: black;">&#41;</span>
&nbsp;
dbjs = ServerSideFunctions<span style="color: black;">&#40;</span><span style="color: #483d8b;">'foo'</span><span style="color: black;">&#41;</span>
var1 = <span style="color: #ff4500;">1</span>
var2 = <span style="color: #ff4500;">2</span>
result = dbjs.<span style="color: black;">add</span><span style="color: black;">&#40;</span>var1, var2<span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">assert</span> result == <span style="color: #ff4500;">3</span></pre></div></div>

<p>I&#8217;m tempted to monkey-patch PyMongo&#8217;s Database class to add a ServerSideFunctions instance directly as a js attribute, so then I could drop the confusing <code>dbjs</code> variable and just use:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">assert</span> db.<span style="color: black;">js</span>.<span style="color: black;">add</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span>,<span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span> == <span style="color: #ff4500;">3</span></pre></div></div>

<p>If someone knows of a better way to access server-side MongoDB functions from Python, please let me know!</p>
<p><small>I modified this code to remove code specific to my project, so please let me know if there are errors.</small></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.schmichael.com/2010/01/11/making-server-side-mongodb-functions-less-awkward/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Web Developer Contractor Rates</title>
		<link>http://blog.schmichael.com/2009/11/18/web-developer-contractor-rates/</link>
		<comments>http://blog.schmichael.com/2009/11/18/web-developer-contractor-rates/#comments</comments>
		<pubDate>Wed, 18 Nov 2009 20:15:35 +0000</pubDate>
		<dc:creator>Michael Schurter</dc:creator>
				<category><![CDATA[Open Source]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Personal]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[$$$]]></category>
		<category><![CDATA[benjamins]]></category>
		<category><![CDATA[billing]]></category>
		<category><![CDATA[contractor]]></category>
		<category><![CDATA[navel gazing]]></category>
		<category><![CDATA[rates]]></category>
		<category><![CDATA[webdev]]></category>

		<guid isPermaLink="false">http://michael.susens-schurter.com/blog/?p=752</guid>
		<description><![CDATA[We just happened* to start chatting in the #pdxdjango IRC channel on Freenode about what the rates web developer contractors charge today, and I wanted to post my experiences after leaving the contractor world a few months ago after 2 &#8230; <a href="http://blog.schmichael.com/2009/11/18/web-developer-contractor-rates/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>We just happened* to start chatting  in the #pdxdjango IRC channel on Freenode about what the rates web developer contractors charge today, and I wanted to post my experiences after leaving the contractor world a few months ago after 2 years of more or less successful contracting either individually or via <a href="http://lofiart.com">Lo-Fi Art</a>.</p>
<p>A really rough table of my rates as a contractor:</p>
<table>
<tr>
<th>Language</th>
<th>Experience</th>
<th>Rate per hour</th>
</tr>
<tr>
<td>PHP</td>
<td>Entry Level</td>
<td>$8-20</td>
</tr>
<tr>
<td>PHP</td>
<td>Experienced</td>
<td>$20-65</td>
</tr>
<tr>
<td>PHP</td>
<td>Specialist</td>
<td>never got here with PHP (thankfully <img src='http://blog.schmichael.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  )</td>
</tr>
<tr>
<td>Sysadmin</td>
<td>Slightly Experienced</td>
<td>$45</td>
</tr>
<tr>
<td>Python</td>
<td>Entry Level</td>
<td>$25-35</td>
</tr>
<tr>
<td>Python</td>
<td>Experienced</td>
<td>$35-65</td>
</tr>
<tr>
<td>Python</td>
<td>Specialist (Django)</td>
<td>$65-85</td>
</tr>
</table>
<p>However, I think I&#8217;ve billed pretty cheaply, especially for Python work.  If I had continued in the contracting world I think I would have been aiming for north of <strong>$100/hr for new contracts by the end of 2009</strong>.</p>
<p><strong>Important Notes</strong></p>
<ul>
<li>All of the experience levels and rates are really rough estimates, please don&#8217;t read too much into it.  I just wanted to give people <em>some</em> idea of what rates are floating around.  (I also have a terrible memory, so these numbers could be way off.  Mea culpa.)</li>
<li>The sysadmin job is a career oddity for me and consisted of mostly doing Active Directory / Exchange setup (snuck in a Debian server of course).  That being said I still enjoy sysadminish type work today.</li>
<li><strong>Experienced</strong> means you have a few &#8220;serious&#8221; projects under your belt (not the meaingless &#8220;5 years of experience&#8221; so many job descriptions call for).</li>
<li><strong>Specialist</strong> is a poor term, but I needed someway to describe the shift from &#8220;I&#8217;ll do anything if it&#8217;s PHP or Python&#8221; to &#8220;I&#8217;m a Django&#8221; developer.  My guess is that <em>real</em> specialists (contributors to major projects or popular plugin/module authors) fall into the upper end of this spectrum and can often charge well over $100/hr for highly sought after specialties (Anything + Facebook might be a good example of that right now).</li>
<li>I started with PHP first (2000-2006), so I was just less experienced in general.</li>
<li>Not only does supply &#038; demand help Python devs fetch a higher rate (reasonable demand, with low supply), but also a Python developer knows how to write code.<br/><br />
  A PHP &#8220;developer&#8221; could just be someone who has setup a few WordPress or Drupal sites and maybe done some theming.  I think you&#8217;d be hard pressed to find a web developer who couldn&#8217;t be described as having PHP &#8220;experience.&#8221;</li>
<li>My entire career in the &#8220;Specialist (Django)&#8221; range was in Portland, OR which has a vibrant web related economy (at least as far as my untrained eye can tell).  All other rates fell at least partially into time periods where I lived in Illinois (and not Chicago), so that could account for some of the upward shift in the my rates.</li>
<li>These numbers are also rough estimates because I&#8217;ve done flat per-project billing, retainers, and a variety of other crazy ways of exchanging money for labor.  Dollars per hour is still what it all comes down to in the end (like <a href="http://en.wikipedia.org/wiki/Damage_per_second">DPS</a> for you MMORPG freaks).</li>
</ul>
<p><strong>So I&#8217;m {ripping off,getting ripped off by} my clients?</strong></p>
<p>I don&#8217;t know, but I doubt it.  If anything my rough estimates should show what an inexact science billing is.  It probably varies more on project factors than on the contractor&#8217;s experience.</p>
<p>Right up until I took my full time job at <a href="http://www.yougov.com">YouGov</a> my <em>favorite</em> client was still paying me at my $35/hr rate.  In fact sometimes <strong>I wonder if there might have been an inverse relationship between hourly rate and job satisfaction.</strong></p>
<p>This could be a quirk of me being a pretty neurotic person and therefore feeling more pressure when working at a higher rate.  At lower rates I generally worked more hours and spent more time tweaking designs, writing tests, and doing other tasks other than putting my head down and coding.  Thus at the end of the day, the more hours I worked on projects I liked, the less money I made (relative to working fewer hours on less enjoyable projects).</p>
<p><small>* Ok, so it looks like I brought it up&#8230; but I&#8217;d like to think it spawned some good discussion.</small><br />
<small>Left off the Python category as that gets syndicated on Unofficial Planet Python, and I don&#8217;t think this post is high enough quality to deserve that.  <img src='http://blog.schmichael.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </small></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.schmichael.com/2009/11/18/web-developer-contractor-rates/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Ubuntu 9.10 on a Thinkpad T400</title>
		<link>http://blog.schmichael.com/2009/11/11/ubuntu-9-10-on-a-thinkpad-t400/</link>
		<comments>http://blog.schmichael.com/2009/11/11/ubuntu-9-10-on-a-thinkpad-t400/#comments</comments>
		<pubDate>Thu, 12 Nov 2009 06:11:08 +0000</pubDate>
		<dc:creator>Michael Schurter</dc:creator>
				<category><![CDATA[GNU/Linux]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://michael.susens-schurter.com/blog/?p=746</guid>
		<description><![CDATA[I upgraded from Ubuntu 9.04 to 9.10 on my Lenovo Thinkpad T400 about a week ago, and thought I&#8217;d write down some of my impressions. Good news ATI graphics card with proprietary binary driver &#8211; just works (much better than &#8230; <a href="http://blog.schmichael.com/2009/11/11/ubuntu-9-10-on-a-thinkpad-t400/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I upgraded from Ubuntu 9.04 to <a href="http://www.ubuntu.com/products/whatisubuntu/910features">9.10</a> on my Lenovo Thinkpad T400 about a week ago, and thought I&#8217;d write down some of my impressions.</p>
<p><strong>Good news</strong></p>
<ul>
<li>ATI graphics card with proprietary binary driver &#8211; just works (much better than 9.04 as well).</li>
<li>Suspend &#038; hibernate work flawlessly.</li>
<li>Boots fast.</li>
<li>Upgrade worked flawlessly.</li>
<li>Sound (better volume panel!), wifi, USB, network printer, bluetooth (option to turn it on/off!), webcam, etc. all still work.</li>
<li>New theme is nice.</li>
<li>I manually <a href="http://ext4.wiki.kernel.org/index.php/Ext4_Howto#Converting_an_ext3_filesystem_to_ext4">upgraded my filesystem to ext4</a> and <a href="https://wiki.ubuntu.com/KernelTeam/Grub2Testing">grub from version 1 to 2</a>.  It was a bit scary but worked out fine.  Can&#8217;t really feel a difference, but my laptop is mainly a dumb terminal for running web browsers and ssh.
</ul>
<p><strong><a href="http://live.gnome.org/Empathy">Empathy</a></strong></p>
<p>Ubuntu replaced Pidgin with Empathy as the default IM client in Ubuntu 9.10.  I think because Empathy supports voice/video chat and Pidgin doesn&#8217;t? I&#8217;ve used video chat once in my life and that was through Skype on Linux.  It worked great, but it&#8217;s really not a feature I care about.</p>
<p>So for someone like me who doesn&#8217;t care about Empathy&#8217;s singular advantage over Pidgin, Empathy is a major step backward.  At first it was extremely crashy, but a recent update seems to have fixed that.  However, now it mysteriously loses messages.  I&#8217;m a very light IM user, but it would start silently missing messages a few hours into the day every day.</p>
<p>I&#8217;ve switched back to Pidgin and couldn&#8217;t be happier.</p>
<p><strong>Message Notification applet deal</strong></p>
<p>Empathy integrates with the message notification applet deal along with Evolution.  Other apps may as well, but evidently I don&#8217;t use any of them.  My top panel is 75% whitespace, so the singular benefit of Ubuntu&#8217;s consolidated message notification applet was completely lost on me.</p>
<p>So much like Empathy, I removed this specialized applet as well.  I&#8217;m much happier with per-application icons anyway and fail to see what the benefit of consolidating them is (unless you have a really cramped top panel).</p>
<p><strong>New Theme</strong></p>
<p>I like it, but then I installed Chromium.  Now the bold window titles and expansive title bars in Metacity look bulky and antiquated.  Gnome really needs to evolve their window manager and default UI.  Chrome is an excellent example of how to design a compact, minimal, yet still pleasant and intuitive user interface.</p>
<p><strong><a href="https://wiki.ubuntu.com/SoftwareCenter">Ubuntu Software Center</a></strong></p>
<p>What a curious little replacement for the old Add/Remove Applications program.  I think I see the direction they&#8217;re headed, but it definitely feels like rolled out a beta program to replace a perfectly functional and stable one.</p>
<p>The left pane with expansive whitespace and 2 options <em>hints</em> that there might be more categories in the future, but right now it just looks like a mistake.  Like maybe something isn&#8217;t working properly, and I&#8217;m not seeing all the options I should see.</p>
<p>At any rate, I hope Ubuntu adds an App Store that even includes evil proprietary software.  I&#8217;d love to be able to plunk down a few bucks for a game like <a href="http://braid-game.com/">Braid</a> directly from Ubuntu Software Center.  That&#8217;d be great!  Maybe Canonical could even pocket a few pennies and start making Linux-on-the-desktop profitable.  Now I&#8217;m just dreaming though&#8230;</p>
<p><strong>Bottom Line</strong></p>
<p>9.10 is a solid and safe upgrade for any users of previous versions.  Not sure there&#8217;s anything new to win over users from OS X or Windows though.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.schmichael.com/2009/11/11/ubuntu-9-10-on-a-thinkpad-t400/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>lxml vs. ElementTree</title>
		<link>http://blog.schmichael.com/2009/10/14/lxml-vs-elementtree/</link>
		<comments>http://blog.schmichael.com/2009/10/14/lxml-vs-elementtree/#comments</comments>
		<pubDate>Wed, 14 Oct 2009 23:18:31 +0000</pubDate>
		<dc:creator>Michael Schurter</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[benchmarks]]></category>
		<category><![CDATA[ElementTree]]></category>
		<category><![CDATA[lxml]]></category>
		<category><![CDATA[xml]]></category>

		<guid isPermaLink="false">http://michael.susens-schurter.com/blog/?p=736</guid>
		<description><![CDATA[While lxml has some excellent benchmarks about the speed of lxml.etree vs. ElementTree, I wanted to run some tests that were as close as possible to my own use case (fairly simple multi-megabyte XML files). Here are the results of &#8230; <a href="http://blog.schmichael.com/2009/10/14/lxml-vs-elementtree/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>While lxml has some <a href="http://codespeak.net/lxml/performance.html">excellent benchmarks about the speed of lxml.etree vs. ElementTree</a>, I wanted to run some tests that were as close as possible to my own use case (fairly simple multi-megabyte XML files).</p>
<p>Here are the results of my little test script <a href="http://blog.schmichael.com/files/lxml-v-etree.py-remove-me">lxml-v-etree.py</a> (times are in milliseconds):</p>
<pre>
name           generate | tostring | total | write | parse | find | total
------------------------+----------+-------+-------+-------+------+------
xml.cElementTree    132 |   2430   |  2562 |  2433 |   158 |   58 |   216
xml.cElementTree    112 |   2384   |  2497 |  2387 |   158 |   25 |   183
xml.cElementTree    113 |   2393   |  2507 |  2396 |   161 |   25 |   187
xml.ElementTree     591 |   2571   |  3163 |  2574 |  3613 |   25 |  3638
xml.ElementTree     619 |   2567   |  3187 |  2570 |  3589 |   55 |  3644
xml.ElementTree     609 |   2578   |  3188 |  2581 |  3564 |   55 |  3619
lxml                333 |     75   |   409 |    82 |   200 |    0 |   201
lxml                355 |     93   |   448 |    95 |   182 |   32 |   214
lxml                310 |     94   |   404 |    96 |   156 |   56 |   213
------------------------+----------+-------+-------+-------+------+------
name           generate | tostring | total | write | parse | find | total
------------------------+----------+-------+-------+-------+------+------
</pre>
<p>Note that the first &#8220;total&#8221; is &#8220;generate + tostring&#8221; while the second &#8220;total&#8221; is for the 2 parsing related tests (previous 2 columns summed).</p>
<p>My parsing tests are basically &#8220;etree.parse&#8221; and then running &#8220;Element.getchildren()&#8221; 3 times, which is ridiculously simplistic and should probably be ignored.  My writing tests are far more thorough/realistic.</p>
<p>I&#8217;m running Python 2.6.2 with lxml 2.1.5 and libxml2 2.6.32 on Ubuntu 9.04 x86_64.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.schmichael.com/2009/10/14/lxml-vs-elementtree/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>
