<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>John P Wood</title>
	
	<link>http://johnpwood.net</link>
	<description>collection of thoughts...</description>
	<lastBuildDate>Thu, 10 May 2012 16:37:51 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/johnpwood" /><feedburner:info uri="johnpwood" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Want to Build a Better Web API?  Build a Client Library!</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/yHD0IwnIAQg/</link>
		<comments>http://johnpwood.net/2012/05/10/want-to-build-a-better-web-api/#comments</comments>
		<pubDate>Thu, 10 May 2012 16:37:51 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[client library]]></category>
		<category><![CDATA[http]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=1445</guid>
		<description><![CDATA[A solid web API can be an important thing to have. Not only is it great to give users direct access to their data, but exposing data and operations via [...]]]></description>
			<content:encoded><![CDATA[<p>A solid web API can be an important thing to have.  Not only is it great to give users direct access to their data, but exposing data and operations via a web API enables your users to help themselves when it comes to building functionality that doesn&#8217;t really make sense in the application itself (or functionality that you never really thought of).  It&#8217;s also a great way for users to get more familiar with your service.</p>
<p>However, if your API sucks, you can rest assured that nobody will touch it.  We&#8217;ve all had to deal with crappy web APIs, the ones that make you jump through hoops in order to perform a task that should be dead simple to do.  Web APIs should make the simple tasks easy, and the hard tasks possible.  To add to the challenge, APIs are notoriously difficult to change.  Even with a solid versioning scheme, it is often a real chore to get your users to stop using the deprecated API in favor of the new version.  So, it&#8217;s important to do a good job the first time.</p>
<p>When building a web API, identifying the tasks that one might want to perform can sometimes be difficult to see when you&#8217;re surrounded by JSON, XML, GETs, POSTs, PUTs, DELETEs, and HTTP status codes.  While it can be easy to see what single actions you would want to expose, seeing how those actions may interact with each other can be much more difficult.  Sometimes you need to take a step back, away from the land of HTTP, in order to see your API as another programmer would see it.</p>
<p>Building a client library that wraps your web API is a great way to do this.  It&#8217;s relatively easy to imagine how your requests and responses could be represented as objects.  The largest benefit of this exercise is to take it a step further, and give the user of your client library the ability to determine what they should do next.  Simply knowing if an API call succeeded or failed is usually not enough.  Users of your client library need to be able to determine why the request failed, and understand what they can do about it.  This extends well beyond the lifecycle of a single HTTP request and response.</p>
<h2>Communicating errors</h2>
<p>There are several different ways to communicate errors to the user.  The proper use of <a href="http://en.wikipedia.org/wiki/List_of_HTTP_status_codes">HTTP status codes</a> is one such way.  The 4xx class of status codes are specifically intended to be used to communicate that something was wrong with the client&#8217;s request.  If your API methods are simple, and specific in their purpose, you may be able to rely on HTTP status codes alone to communicate the various causes of failure to the client.</p>
<p>If your API method is complex, and could result in many different failure scenarios, you should first try to break it down into smaller, more specific API methods :)  If that can&#8217;t be done, then another option is to return some easily parseable text in the response body (JSON or XML) that includes an error code that identifies the specific failure scenario.  The response body could be as simple as:</p>
<pre class="brush:js">
{ "error_code" : 123 }
</pre>
<p>You could also provide a description of the error in the response as well.  This helps users getting started with the API, saving them from having to constantly refer to your API&#8217;s documentation every time they get an error:</p>
<pre class="brush:js">
{
  "error_code" : 123,
  "error_message" : "A widget with that name already exists"
}
</pre>
<p>The important thing is that all failure cases be easily identifiable via a specific, documented code (HTTP status code or custom error code).  Error messages should be seen as purely supplemental information.  At no point should your users have to parse the error message to determine what happened, or what they should do next.</p>
<h2>Isn&#8217;t this the same as &#8220;dogfooding&#8221;</h2>
<p>Not exactly.  Dogfooding simply involves using what you have created.  You could easily dogfood your web API by firing HTTP requests at it using a simple HTTP client library.  It is not until you need to take different actions based on different responses that you really start to see if you are properly communicating the result of the request.  Building a client helps with this because it forces you to think about the different results and error scenarios in order to decide how your client should handle them.  Which failures should raise exceptions?  What sort of exception should be raised?  How should non exceptional failures be communicated to the caller?</p>
<p>The next step in this process would be to build an application that uses your client library.  That step could help identify issues with your client library, just like building the client library helps identify issues with your web API.</p>
<h2>The client library</h2>
<p>Oh, and don&#8217;t forget.  At the end of the day, you&#8217;ll end up with a better designed web API, <strong>AND</strong> a great client library that your users can use to interact with your system.  Not a bad deal!</p>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/yHD0IwnIAQg" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2012/05/10/want-to-build-a-better-web-api/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://johnpwood.net/2012/05/10/want-to-build-a-better-web-api/</feedburner:origLink></item>
		<item>
		<title>Professionals Act Professional</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/FiBUUzwtLhk/</link>
		<comments>http://johnpwood.net/2012/04/12/professionals-act-professional/#comments</comments>
		<pubDate>Thu, 12 Apr 2012 20:31:17 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[community]]></category>
		<category><![CDATA[professionalism]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=1433</guid>
		<description><![CDATA[I&#8217;m sick of it. Every week (at least it feels that way) some new drama rears its ugly head in the ruby community. Petty arguments via twitter, one ranting blog [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m sick of it.</p>
<p>Every week (at least it feels that way) some new drama rears its ugly head in the ruby community.  Petty arguments via twitter, one ranting blog post after another, people mocking ideas they consider less than ideal, and even some personal attacks thrown in the mix.  There&#8217;s so much drama in fact that there is now a site out there that lists it all for the rest of the world to see.</p>
<p>Seriously?  Are we all still in junior high?  </p>
<p>Just think for a minute about all of the time and energy we are wasting here.  Instead of igniting these flame wars, from which nothing productive is ever achieved, we could be growing as a community.  We could be bringing up the next generation of software developers.  We could be positively encouraging others to build better software.  We could be sharing our experiences with others.  We could be leading by example.</p>
<p>For a community of people complaining that they&#8217;re not treated like professionals, we sure don&#8217;t act very professional.  If this is the way we behave, can we honestly expect people to treat us with the respect that they treat doctors, accountants, teachers, and members of other professions?</p>
<p>If you want to be treated like a professional, it&#8217;s best to start acting like one first.</p>
<p>Take the high road for once.  The view is much nicer.</p>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/FiBUUzwtLhk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2012/04/12/professionals-act-professional/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://johnpwood.net/2012/04/12/professionals-act-professional/</feedburner:origLink></item>
		<item>
		<title>ReloadablePath in Rails 3</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/bKVJcZzcGY4/</link>
		<comments>http://johnpwood.net/2012/03/02/reloadablepath-in-rails-3/#comments</comments>
		<pubDate>Fri, 02 Mar 2012 12:40:57 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[rails3]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=1360</guid>
		<description><![CDATA[A core feature of the Signal application is support for custom promotion web forms. Custom promotion web forms allow our customers to create custom web pages that will allow their [...]]]></description>
			<content:encoded><![CDATA[<p>A core feature of the <a href="http://www.signalhq.com">Signal</a> application is support for <a href="http://www.signalhq.com/features/custom-promotion-web-forms">custom promotion web forms</a>.  Custom promotion web forms allow our customers to create custom web pages that will allow their customers to interact with their promotions via the web.  Our customers currently use these web forms for sweepstakes entry, email/SMS subscription list opt-ins, online polls, and more.</p>
<p>One of the best things about custom promotion web forms is that it allows our customers to completely control what the web page looks like.  The Signal application allows for the creation of a web form theme that can be used as a template for a given promotion.  The specific promotion can then customize the web page further by specifying the copy that appears on the web page, the data attributes that should be collected, and more.</p>
<p>The web form themes are managed by the Signal application, and are saved to disk as a view (an ERB template) when created or updated.  Our customers can edit these themes at any time.  When a theme is updated, we need to tell Rails to clear the cache for these specific views, so our customer will see their changes the next time they visit a web page that uses the updated theme.</p>
<p>In Rails 2, this was done using <code>ReloadablePath</code>.</p>
<pre class="brush:ruby">
class SomeController < ApplicationController
  prepend_view_path
    ActionView::ReloadableTemplate::ReloadablePath.new(
      "/path/to/my/reloadable/views")
end
</pre>
<p>However, <code>ReloadablePath</code> is no more in Rails 3.  So, we needed to find a new solution to this problem.</p>
<p>Rails 3 introduced the concept of a <a href="http://api.rubyonrails.org/classes/ActionView/Resolver.html"><code>Resolver</code></a>, which is responsible for finding, loading, and caching views.  Rails 3 also comes with a <a href="http://api.rubyonrails.org/classes/ActionView/FileSystemResolver.html"><code>FileSystemResolver</code></a> that the framework uses to find and load view templates that are stored on the file system.</p>
<p><code>FileSystemResolver</code> is very close to what we want.  However, we need the ability to clear the view cache whenever one of the web form themes has been updated.  Thankfully, this was fairly easy to do by creating a new Resolver that extends <code>FileSystemResolver</code>, which is capable of clearing the view cache if it determines that it needs to be cleared.</p>
<p>Looking at the <a href="https://github.com/rails/rails/blob/3-1-stable/actionpack/lib/action_view/template/resolver.rb#L44-46">code for the <code>Resolver</code> class</a>, you can see that it checks the view cache in the <code>find_all</code> method.  If it does not have the particular view cached, it will proceed to load it using the proper Resolver.  So, we simply have to override <code>find_all</code> to clear the cache if necessary before delegating the work to the super class to find, load, and cache the view.</p>
<pre class="brush:ruby">
class ReloadablePathResolver < ActionView::FileSystemResolver

  def initialize
    super("/path/to/my/reloadable/views")
  end

  def find_all(*args)
    clear_cache_if_necessary
    super
  end

  def self.cache_key
    ActiveSupport::Cache.expand_cache_key("updated_at",
      "reloadable_templates")
  end

  private

  def clear_cache_if_necessary
    last_updated = Rails.cache.fetch(ReloadablePathResolver.cache_key) { Time.now }

    if @cache_last_updated.nil? ||
        @cache_last_updated < last_updated
      Rails.logger.info "Reloading reloadable templates"
      clear_cache
      @cache_last_updated = last_updated
    end
  end

end
</pre>
<p>Since we're running multiple processes in production, we need a way to signal all processes that their view caches should be cleared.  So, we're using memcache to store the time that the web form themes were last updated.  Each process then checks that timestamp against the time that particular process last updated its cache.  If the timestamp in memcache is more recent, then the <code>ReloadablePathResolver</code> will clear the cache using the <a href="http://api.rubyonrails.org/classes/ActionView/Resolver.html#method-i-clear_cache">clear_cache</a> method it inherited from <code>Resolver</code>.</p>
<p>Next, we need to add some code that will update memcache any time a web form theme has been updated and saved to disk.</p>
<pre class="brush:ruby">
class WebFormTheme < ActiveRecord::Base
  after_save :update_cache_timestamp

  private

  def update_cache_timestamp
    Rails.cache.write(ReloadablePathResolver.cache_key, Time.now)
  end
end
</pre>
<p>The final step is to simply prepend the view path with the new <code>ReloadablePathResolver</code>.</p>
<pre class="brush:ruby">
class SomeController < ApplicationController
  prepend_view_path ReloadablePathResolver.new
end
</pre>
<h4>References</h4>
<li>
<ul><a href="http://www.arailsdemo.com/posts/50">Using View Templates From The Database - Mongoid and ActionView::Resolver</a></ul>
</li>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/bKVJcZzcGY4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2012/03/02/reloadablepath-in-rails-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://johnpwood.net/2012/03/02/reloadablepath-in-rails-3/</feedburner:origLink></item>
		<item>
		<title>Beware the Hack!</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/TTvwmsL0Frk/</link>
		<comments>http://johnpwood.net/2012/02/14/beware-the-hac/#comments</comments>
		<pubDate>Tue, 14 Feb 2012 14:36:21 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[hacks]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=1345</guid>
		<description><![CDATA[Hacks are dangerous little creatures. They live in the darkest, dustiest corners of your application, forgotten about, waiting&#8230; Waiting for the chance to rear their ugly little heads, open their [...]]]></description>
			<content:encoded><![CDATA[<p>Hacks are dangerous little creatures.  They live in the darkest, dustiest corners of your application, forgotten about, waiting&#8230;  Waiting for the chance to rear their ugly little heads, open their disease infested mouths, and sink their jagged teeth into customer confidence and developer productivity.</p>
<p>We&#8217;ve all been there.  We have a product that works great.  It solves a certain problem incredibly well.  Then, a well meaning customer comes along and says, “This is fantastic.  It almost solves my problem perfectly.  Is there any way you can modify it slightly to do X instead of Y.”</p>
<p>Sometimes this is no problem at all.  Sometimes the design of the product is flexible in ways that make it a breeze to add this functionality.  But, sometimes the request comes out of left field, and takes your product in a direction you never anticipated.  While we strive to build software that is extensible and adaptable (it is <strong><em>soft</em></strong>ware after all, isn&#8217;t it?), none of us can see the future, nor anticipate every possible customer request.</p>
<p>About this time you start to hear a little voice inside your head.  “Well, I suppose I can hard code this, or add an if statement here, or write a one-off script to do X”.  After all, you don&#8217;t want to tell your customer “No, sorry, we can&#8217;t do that”.  And, they certainly don&#8217;t want to hear “Sure, we can make that modification, but it will require a significant amount of refactoring in order to &#8216;do it right&#8217;”.</p>
<p>Acting on these thoughts births a tiny baby Hack.  The Hack is little when it&#8217;s born, but it certainly doesn&#8217;t stay that way.  Once the Hack is born, it is much easier to add to the Hack, or feed it.  With everybody modification to the Hack, it gets bigger, and bigger.  Pretty soon you have a large, ugly Hack with a nasty attitude on your hands.  And, despite you being it&#8217;s “mommy”, it doesn&#8217;t like you, at all.  Not one bit.</p>
<p>Hacks are dangerous for several reasons.</p>
<p>First, they almost always live outside the main execution path of the code.  This means they&#8217;re not executed nearly as often as the other code.  Even if you have a series of tests for the Hack, nothing exercises code like constant execution by your customers.  Also, because they&#8217;re not really “part of the application”, Hacks are often forgotten about when updating or fixing code.</p>
<p>Second, they&#8217;re usually created to quickly get around some issue.  And by “quickly”, I mean “didn&#8217;t totally think this though, but I&#8217;m fairly certain that if I tweak X, alter Y, and drive it with a custom script, it <strong><em>should</em></strong> work just fine”.  And, usually it does work just fine&#8230;at least in the beginning.  But, this is when the Hack is still young, and under your control.  Adult Hacks are not nearly as cooperative.</p>
<p>Third, they&#8217;re usually only known about (at least in detail) by the members of the team that created them.  A Hack is like a big, puss filled pimple on your ass.  You don&#8217;t go around showing those to your friends and co-workers, do you?  Hacks, by definition, are quick and dirty solutions to problems.  They&#8217;re not elegant, or sexy.  So, developers tend to keep their Hacks to themselves.  At most, a developer will mention that they hacked around a problem, but rarely do they go into details.  The other team members are largely left in the dark.  Not knowing where a Hack lives or how it behaves is a sure fire way to get bitten by it down the road.</p>
<p>Always remember, that little baby Hack&#8230;it will grow up.  It will get nasty.  It will bite.  It&#8217;s just a matter of when and where.  Those who have been programming for long enough know this to be fact.  And, being nasty little creatures, Hacks usually wait until the worst possible time to bite.</p>
<p>So, beware the Hack!  They are big, ugly, mean, have teeth, and will most certainly bite.</p>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/TTvwmsL0Frk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2012/02/14/beware-the-hac/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://johnpwood.net/2012/02/14/beware-the-hac/</feedburner:origLink></item>
		<item>
		<title>An Adjustment in Priorities</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/DvJEWjjwPgc/</link>
		<comments>http://johnpwood.net/2011/12/10/an-adjustment-in-priorities/#comments</comments>
		<pubDate>Sat, 10 Dec 2011 14:44:50 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Life]]></category>
		<category><![CDATA[career development]]></category>
		<category><![CDATA[continued education]]></category>
		<category><![CDATA[personal projects]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=1324</guid>
		<description><![CDATA[I think it is very common this time of year for people to reflect, especially on their accomplishments during the past year, and what they would like to accomplish next [...]]]></description>
			<content:encoded><![CDATA[<p>I think it is very common this time of year for people to reflect, especially on their accomplishments during the past year, and what they would like to accomplish next year.  Personally, I&#8217;ve been doing quite a bit of thinking about my development as a software engineer.</p>
<p>It feels as though, over the past couple of years, the pace at which I have been learning has slowed.  This troubles me.  One of the reasons I love the software industry is that there is always something new to learn.  I love learning about new ways to solve problems with technology.  And, learning is what you must continue to do if you are to stay relevant as a software engineer.</p>
<p>Now, this doesn&#8217;t mean that I haven&#8217;t learned anything over the past couple of years.  Quite the contrary actually.  I&#8217;ve really been enjoying the boom our industry is seeing in non relational data storage systems.  We&#8217;ve been utilizing many of these newer technologies at Signal with <a href="http://johnpwood.net/2011/05/31/fast-queries-on-large-datasets-using-mongodb-and-summary-documents/">great</a> <a href="http://johnpwood.net/2009/06/15/couchdb-a-case-study/">success</a>.  I&#8217;ve been an active member of the <a href="http://chicagodb.gathers.us/">ChicagoDB</a> users group, attended a few <a href="http://windycitydb.org/">conferences</a> on this topic, and even <a href="http://www.slideshare.net/jwoodslideshare/real-world-couchdb">gave</a> <a href="http://www.slideshare.net/jwoodslideshare/tenacity">a</a> <a href="http://www.slideshare.net/jwoodslideshare/introduction-to-couchdb-4984849">few</a> <a href="http://www.slideshare.net/jwoodslideshare/polyglot-persistence-two-great-tastes-that-taste-great-together-4625004">talks</a>&#8230;a first for me.  I&#8217;ve also been learning first hand how to scale a system as we&#8217;ve taken the <a href="http://www.signalhq.com">Signal</a> application from one capable of sending thousands of messages per “blast” to one that regularly sends millions of messages per “blast”.</p>
<p>However, something is missing.  I used to read more.  I used to blog more.  I used make a sincere effort to learn a new programming language every year.  I used to constantly download and tinker with new tools.  These things have fallen to the wayside.  And, I really miss them.</p>
<p>Instead, over the past couple of years, I have been spending the majority of my time creating.  When creating a new project, there is usually something new to learn.  That&#8217;s what drives me to create the project in the first place.  But as time goes on, the cost of the time I spend working on the project starts to outweigh the benefit.  I reach a point where I am no longer learning anything while working on the project.  If I&#8217;m not benefiting from working on the project in any way (now that the learning part has mostly dried up), there is little motivation for me to keep working on it.</p>
<p>Part of me feels obligated to keep these projects going, and to get them to a point that I would consider “done”.  I&#8217;ve never been a quitter, and stopping work on these projects sounds like quitting to me.  However, it makes little sense to continue working on something that gives me nothing in return.  I will still fix bugs that are reported, but any new development on most of my active projects is likely going to cease.  Time is precious, and I need to make the most of it.</p>
<p>So as of right now, I&#8217;m making an adjustment in priorities.  I&#8217;m using my free time to invest in myself.  To read more.  To blog more.  To get back to learning a new programming language a year (Clojure is on tap for 2012&#8230;I&#8217;ve been wanting to learn it for 3 years now).  To resume downloading and tinkering with tools that spark my curiosity.  I want to start practicing how to write code, like I learned at the code retreat I attended over the summer.</p>
<p>I&#8217;ll still be creating, as I&#8217;m someone who learns by doing, but I have a feeling from now on my projects will be much, much smaller.</p>
<p>It is impossible to “find” time to do something.  You have to make time.</p>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/DvJEWjjwPgc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2011/12/10/an-adjustment-in-priorities/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://johnpwood.net/2011/12/10/an-adjustment-in-priorities/</feedburner:origLink></item>
		<item>
		<title>Introducing Proby – Task monitoring made simple!</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/QOBPAA8h6RY/</link>
		<comments>http://johnpwood.net/2011/09/19/introducing-proby-task-monitoring-made-simple/#comments</comments>
		<pubDate>Mon, 19 Sep 2011 15:50:20 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[personal projects]]></category>
		<category><![CDATA[proby]]></category>
		<category><![CDATA[signal]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=1293</guid>
		<description><![CDATA[Note: This entry has been cross-posted from the Signal company blog. One Monday morning about a month ago, I was browsing through open issues in our bug tracker looking for [...]]]></description>
			<content:encoded><![CDATA[<p>Note: This entry has been cross-posted from the <a href="http://blog.signalhq.com/2011/09/19/introducing-proby-task-monitoring-simple/">Signal company blog</a>.</p>
<p><a href="http://probyapp.com"><br />
<img src="http://johnpwood.net/wp-content/uploads/2011/09/proby_logo.png" alt="" title="proby_logo" width="225" height="68" class="alignright size-full wp-image-1296" /><br />
</a></p>
<p>One Monday morning about a month ago, I was browsing through open issues in our bug tracker looking for something to work on.  It was my week on &#8220;technical support&#8221;.  At Signal, the engineer on technical support spends a week working on non customer facing issues with our infrastructure, process, etc, that are too large to simply knock out as you encounter them.</p>
<p>An issue entitled <em>&#8220;Add monitoring to detect jobs that do not start&#8221;</em> caught my attention.  We already had monitoring in place to alert us when a job fails.  However, we had nothing to alert us if that job never starts in the first place.  We have many scheduled jobs that run in the background, some pretty important, and have been bitten on more than one occasion by reoccurring jobs that had not run for quite some time.</p>
<p>I bounced some ideas off the team, and we all agreed that that it made sense to create a new application to monitor our scheduled tasks.  The app would know what is supposed to run, and when.  We would change all of our jobs to &#8220;ping&#8221; the app when they start and finish.  If the app detects that something did not run when it was supposed to, or did not finish when it was supposed to, it could alert the team.</p>
<p>And just like that, <a href="http://probyapp.com">Proby</a> was born.</p>
<p><em>“What&#8217;s with the name?”</em>, you ask?  I likened the application to a probation officer, keeping an eye on wayward tasks.</p>
<p>Since going live, Proby has caught several issues with our scheduled jobs that would have gone unnoticed&#8230;many more than I would have anticipated.  It has also evolved into a fairly complete application.  It is easy to setup new tasks.  It holds onto the execution history of each task, allowing you to see a trend of the run time for each task, and which runs resulted in an alarm.  It supports alarms via email or SMS, using Signal&#8217;s SMS messaging API.  It has several settings that allow you to tweak exactly when an alarm is sent, cutting down on the number of false alarms.  And, thanks to designer extraordinaire <a href="http://www.signalhq.com/about-us/team/drew-myler">Drew Myler</a>, it <a href="http://probyapp.com/screenshots">looks amazing</a>!</p>
<p>Shortly after creating Proby, we realized that there is no way we are the only ones who have had scheduled tasks go days without running, unnoticed.  So today, we are making Proby available to the general public.  It is currently in closed beta, and we are letting people in slowly to flush out any bugs or other issues.  <strong>Use of Proby is also FREE while it is in beta.</strong></p>
<p>Interested?  Head on over to <a href="http://probyapp.com/signup">http://probyapp.com/signup</a> and signup to participate in the closed beta!  And, <strong>don&#8217;t forget to <a href="http://twitter.com/#!/probyapp">follow @probyapp on Twitter</a></strong> for updates!</p>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/QOBPAA8h6RY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2011/09/19/introducing-proby-task-monitoring-made-simple/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://johnpwood.net/2011/09/19/introducing-proby-task-monitoring-made-simple/</feedburner:origLink></item>
		<item>
		<title>What I Learned by Attending a Code Retreat</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/Qh2NjWrgfSc/</link>
		<comments>http://johnpwood.net/2011/07/29/what-i-learned-by-attending-a-code-retreat/#comments</comments>
		<pubDate>Fri, 29 Jul 2011 13:23:42 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[career development]]></category>
		<category><![CDATA[code retreat]]></category>
		<category><![CDATA[continued education]]></category>
		<category><![CDATA[practice]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=1258</guid>
		<description><![CDATA[On Tuesday, July 26th, I attended my first code retreat. The code retreat was led by Corey Haines and Tyler Jennings, sponsored by Obtiva, and held to coincide with Tech [...]]]></description>
			<content:encoded><![CDATA[<p>On Tuesday, July 26th, I attended my first code retreat.  The code retreat was led by <a href="https://twitter.com/#!/coreyhaines">Corey Haines</a> and <a href="http://twitter.com/#!/Tyler_Jennings">Tyler Jennings</a>, sponsored by <a href="http://obtiva.com/">Obtiva</a>, and held to coincide with <a href="http://techweek.com/">Tech Week</a>.  Simply put, it was invaluable experience, and I can&#8217;t wait for the chance to do it again.</p>
<h2>What the hell is a code retreat?</h2>
<p>For those who have never attended a <a href="http://coderetreat.com/">code retreat</a>, the format is simple.  The group is given a problem.  In our case the problem was <a href="http://en.wikipedia.org/wiki/Conway's_Game_of_Life">Conway&#8217;s Game of Life</a>.  The code retreat is then broken up into several 45 minute sessions.  Each session you pair with somebody new, and you take a stab at solving some portion of the problem.  All code is written using <a href="http://en.wikipedia.org/wiki/Test-driven_development">TDD</a>.  At the end of each session you delete all of the code you created.  The problem, and the time limit on the sessions, are specifically chosen so that there is little to no chance that you will come up with a complete solution in the allowed time.  Solving the problem is not the point.  <strong>The point is to practice.</strong></p>
<p>We as software developers don&#8217;t give ourselves enough time to practice.  At work, or even with open source or side projects, we&#8217;re always trying to get something done.  Rarely, if at all, do we practice solely for the sake of practice.  This is what we focused on at the code retreat.  At the end of each 45 minute session, we deleted our code, and discussed what we learned.  We were encouraged to try new ideas, to really push ourselves outside of our comfort zone.  Knowing that the code would be deleted in a matter of minutes, there was no need to worry about screwing up the design, or creating something that would ultimately become unmaintainable.  We had a safety net, and were encouraged to use it.</p>
<p>Corey said that we never get the chance to write &#8220;perfect code&#8221;.  Perfect code doesn&#8217;t exist in the real world.  In the real world products need to ship.  It is simply not practical to spend the amount of time on a piece of code that is required to make it &#8220;perfect&#8221;.  There are always trade offs of one form or another.  However, code retreats give you the opportunity to practice writing perfect code.  There is nothing to ship, nothing to “get done”.  The code lives for 45 minutes, and then it disappears, giving you a unique opportunity to practice writing tiny pieces of perfect code.</p>
<h2>Structured chaos</h2>
<p>Sounds pretty hectic, right?  But, the sessions were far from unstructured.  Throughout the retreat, we were encouraged to keep in mind the four rules of simple design:</p>
<ul>
<li>Runs all the tests</li>
<li>No duplication</li>
<li>Proper naming (expresses ideas and reveals intent)</li>
<li>Small (small methods, small classes)</li>
</ul>
<p>The morning sessions were primarily focused on getting familiar with the problem.  As the day progressed, we were encouraged by Tyler and Corey to try out certain ideas, or to think about tackling old problems in new ways.  Can you solve the issue using polymorphism instead of if statements?  Can you do it by capping all of your methods to 3 lines?  What happens when you focus on proper naming of your tests and your methods?  These &#8220;challenges&#8221; were designed to push us out of our comfort zone.  It worked.</p>
<h2>Some thoughts</h2>
<p>I quickly realized that 45 minutes was not enough time to solve the problem.  However, it took me almost the entire day to finally get comfortable with the fact that even solving a significant portion of the problem was out of reach.  About half way through the day, I caught myself trying to solve a chunk of the problem that was way too large for the amount of time we were given.  This resulted in me rushing a solution in an attempt to get my way too large of scope test to pass.  At the end of the session, I was left confused with a pile of crappy code (unnecessary abstractions, no encapsulation, etc), at least half of which I wasn&#8217;t sure why we wrote.</p>
<p>I love that feeling you get when you create something, or solve a problem.  This is why I am a programmer.  Not being able to finish the problem, or even a significant portion of it, was incredibly frustrating.  It took me the entire day to finally let go of that.</p>
<p>It was also great to see the different ways people were approaching the problem.  At the start of the code retreat most pairs were pursuing a similar approach.  But by the end of the day, the variety of approaches was simply amazing.  With each session, everybody learned something new.  Since we were required to find a new partner for each session, the combined experience of each new team seemed to produce a new approach, slightly different from the approaches each of the partners had tried in previous sessions.  It was almost like looking at an old tree.  Each session resulted in the forking of each branch on the tree.  By then end of the day, there were a whole lot of branches.</p>
<h2>Practice, practice, practice&#8230;</h2>
<p>At the end of the code retreat, we all briefly shared with the group what we learned that day.  Everybody had something new to say.  The code retreat had a way of showing all of us what we needed to work on and what we needed to explore.</p>
<p>When writing this blog post, it suddenly hit me.</p>
<blockquote><p><strong>This is exactly what practice is designed to do!</strong></p></blockquote>
<p>When I practice something else (karate basics or kata for example), it has the same effect.  <strong>Serious practice makes it very clear what needs to be developed.</strong>  It makes no difference what it is that you&#8217;re practicing.  The key to getting better at something is to practice, and to continue developing the &#8220;problem areas&#8221; identified by your practice.  But it can&#8217;t be just any practice.  As Corey stated during the code retreat:</p>
<blockquote><p><strong>Practice doesn&#8217;t make perfect.  Perfect practice makes perfect.</strong></p></blockquote>
<p>Perhaps the single biggest thing I took away from the code retreat is that it showed me how to practice writing code.</p>
<h2>What I learned</h2>
<p>First off, I learned that I need to slow down&#8230;way down.  All too often I think I know the solution to a problem before my fingers even touch the keyboard.  And, perhaps worse, is that I am so focused on implementing <strong>that</strong> solution, that I will often ignore warning signs (code smells) that should be alerting me that my solution may perhaps not be the best solution.  This code retreat  made me realize that I need to do a much better job of listening to my tests, and listening to my code.  I need to do a better job of keeping my nose open at all times for code smells, and to stop when I smell something.  Something that doesn&#8217;t feel right in the tests is a sure sign that something is wrong with the design.  Stop, take a step back, think for a minute about what is causing the smell, and fix it.</p>
<p>There is nothing wrong with having a solution in mind before you start coding.  In fact, I think that most times you want to have a solution in mind before your fingers touch the keyboard.  However, you should keep an open mind as you start to build your solution.  Don&#8217;t assume your initial solution is the best solution.  In fact, it&#8217;s probably safer to assume that it is not, and allow your tests to guide you to a better one.</p>
<h2>Go to a code retreat!</h2>
<p>If you&#8217;re serious about getting better as a programmer, and have never been to a code retreat, your missing out on an incredible learning experience.  Make it a point to go to one.  Since code retreats are essentially group coding practice, you can attend them over and over and be sure to walk away with something new every time.  I&#8217;m already looking forward to the next one.</p>
<p>I have so much to learn.</p>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/Qh2NjWrgfSc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2011/07/29/what-i-learned-by-attending-a-code-retreat/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://johnpwood.net/2011/07/29/what-i-learned-by-attending-a-code-retreat/</feedburner:origLink></item>
		<item>
		<title>Fast Queries on Large Datasets Using MongoDB and Summary Documents</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/X7No6z7dQD0/</link>
		<comments>http://johnpwood.net/2011/05/31/fast-queries-on-large-datasets-using-mongodb-and-summary-documents/#comments</comments>
		<pubDate>Tue, 31 May 2011 18:16:55 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[mongodb signal]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=1204</guid>
		<description><![CDATA[The past few months we at Signal have been spending a considerable amount of time and effort enhancing the performance of our application for some of our larger customers. It [...]]]></description>
			<content:encoded><![CDATA[<p>The past few months we at <a href="http://www.signalhq.com">Signal</a> have been spending a considerable amount of time and effort enhancing the performance of our application for some of our larger customers.  It wasn&#8217;t that long ago that our largest subscription list was only 80,000 subscribers.  We now have many customers with lists topping a million subscribers, with our largest currently sitting at 8.5 million.  That&#8217;s quite a jump in size, and not one that can generally be made without making a few tweaks to the application.  With this jump, certain areas of our application began to slow down considerably.  One such area was subscription list reporting.  Many of the reports were backed by SQL queries that were becoming increasingly expensive to run against the ever growing tables.</p>
<p>To address this issue, we decided to create daily summaries of the subscription list data, and report off of the summary data instead of the raw data.  The vast majority of our list reporting is already broken out by day, so this seemed logical.  And, we decided to use <a href="http://www.mongodb.org/">MongoDB</a> to store the summary data.</p>
<h2>Why MongoDB?</h2>
<p>We are already running MySQL and <a href="http://couchdb.apache.org/">CouchDB</a> in production.  Why not use one of those?  Why introduce a 3rd database product to the architecture?</p>
<p>The summary data is live data, not archived data (like the data we are storing in CouchDB).  As subscriptions are created/destroyed for the current day, we need to increment/decrement the appropriate metrics.  There are also some use cases where we need to alter summary documents for days in the past.  This means we need support for atomic operations in the underlying database, to prevent race conditions from skewing the stats.  CouchDB does not have the ability to atomically update a value in a document with a single call, so that took it out of the running.  Some of our more active lists see several new subscriptions a second.  Dealing with document update conflicts would have been a nightmare.</p>
<p>We decided to go with MongoDB over MySQL because of the structure of the summary data.  We had several metrics that we wanted to keep tabs on (opt ins per day, opt outs per day, etc).  Some of these metrics had a nested nature to them.  For example, in addition to keeping track of the number of opt-outs per day, we also want to keep track of the reasons those subscriptions were canceled.  Also, there are several ways that a user can opt-in to a subscription list.  For example, each list can have several different keywords for entry via SMS.  We needed a way of breaking all of these metrics out by method of entry.</p>
<p>Also, the main data structure containing the stats is repeated twice within the document.  Once for the current day&#8217;s stats, and once for the totals for that subscription list up until the date in the document.  This &#8220;to_date&#8221; data structure keeps us from having to evaluate <strong>ALL</strong> of a list&#8217;s documents in order to determine how many subscribers there are on a given date.  With the &#8220;to_date&#8221; data structure, the specific day&#8217;s document is all we need.</p>
<p>We decided that this data structure was better represented as a single JSON document instead of a series of tables in MySQL.  It seemed much cleaner to have a single document per list, per day that contained all of the information for that list&#8217;s daily activity than to have it scattered about in a series of relational tables.  Choosing MongoDB meant giving up on SQL&#8217;s aggregate functions (AVG(), MAX(), MIN(), SUM(), etc), but the benefits provided by the simplicity of the data structure seemed to make up for the loss of these functions.</p>
<h4>An example summary document</h4>
<pre>
{
  "campaign_id": 1,
  "subscription_campaign_id": 2,
  "account_id": 1,
  "date": ISODate("2010-10-13T00:00:00Z"),
  "stats": {
    "sms_101": {
      "confirmed_opt_ins": 100,
      "unconfirmed_opt_ins": 15,
      "unconfirmed_opt_outs": 10,
      "unconfirmed_opt_out_reasons": { "UI": 5, "CNF": 5 },
      "confirmed_opt_outs": 20,
      "confirmed_opt_out_reasons": { "UI": 10, "CNF": 10}
    }
    "sms_102": {
      "confirmed_opt_ins": 200,
      "unconfirmed_opt_ins": 30,
      "unconfirmed_opt_outs": 20,
      "unconfirmed_opt_out_reasons": { "UI": 5, "CNF": 15 },
      "confirmed_opt_outs": 35,
      "confirmed_opt_out_reasons": { "UI": 15, "CNF": 20 },
    }
    "email": {
      "confirmed_opt_ins": 300,
      "unconfirmed_opt_ins": 35,
      "unconfirmed_opt_outs": 25,
      "unconfirmed_opt_out_reasons": { "UI": 20, "BULK": 5 },
      "confirmed_opt_outs": 30,
      "confirmed_opt_out_reasons": { "UI": 20, "BULK": 10 }
    }
  }
  "to_date": {
    "sms_101": {
      "confirmed_opt_ins": 1100,
      "unconfirmed_opt_ins": 1000,
      "unconfirmed_opt_outs": 200,
      "unconfirmed_opt_out_reasons": { "UI": 100, "CNF": 100 },
      "confirmed_opt_outs": 400,
      "confirmed_opt_out_reasons": { "UI": 300, "CNF": 100 }
    }
    "sms_102": {
      "confirmed_opt_ins": 2200,
      "unconfirmed_opt_ins": 200,
      "unconfirmed_opt_outs": 150,
      "unconfirmed_opt_out_reasons": { "UI": 100, "CNF": 50 },
      "confirmed_opt_outs": 400,
      "confirmed_opt_out_reasons": { "UI": 250, "CNF": 150 }
    }
    "email": {
      "confirmed_opt_ins": 2050,
      "unconfirmed_opt_ins": 125,
      "unconfirmed_opt_outs": 75,
      "unconfirmed_opt_out_reasons": { "UI": 75 },
      "confirmed_opt_outs": 750,
      "confirmed_opt_out_reasons": { "UI": 600, "BULK": 150 }
    }
  }
}
</pre>
<h4>A possible relational database schema</h4>
<p><a href="http://johnpwood.net/wp-content/uploads/2011/05/list_summary_tables.png"><img src="http://johnpwood.net/wp-content/uploads/2011/05/list_summary_tables.png" alt="" title="list_summary_tables" width="521" height="347" class="alignleft size-full wp-image-1229" /></a></p>
<h2>MongoDB&#8217;s Atomic Operations</h2>
<p>As I mentioned above, MongoDB&#8217;s atomic operations were key to us choosing MongoDB for this task.</p>
<p>MongoDB does not support locking or transactions like you would find in a traditional relational database.  However, it does support a <a href="http://www.mongodb.org/display/DOCS/Atomic+Operations">number of operations that are guaranteed to be atomic on a single document</a>.  If your data is designed so that all related data (or at least data that would need to be updated at the same time) is contained within a single document, then the atomic operations supported by MongoDB should be more than enough to handle the majority of use cases.</p>
<p>For this project, we made heavy use of <a href="http://www.mongodb.org/display/DOCS/Updating#Updating-%24inc">the <code>$inc</code> operation</a>.  <code>$inc</code> will atomically increment or decrement a value anywhere in the document by the specified value.  If no property exists in the document with that name, <code>$inc</code> will create it, and set its initial value to the value you wanted to increment it by.  For this project, we simply initialized the data structure holding the metrics to an empty JSON hash when the summary document is created.  The first time <code>$inc</code> is used to increment or decrement some metric, it will insert the metric into the hash, along with the proper initial value.  Subsequent calls to update the document using the $inc operator will then update that value accordingly.</p>
<p>Using the <code>$inc</code> operation also meant we didn&#8217;t have to read the document to get the current value of the field in order to increment/decrement its value.  We were simply able to increment/decrement the value by making one call to the database, keeping things nice and simple.</p>
<h4>Atomically incrementing a document&#8217;s confirmed subscriptions count</h4>
<pre class="brush:ruby">
collection = db.collection('subscription_statistics')
today = Date.today.to_time
collection.update({'campaign_id' => 1, 'date' => today},
  {"$inc" => {"stats.sms_101.confirmed_opt_ins" => 1}},
  {:safe => true})
</pre>
<h2>MapReduce</h2>
<p>MongoDB also supports MapReduce, providing you the ability to evaluate your data in ways that simply can&#8217;t be done using their standard queries.  For this project, we needed to support summing the values of specific keys across several documents (to calculate the total opt-ins over a date range, for example).  Initially, this sounded like a good fit for MapReduce.  However, MapReduce will run the specified map function against each document in the database.  The more documents your database has, the longer it will take MapReduce to run.  I hoped that since we had built an index for the fields that MapReduce was using to determine if a document should be selected, that MongoDB would utilize that index to help find the eligible documents.  Unfortunately that was not the case.</p>
<p><strong>In our case, since we are only dealing with 365 documents for a year&#8217;s worth of statistics, it was considerably faster for us to find the documents using MongoDB&#8217;s standard queries and sum the data in ruby code, than to use MapReduce to do the same.</strong>  If we were evaluating ALL of the documents in the database, then MapReduce would have been a much better option.  I understand that 10gen is hard at work on making MapReduce faster for MongoDB 2.0.  Having a strong MapReduce framework can be a powerful tool for a statistics database.  Hopefully we&#8217;ll be able to utilize it in the future.</p>
<h2>The Results</h2>
<p>The results were staggering.  On our largest list, the overview chart we display showing the current number of subscriptions per day over the last 30 days <strong>went from taking 37726ms to load to just 502ms</strong>.  And the summary report for the list, which contains a wealth of statistics for the list including opt-ins / opt-outs per day, subscriber count by opt in keyword, and a series of summary statistics, <strong>went from taking 64836ms to load to just 322ms</strong>.</p>
<p>Dramatically reducing the size of the data being evaluated had an equally dramatic effect on the amount of time it took to evaluate that data.  And, MongoDBs atomic operations and dynamic queries made this project a blast to work on.</p>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/X7No6z7dQD0" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2011/05/31/fast-queries-on-large-datasets-using-mongodb-and-summary-documents/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		<feedburner:origLink>http://johnpwood.net/2011/05/31/fast-queries-on-large-datasets-using-mongodb-and-summary-documents/</feedburner:origLink></item>
		<item>
		<title>Optional method parameters in Ruby</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/Ag8L5lCnZbk/</link>
		<comments>http://johnpwood.net/2011/04/11/optional-method-parameters-in-ruby/#comments</comments>
		<pubDate>Mon, 11 Apr 2011 15:13:55 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=1139</guid>
		<description><![CDATA[One of the things I love about Ruby is the flexibility it provides when it comes to method parameters. It&#8217;s dead simple to provide default values for one or more [...]]]></description>
			<content:encoded><![CDATA[<p>One of the things I love about Ruby is the flexibility it provides when it comes to method parameters.  It&#8217;s dead simple to provide default values for one or more parameters, to handle variable length argument lists, and to pass blocks of code into a method.  But perhaps my favorite is the ability to tack hash key/value pairs onto the end of a method call, and have those options combined into a Hash on the other side.</p>
<pre class="brush:ruby">
def some_method(required_1, required_2, options={})
  # Do something awesome!
end

some_method("foo", "bar")
some_method("foo", "bar", :option_1 => false, :option_2 => true)
some_method("foo",
            "bar",
            :option_1 => false,
            :option_2 => true,
            :option_3 => "something",
            :option_4 => "something else")
</pre>
<p>This may not look like much.  However, this feature alone is capable of producing some very readable code, and is used extensively in APIs throughout the Ruby ecosystem.  Consider for a moment what these APIs would look like if Ruby did not have this capability, which isn&#8217;t hard to imagine for those of us with a background in a language like Java.  You would either be forced to require that each parameter be specified:</p>
<pre class="brush:ruby">
# What is this code doing?  What do the nil values,
# or even the true and false values map to?
some_method("foo", "bar", false, nil, true, nil)
</pre>
<p>or accept a hash or a request object that contains all of the necessary parameters:</p>
<pre class="brush:ruby">
# This is much more readable, but requires that the
# options hash be created on its own line.
options = {:option_1 => true, :option_2 => false}
some_method("foo", "bar", options)
</pre>
<p>Providing optional parameters via hash key/value paris at the end of a method call produces code that is incredibly readable.  <strong>You have the names of the attributes right next to their corresponding values!</strong>  There is no ambiguity whatsoever as to which values match up with which parameters.</p>
<p>It is also very flexible.  The order of the attributes in the hash does not matter, like it does for required attributes.  And, it is very easy to add new options, or delete old ones.  </p>
<p>This approach also makes it easy to specify default values for options that were not specified when calling the method:</p>
<pre class="brush:ruby">
def some_method(required_1, required_2, options={})
  defaults = {
    :option_1 => "option 1 default",
    :option_2 => "option 2 default",
    :option_3 => "option 3 default",
    :option_4 => "option 4 default"
  }
  options = defaults.merge(options)

  # Do something awesome!
end
</pre>
<p>There are however a few minor drawbacks to this approach.  The first is documentation.  Methods that take a hash of options as a parameter do not convey any information about the valid options in the method definition alone.  And, it is possible that the method in question simply forwards the options to another method, sending you on a wild goose chase to determine the set of valid options the code supports.</p>
<pre class="brush:ruby">
# Looking for a list of valid option keys...no help here.
def some_method(required_1, required_2, options={})
  do_something_awesome_with_the_options(options)
end
</pre>
<p><strong>This is why it is so important do document your public API if you are using this approach.</strong>  Take a look at the <a href="http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html">ActiveRecord::Associations::ClassMethods documentation</a>.  This page documents, in a very clear and easy to read mannor, all of the supported options for each method.</p>
<p>It is also worth pointing out that while this approach is great for optional parameters, it is ill suited for required parameters.  Required parameters should be specified outside of the options hash, making it clear that values for the required parameters must be provided.  While it&#8217;s true that stuffing all of your parameters inside a hash means you&#8217;ll never have to look at another <code>wrong number of arguments</code> error again, it will make your code difficult to understand, and easy to misuse.</p>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/Ag8L5lCnZbk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2011/04/11/optional-method-parameters-in-ruby/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://johnpwood.net/2011/04/11/optional-method-parameters-in-ruby/</feedburner:origLink></item>
		<item>
		<title>Custom Audio Notifications in Campfire</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/osIoIwo7D-8/</link>
		<comments>http://johnpwood.net/2011/03/26/custom-audio-notifications-in-campfire/#comments</comments>
		<pubDate>Sat, 26 Mar 2011 20:09:03 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Scripts]]></category>
		<category><![CDATA[campfire]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=1141</guid>
		<description><![CDATA[Continuous integration is great. What&#8217;s even better is knowing immediately when the build is broken. The past couple of weeks we&#8217;ve been using the Jenkins Campfire Plugin to shoot a [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.martinfowler.com/articles/continuousIntegration.html">Continuous integration</a> is great.  What&#8217;s even better is knowing immediately when the build is broken.  The past couple of weeks we&#8217;ve been using the <a href="http://wiki.jenkins-ci.org/display/JENKINS/Campfire+Plugin">Jenkins Campfire Plugin</a> to shoot a message into our team campfire room to let us know the status of our builds.</p>
<p>This was our first &#8216;bot&#8217; in the room, and it felt a little weird.  My teammate Ray created a persona for the Jenkins bot by associating it with the one and only <a href="http://www.youtube.com/watch?v=LkCNJRfSZBU">Leroy Jenkins</a>, changing the name of the bot from &#8220;Jenkins&#8221; to &#8220;Leeerrroooy J&#8221;.  We also changed the sender name of the emails that Jenkins delivers to match.</p>
<p>But something was missing.  Every time Leeerrroooy J. popped into our chat room with something to say, we&#8217;d all scream <strong>&#8220;Leeerrroooy Jenkins!&#8221;</strong>.  I thought it would be awesome if we could have Campfire do this for us!  But sadly, I couldn&#8217;t find any scripts to play audio in Campfire in response to a certain message.</p>
<p>So, <a href="https://github.com/jwood/campfire-audio-notification">I created one</a>.  Now, every time Leeerrroooy J. lets us know the status of a build, <a href="http://www.leeroyjenkins.net/soundbites/warcry.wav">this</a> will play.  Makes me laugh every time.</p>
<p>Realizing this script could be useful in other contexts, I made it a tad more configurable, and put it on github.  Right now, you can setup the script to play the sound at a given URL any time a specific word is said in the room, or when a certain user posts a message.</p>
<p>Hope you enjoy it as much as I do.</p>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/osIoIwo7D-8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2011/03/26/custom-audio-notifications-in-campfire/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
<enclosure url="http://www.leeroyjenkins.net/soundbites/warcry.wav" length="430206" type="audio/wav" />
		<feedburner:origLink>http://johnpwood.net/2011/03/26/custom-audio-notifications-in-campfire/</feedburner:origLink></item>
		<item>
		<title>CouchDB Plugins for Scout</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/0ljndlt88lg/</link>
		<comments>http://johnpwood.net/2011/01/12/couchdb-plugins-for-scout/#comments</comments>
		<pubDate>Wed, 12 Jan 2011 20:09:23 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[couchdb]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[scout]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=1035</guid>
		<description><![CDATA[Back in December I whipped up a series of CouchDB plugins for the Scout monitoring service. The plugins allow you to track all sorts of metrics for CouchDB, including (but [...]]]></description>
			<content:encoded><![CDATA[<p>
Back in December I whipped up a series of <a href="http://couchdb.apache.org/">CouchDB</a> plugins for the <a href="https://scoutapp.com/">Scout</a> monitoring service.  The plugins allow you to track all sorts of metrics for CouchDB, including (but not limited to):</p>
<ul>
<li>Mean reads / second</li>
<li>Mean writes / second</li>
<li>Mean requests / second for DELETE, GET, HEAD, POST, and PUT requests</li>
<li>Mean view requests / second</li>
<li>Mean bulk HTTP requests / second</li>
<li>Counts for various HTTP response codes</li>
</ul>
<p>In addition, there is a plugin for individual CouchDB databases and individual couchdb-lucene indexes.  The database plugin will report:</p>
<ul>
<li>Database size</li>
<li>Number of documents</li>
<li>Number of deleted documents</li>
<li>Number of update operations</li>
</ul>
<p>The couchdb-lucene plugin will report:</p>
<ul>
<li>Size of the index</li>
<li>Number of documents indexed</li>
<li>Number of deleted documents</li>
</ul>
<p>The kind folks over at Scout have just released two new, official plugins based on the ones I created.  The <a href="https://scoutapp.com/plugin_urls/441-couchdb-overall">CouchDB Overall</a> plugin combines some of the more important CouchDB metrics into a single plugin, and the <a href="https://scoutapp.com/plugin_urls/451-couchdb-database">CouchDB Database</a> plugin reports the same set of the stats as the database plugin listed above.</p>
<p>The original plugins can be found at <a href="https://github.com/signal/scout-plugins/tree/master/couchdb">https://github.com/signal/scout-plugins/tree/master/couchdb</a>.  More information can be found <a href="https://github.com/signal/scout-plugins/blob/master/couchdb/README.md">here</a>.  I hope you find them useful.</p>
<p>Thanks to Doug Barth for some help on the plugins, and Derek over at Scout for putting together the official plugins.</p>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/0ljndlt88lg" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2011/01/12/couchdb-plugins-for-scout/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://johnpwood.net/2011/01/12/couchdb-plugins-for-scout/</feedburner:origLink></item>
		<item>
		<title>Introducing Tenacity – An ORM Independent Way to Manage Inter-database Relationships</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/IJ5XjiulDEc/</link>
		<comments>http://johnpwood.net/2011/01/05/introducing-tenacity-an-orm-independent-way-to-manage-inter-database-relationships/#comments</comments>
		<pubDate>Wed, 05 Jan 2011 15:45:55 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[nosql]]></category>
		<category><![CDATA[polyglot persistence]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[tenacity]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=1003</guid>
		<description><![CDATA[I&#8217;m a big believer in polyglot persistence. There are so many (very different) production ready databases available today that&#8217;s it is becoming more and more common to find applications using [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m a big believer in <a href="http://www.slideshare.net/jwoodslideshare/polyglot-persistence-two-great-tastes-that-taste-great-together-4625004">polyglot persistence</a>.  There are so many (very different) production ready databases available today that&#8217;s it is becoming more and more common to find <a href="2009/09/29/using-multiple-database-models-in-a-single-application/">applications using more than one database</a>, utilizing the strengths of each.  Using the right tool for the job gives me a warm, fuzzy feeling inside.</p>
<p>However, polyglot persistence comes with its own set of drawbacks.  One of those drawbacks is the loss of foreign keys, which are very important in maintaining data integrity.  Another drawback is that Object/Relational Mapping (ORM) libraries typically focus on a specific database, or type of database.  So, writing code that manages relationships between objects backed by different databases hasn&#8217;t been nearly as easy as writing code to manage relationships between objects in the same database.</p>
<p>Tenacity&#8217;s goal is to address some of these issues.  <a href="https://github.com/jwood/tenacity">Tenacity</a> is a ruby gem that provides an ORM independent way of managing relationships between models backed by different databases.</p>
<p>Tenacity works by <a href="https://github.com/jwood/tenacity/blob/master/EXTEND.rdoc">extending</a> popular Ruby ORM libraries to respond to a set of methods that the tenacity core uses to build and manage relationships between objects.  By extending the ORM libraries to implement this interface, tenacity is able work with the objects in a generic way, without having to know what database is backing the given objects.  This approach also allows you to continue using your favorite ORM libraries.  To use tenacity, you simply need to <code>include Tenacity</code> inside your model.</p>
<p>Tenacity is heavily based on <a href="http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html">ActiveRecord&#8217;s associations</a>, and aims to behave in much the same way, supporting many of the same options.</p>
<p>This initial release of tenacity supports <code>belongs_to</code>, <code>has_one</code>, and <code>has_many</code> associations, and the <a href="http://api.rubyonrails.org/classes/ActiveRecord/Base.html">ActiveRecord</a>, <a href="https://github.com/couchrest/couchrest">CouchRest</a>, and <a href="https://github.com/jnunemaker/mongomapper">MongoMapper</a> ORMs.  However, there is still <a href="https://github.com/jwood/tenacity/issues">plenty of work to be done</a>.  Feedback, <a href="https://github.com/jwood/tenacity/issues">bug reports</a>, and code contributions are always welcome.</p>
<p>Tenacity is free and open source, and can be found on GitHub at <a href="https://github.com/jwood/tenacity">https://github.com/jwood/tenacity</a>.</p>
<h2>Example</h2>
<p><script src="http://gist.github.com/754326.js"></script></p>
<h2>Download</h2>
<p><code>gem install tenacity</code></p>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/IJ5XjiulDEc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2011/01/05/introducing-tenacity-an-orm-independent-way-to-manage-inter-database-relationships/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://johnpwood.net/2011/01/05/introducing-tenacity-an-orm-independent-way-to-manage-inter-database-relationships/</feedburner:origLink></item>
		<item>
		<title>The (Eventual) Return of Mobile Applications to the Web</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/jGZ2UWuI-g0/</link>
		<comments>http://johnpwood.net/2010/11/22/the-eventual-return-of-mobile-applications-to-the-web/#comments</comments>
		<pubDate>Mon, 22 Nov 2010 14:16:53 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[application development]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[mobile web]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=969</guid>
		<description><![CDATA[I don&#8217;t think that I&#8217;m overstating things by saying that the web has changed the world. There is no doubt about it in my mind. The past 20 years or [...]]]></description>
			<content:encoded><![CDATA[<p>I don&#8217;t think that I&#8217;m overstating things by saying that the web has changed the world.  There is no doubt about it in my mind.  The past 20 years or so have been a continuous stream of innovation.  We&#8217;ve seen the web transform from a simple platform used by government agencies to share information into something whose capabilities seem limitless.  Streaming media, e-commerce, full fledged office applications, real time news from more sources than you can imagine, and much more.  It truly is amazing what we&#8217;ve been able to accomplish with a platform whose original purpose was to share static documents.</p>
<h2>The Rise of the App</h2>
<p>However, the web does have limitations.  One such limitation has long been the ability to interact with the hardware you are using to browse the web.  Over the years technologies have been developed to help with this limitation (Java, Flash, etc), but I see those technologies as extensions the web, and not part of the web itself.  Those technologies have carried their own set of drawbacks as well, such as requiring the installation of client software, performance issues, and security vulnerabilities.</p>
<p>Nowhere is this limitation more obvious then in the mobile arena.  Today&#8217;s smartphones are amazing.  You have a camera, a GPS, a MP3 player, a web browser, oh&#8230;and a phone too, all wrapped up in one device.  It&#8217;s like walking around with a tiny computer in your pocket.  But currently, the web is only able to utilize a tiny fraction of this functionality.  Its document centric design, which has been stretched, twisted, and pulled in directions that its creators never could have imagined, appears to have finally reached its limit.  </p>
<p>In addition to hardware access limitations, may web applications were not designed to be viewed on a device with such a small screen and no mouse.  It can be very difficult to view a 900 pixel wide web page on a screen that is only a couple of inches wide.  And while you can zoom in over certain areas of the page, not being able to view and interact with the page in its entirety introduces some big usability issues.  Also, many web applications rely on mouse over events to interact with the user.  Drop down menus are a perfect example of this.  There is no mouse on today&#8217;s touch screen smartphones, making these sites very difficult to navigate when viewed on a mobile device.</p>
<p><a href="http://johnpwood.net/wp-content/uploads/2010/11/apple-app-store.jpg"><img src="http://johnpwood.net/wp-content/uploads/2010/11/apple-app-store.jpg" alt="" title="apple-app-store" width="200" height="200" class="alignleft size-full wp-image-972" /></a></p>
<p>These limitations have given rise to &#8220;the app&#8221;.  The app, a native application that runs on the smartphone itself, it not a new idea.  Smartphones have been around for quite some time, and these phones have always had native applications.  But only recently did Apple change the game by providing independent application developers with a way to easily sell and distribute their applications.  As a result, there are now hundreds of thousands of applications out there for various platforms, many of which do incredible things with the hardware on the mobile device.  Things that simply could not be done via a web application.</p>
<p>The rise of the app came on so quick and so strong that many began to ask <a href="http://ideas.blogs.nytimes.com/2010/08/18/did-the-app-kill-the-web/">if the app killed the web</a>.  This is certainly a reasonable question to ask.  A native application has access to all that the device has to offer; the camera, the GPS, and yes, even the web.  Apps are also designed to run on a mobile device.  So, the text is easy to read, the controls are easy to use, and the user experience is generally much better than a web application.</p>
<h2>Problems with Native Applications</h2>
<p>While native applications have full access the the hardware and address many usability issues that web applications have on mobile devices, they introduce a new set of problems.</p>
<p>Some platforms charge program fees that must be paid to develop applications for the particular platform, even if you plan on creating a free app, or an app you never plan on releasing.  While generally inexpensive, it is a new cost that web application developers have never had to deal with.</p>
<p>Deployment is more complicated for native applications than it is for web applications.  Even though Apple&#8217;s App Store, Google&#8217;s Android Market, and other application marketplaces have made the distribution and installation of native applications much easier, it still does not compare to the ease of deployment of a web application.  On the web, you have complete control over what you deploy, when you deploy it, and to whom you deploy it to.  This is not the case with native applications, which can be subjected to drawn out, illogical review processes before being made available to the public.  If you have a critical bug that needs fixing, or you&#8217;re trying to beat your closest competitor to market with some killer new feature, this process can be agonizing.  And, even after publishing the latest version of your application to the marketplace, there is no way to force your users to upgrade.  This could leave you supporting older, buggy versions of your application, or maintaining old services still in use by those old versions of the application.</p>
<p><a href="http://johnpwood.net/wp-content/uploads/2010/11/xcode.png"><img src="http://johnpwood.net/wp-content/uploads/2010/11/xcode.png" alt="" title="xcode" width="500" height="436" class="alignnone size-full wp-image-975" /></a></p>
<p>If you charge for your application, distribution of that application is not free, or even cheap.  Apple&#8217;s App Store, Google&#8217;s Android Market, and Palm&#8217;s App Store all take a 30% cut of the sale price of your application.  Some may argue that this is a small price to pay for distribution to such a large audience.  However, the audience is larger on the web, and distribution is piratically free.</p>
<p>The development of native applications is largely platform dependent.  Programming languages and APIs vary significantly from platform to platform, making it practically impossible to develop a single application capable of running on multiple platforms.  Cross platform development tools, such as <a href="http://www.phonegap.com/">PhoneGap</a> and <a href="http://rhomobile.com/">Rhomobile</a>, address this issue by allowing you to develop native applications using HTML, Javascript, and CSS.  However, these tools don&#8217;t address the other issues mentioned in this section.  In addition, you also run the risk of some App stores outright banning applications not built using their tools (looking at you Apple).  Though Apple has recently softened their stance on this issue, I would not rule out future restrictions.</p>
<h2>A Mobile Web App in Native App&#8217;s Clothing</h2>
<p>The sad part is, <strong>the majority of today&#8217;s apps don&#8217;t need to be native applications</strong>.  They do nothing to utilize the features exclusive to native applications.  Most of these apps simply fetch data from a web service and display it nicely to the user.  Some take advantage of the GPS, or store data locally on the device.  However, these apps could just as easily be mobile optimized web applications.</p>
<p>&#8220;Look and feel&#8221; is often cited as a reason to develop a native application instead of a mobile optimized web application.  Though this argument certainly has its merits, there are plenty of options out there to help make a web application look and feel great on a mobile device.  A bit of mobile optimized CSS (fluid layouts, properly sizing images, etc) can go a long way in terms of making a web application look awesome on a mobile device.  And, there are projects out there, such as <a href="http://www.jqtouch.com/">jQTouch</a>, <a href="http://jquerymobile.com/">jQuery Mobile</a>, and <a href="http://www.sencha.com/products/touch/">Sencha Touch</a> that can help your web application look and feel more like a native application if that&#8217;s what you are looking for.</p>
<p><a href="http://johnpwood.net/wp-content/uploads/2010/11/hilton.jpg"><img src="http://johnpwood.net/wp-content/uploads/2010/11/hilton.jpg" alt="" title="hilton" width="400" height="258" class="alignright size-full wp-image-973" /></a></p>
<p>Web applications also have access to a few features that, just a short time ago, were only available to native applications.  The device&#8217;s location is accessible via <a href="http://www.dinnermint.org/blog/javascript/javascript-geolocation-api/">Javascript&#8217;s Geolocation API</a>, eliminating the need to build a native application solely for access to the device&#8217;s GPS.  Web applications also have access to persistent local storage on the mobile device via the <a href="http://dev.w3.org/html5/webstorage/">HTML5 Web Storage API</a>.  While the Web Storage API falls short of a being a true database (it&#8217;s more like a hash, allowing you to store key/value pairs), it can go a long way in addressing the storage needs of most applications.</p>
<p>A good comparison between mobile apps and native apps can be found <a href="http://www.webmonkey.com/2010/08/how-do-native-apps-and-web-apps-compare/">here</a>.</p>
<h2>Making the Web a Friendlier Place for Mobile Web Apps</h2>
<p>The mobile web is growing by leaps and bounds, but much work is needed in order to make the web a friendlier place for mobile web applications.  The W3C realizes this, and have started a <a href="http://www.w3.org/2009/dap/">Device APIs and Policy Working Group</a> to create a specification that will allow web applications to access device hardware and services (contacts, calendar, camera, microphone, SMS, MMS, email, etc) in a standardized way.  The specification is far from complete, and it will likely be quite some time before these APIs have been implemented by the various web browsers.  But, the mere fact this is being worked on is a great sign for the future of mobile web applications.</p>
<p>There are non-technical hurdles preventing the web from being a more mobile friendly place as well.  </p>
<p><a href="http://johnpwood.net/wp-content/uploads/2010/11/mobileOK.jpg"><img src="http://johnpwood.net/wp-content/uploads/2010/11/mobileOK.jpg" alt="" title="Mobile OK" width="200" height="186" class="alignleft size-full wp-image-974" /></a></p>
<p>First, browsing the web on a mobile device isn&#8217;t a very pleasant experience right now.  Having &#8220;real&#8221; web browsers on mobile devices, such as WebKit, have gone a long way in fixing this.  However, many sites are not optimized for mobile devices, making them difficult to use.  I think this prevents the majority of people from browsing the web on their mobile device like they would on their desktop or laptop computer.</p>
<p>Second, we&#8217;ve been told for years that &#8220;There&#8217;s an app for that&#8221;.  Personally, when I&#8217;m looking for a mobile application take care of some task, the first place I hit is the Android Market.  Having one place, where apps are broken up into categories, and searchable, makes finding the right app for my needs incredibly easy.  There is no such place for web applications.  This used to be the case for desktop applications as well, but not anymore.  The Ubuntu App Center, and now the Apple Mac Store, aim to serve the same purpose as the App Store or the Android Market, but for desktop computers instead of mobile devices.  Perhaps it won&#8217;t be long before we see a categorized, searchable index for mobile web applications as well.</p>
<h2>Conclusion</h2>
<p>Opting to build a mobile optimized web application instead of a native application is <a href="http://scobleizer.com/2009/12/16/iphone-developers-abandoning-app-model-for-html5/">not a new idea</a>.  However, the idea has been slow to catch on, due to some of the reasons I&#8217;ve outlined above.</p>
<p>Obviously, even after the Device APIs being worked on by the W3C have been implemented, there will always be a need for native applications.  Games that need hardware accelerated graphics, or apps with complex user interaction will most likely stay native applications for the foreseeable future.  But I hope that the other applications, <strong>especially</strong> the applications that are simply a front to a web service, are re-born as web applications at some point in the near future.  For these apps, it&#8217;s time to come home.</p>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/jGZ2UWuI-g0" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2010/11/22/the-eventual-return-of-mobile-applications-to-the-web/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://johnpwood.net/2010/11/22/the-eventual-return-of-mobile-applications-to-the-web/</feedburner:origLink></item>
		<item>
		<title>Script for Creating and Fetching Database Backups on Heroku</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/3WIFNmtfEVw/</link>
		<comments>http://johnpwood.net/2010/11/17/script-for-creating-and-fetching-database-backups-on-heroku/#comments</comments>
		<pubDate>Wed, 17 Nov 2010 14:47:53 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Scripts]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[heroku]]></category>
		<category><![CDATA[scripts]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=955</guid>
		<description><![CDATA[Heroku recently announced a new database backup solution, PG Backups, to replace their previous one, bundles. The basic plan allows you to store two database backups on Heroku&#8217;s servers for [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://heroku.com">Heroku</a> recently announced a new database backup solution, <a href="http://blog.heroku.com/archives/2010/11/16/pgbackups/">PG Backups</a>, to replace their previous one, bundles.  The basic plan allows you to store two database backups on Heroku&#8217;s servers for free.  Since I already have a backup solution which allows me to store any number of backups, I decided to write a script to create database backups on Heroku, download them, and back them up using my existing solution.  PG Backups is incredibly easy to use, and does all of the heavy lifting.</p>
<p>Kudos to Heroku for continuing to find ways to make their platform easier to use.</p>
<p>The script is below.  Hope you find it useful.</p>
<p><script src="http://gist.github.com/703438.js"></script></p>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/3WIFNmtfEVw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2010/11/17/script-for-creating-and-fetching-database-backups-on-heroku/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://johnpwood.net/2010/11/17/script-for-creating-and-fetching-database-backups-on-heroku/</feedburner:origLink></item>
		<item>
		<title>Why I LOVE Working at a Startup</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/3Sr2f4bzQow/</link>
		<comments>http://johnpwood.net/2010/09/26/why-i-love-working-at-a-startup/#comments</comments>
		<pubDate>Sun, 26 Sep 2010 17:32:46 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Tech Industry]]></category>
		<category><![CDATA[startups]]></category>
		<category><![CDATA[work environment]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=909</guid>
		<description><![CDATA[Disclaimer: Not all startups are created equal. This article is based on my work at Signal, which is the only startup I have worked at. However, I have read many [...]]]></description>
			<content:encoded><![CDATA[<p><em><strong>Disclaimer</strong>: Not all startups are created equal.  This article is based on my work at <a href="http://www.signalhq.com">Signal</a>, which is the only startup I have worked at.  However, I have read many articles and have had many conversations with others who work at a startup, and I&#8217;ve found that much of what I&#8217;ve listed here applies to those startups as well.  In addition, many of these points may very well apply to small companies that aren&#8217;t necessarily startups.<br />
</em></p>
<p>After graduating from college I immediately started work at a very large, multi-national communications technology company.  At the time, I liked the idea of working for a large company.  I thought that it would provide room to grow, job security, and the ability to switch roles easily without leaving the company.  Over the past 10 years, I have moved from a company with about 100,000 employees, to a company with about 2,000 employees, and then to a company with 4 employees.  Every time I moved to a company with substantially less employees, I noticed that my job satisfaction increased.</p>
<p>I have never been more happy with a job that I am at Signal.  And, there are several reasons for this.</p>
<h2>The ability to wear all hats</h2>
<p>When I started at Signal there were only 2 other engineers (3 if you include the technical co-founder who was looking to focus less on technology and more on growing the business).  With only three people on the engineering team, you need to be able to work on any part of the system, at any time.  Application design and development, user interface design and development, database administration, system administration, IT, etc.  <strong>In an environment like this there are many opportunities to learn something new.</strong>  Database administration and user interface design were not strong areas of mine, but working at a startup gave me the opportunity to develop those skills.  Much more so than at company where teams are segmented based on the functional areas of the application.  And, even after a year and a half, I&#8217;m still learning new stuff like crazy.</p>
<p>This isn&#8217;t to say that some engineers aren&#8217;t stronger than others in certain areas, and you can&#8217;t play to those strengths.  You&#8217;d be foolish not too.  But at this size, it&#8217;s simply not practical to have one engineer do all the UI work, one do all of the database work, and one do all of the application development.</p>
<h2>The chance to make a really big impact</h2>
<p>I was hired as Signal&#8217;s fifth overall employee.  When there are only five people in the entire company, everybody has the chance to make a really big impact.   The harder you work, the bigger contribution you get to make.  And contributions can take many forms, like a newly implemented feature, providing awesome support to a customer, a new idea for a product, or a new way of resolving a problem that has been facing the company.  There is no product group or customer service team at a very small startup, but the needs traditionally serviced by those roles still exist.  In addition, you are able to see the impact of your contribution immediately, be it positive or negative.</p>
<p>You also have the opportunity to play a bigger role in how your product or service develops.  If you see an area that needs improvement, a bug that needs fixing, or a gaping hole in the product that could be addressed by a new feature, it&#8217;s up to you to address it.  There is no committee or change board that needs to approve the change, and no need to line up resources from the database and UI teams to help with the work.  <strong>You are expected to drive the change.</strong>  This, for me, is a major source of inspiration and motivation.</p>
<h2>The ability to choose your tools</h2>
<p><img src="http://johnpwood.net/wp-content/uploads/2010/09/mac-and-linux-equal-microsoft.jpg" alt="windows vs linux vs mac" title="windows vs linux vs mac" width="180" height="180" style="float:left; margin: 0px 25px 25px 0px;" />  One of the things a software engineer likes to complain about the most are the tools they are forced to use at their company.  And with good reason.  <strong>Using tools that you are comfortable with can make you more productive, and quite frankly, happier.</strong>  Little is more frustrating than being forced to solve a problem one way when you know you can solve it quicker, and better, another way.  This is generally not a problem at startups.  While you rarely have the ability to choose all of your tools (programming language for example), you will generally have the ability to choose your work machine, operating system, editor/IDE, web browser, mail client, etc.  And, since you are your own IT department, there is no question that you will have root access to your work machine.  These may sound like minor details to most people, but any geek will assure you how important this actually is.</p>
<h2>The ability to choose the right technology for the job</h2>
<p>Many companies standardize on technologies.  There are many valid reasons for this.  However, sometimes the standardized option makes little sense for the task at hand, and you are forced trying to fit a square peg through a round hole.  This generally doesn&#8217;t happen at startups.  One of the best things about my job is that the engineering team gets to decide which technologies to use after evaluating the technical details of the problem it is trying to address.  One might think that a stance like this would lead to an explosion of tools and technologies in our stack, but this is hardly the case.  Being responsible and professional developers, we understand that we will have to live with and support the decisions we make.  So, we carefully evaluate any significant new technology before bringing it into the stack.</p>
<h2>A voice in choosing your teammates</h2>
<p>When working on a small team, everybody needs to pull their weight in order for the company to be successful.  <strong>Bringing new people onto the team is a big deal.</strong>  A single bad team member can be a cancer to an otherwise strong and healthy team.  Therefore, people interviewing for the company talk with <strong>everybody</strong>.  This may be a bit overwhelming for candidates, but I think most appreciate how closely we protect our team dynamic.  Now, this isn&#8217;t to say that everybody &#8220;interviews&#8221; each candidate.  Our VP of marketing isn&#8217;t going to ask a candidate for an engineering position to solve a coding problem on the whiteboard.  Instead, a few people will determine if the candidate is qualified for the position, and the rest will determine if the candidate would be a good fit, personality wise, on the team.  As an engineer, I think this is great.  When hiring a new engineer, we all get to ensure that the candidate is smart, loves technology, knows how to solve problems, and will get along with the rest of the team.  And when hiring for a non engineering position, we get to ensure that the person we&#8217;re bringing onto the team isn&#8217;t a jerk.</p>
<h2>A great work culture</h2>
<p><img src="http://johnpwood.net/wp-content/uploads/2010/09/nerd_campfire-300x224.jpg" alt="Nerd Campfire" title="Nerd Campfire" width="300" height="224" style="float: right; margin: 0px 0px 25px 25px;" />  Startup culture is great.  I&#8217;m not talking about $1,000 desk chairs or having a company masseuse.  Hell, two other engineers and I were coding around what the CEO called a &#8220;nerd campfire&#8221; the other day (can&#8217;t say that I disagree with that terminology) because we currently have no desks.  I&#8217;m talking about the camaraderie; eating lunch together, getting to meet each others spouses and children, chilling out and having a few drinks with each other.  I think this is very hard to find outside of small company.  It seems to me that most startups have a casual, relaxed, and fun work environment.  Flexible hours.  No dress code to speak of.  Signal even has a kegerator in the office which is always fully loaded with great beer.  They do pretty much anything they can to make their employees feel comfortable so they  be productive.</p>
<h2>Flexible work options</h2>
<p>Flexible work hours are very popular now-a-days.  Even at the larger companies where I have worked, it wasn&#8217;t uncommon for some people to come in at 11am and stay until 7pm or 8pm.  Having the ability to fit work around your personal schedule is a big plus, and I feel it makes for a much happier employee.  </p>
<p>Even more important to me is the ability to work from home.  I live about 30 miles from the office, which takes about 90 minutes to travel during rush hour by car or train.  I&#8217;ve tried not too complain too much about my commute, because nobody forced me to live where I do.  It was my choice, and I&#8217;d make the same choice again.  But, when I was given the opportunity to work from home, I jumped on it.  Working from home saves me 3 hours a day.  That is huge for me, as it always feels like there are not enough hours in the day.  Working from home 4 days a week has given me the ability to see my wife and kids more often, start running, and to spend a little more time on some side projects to improve and expand my skills as a developer.  Seeing my kids more often is by far the biggest benefit of working from home.  Now that my oldest is in school, I barely see him when I go into the office.  Our schedules only overlap for about 30 minutes in the morning, and 30 minutes at night.  This is not the case when I work from home.</p>
<h2>No bullshit politics</h2>
<p>When there are only 13 people in the entire company, there are no politics.  There is no jockeying for position.  There is no posturing to give the impression that you are an all star employee when you&#8217;re really just a leech.  There is no room to hide.  If you&#8217;re not contributing, everybody knows it.  If you are trying to exploit others for personal gain, everybody sees it.</p>
<h2>Your opinion matters</h2>
<p>It&#8217;s hard to make a decision that impacts the company in complete isolation at a startup.  The founders at Signal always consult the team when it comes to making a big decision.  Make no mistake, they have the final say.  But they value the opinions of the team and constantly request feedback.  This gives the employees a sense of ownership, which is great for morale and can be very motivational.</p>
<h2>Conclusion</h2>
<p>I think many of these points are the product of a few major differences between a big company and a startup.  First, <strong>productivity is paramount</strong>, and anything that can be done to increase productivity is done.  If employees aren&#8217;t producing at their highest level, it greatly increases the risk of the startup folding (delaying time to market or being surpassed by a competitor).  Second, <strong>startups run on the bare minimum</strong>.  There is no extra weight to carry around.  There are no employees that need to justify their existence by adding unnecessary overhead.  Everybody has more than enough &#8220;real work&#8221; to do in order to move the company forward.</p>
<p>Working for a startup does have some drawbacks as well, but the positives far outweigh the negatives.  The comparison is not even close as far as I&#8217;m concerned.  As I mentioned at the beginning of the post, each company I have worked at has been substantially smaller than the previous one, and my job satisfaction has increased with each move.  While some large companies can have some really interesting problems to solve (especially when it comes to problems of scale), I&#8217;m not sure I could ever go back to working for a large company.</p>
<p>Yo Ho, it&#8217;s a startup&#8217;s life for me.</p>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/3Sr2f4bzQow" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2010/09/26/why-i-love-working-at-a-startup/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		<feedburner:origLink>http://johnpwood.net/2010/09/26/why-i-love-working-at-a-startup/</feedburner:origLink></item>
		<item>
		<title>Slides From My Intro To CouchDB Talk</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/FeY4iFKL8wA/</link>
		<comments>http://johnpwood.net/2010/08/18/slides-from-my-intro-to-couchdb-talk/#comments</comments>
		<pubDate>Wed, 18 Aug 2010 13:40:08 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[chicagodb]]></category>
		<category><![CDATA[couchdb]]></category>
		<category><![CDATA[talks]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=900</guid>
		<description><![CDATA[Thanks to everybody who showed up at Monday&#8217;s ChicagoDB meeting for the great discussion on MapReduce and my talk on CouchDB. Sides from my talk can be found on Slideshare, [...]]]></description>
			<content:encoded><![CDATA[<p>Thanks to everybody who showed up at Monday&#8217;s <a href="http://chicagodb.com/">ChicagoDB</a> meeting for the great discussion on <a href="http://nosqlsummer.org/paper/google-mapreduce">MapReduce</a> and my talk on <a href="http://couchdb.apache.org/">CouchDB</a>.  Sides from my talk can be found <a href="http://www.slideshare.net/jwoodslideshare/introduction-to-couchdb-4984849">on Slideshare</a>, and the files/commands that were used for the demo can be found on <a href="http://github.com/jwood/couchdb_demo">github</a>.  As usual, please don&#8217;t hesitate to <a href="mailto:john_p_wood@yahoo.com">email me</a> with any questions or comments.</p>
<p>See everybody next month!</p>
<div style="width:425px" id="__ss_4984849"><strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/jwoodslideshare/introduction-to-couchdb-4984849" title="Introduction to CouchDB">Introduction to CouchDB</a></strong><object id="__sse4984849" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=chidb-couchdb-100816161336-phpapp01&#038;stripped_title=introduction-to-couchdb-4984849" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed name="__sse4984849" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=chidb-couchdb-100816161336-phpapp01&#038;stripped_title=introduction-to-couchdb-4984849" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object>
<div style="padding:5px 0 12px">View more <a href="http://www.slideshare.net/">presentations</a> from <a href="http://www.slideshare.net/jwoodslideshare">John Wood</a>.</div>
</div>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/FeY4iFKL8wA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2010/08/18/slides-from-my-intro-to-couchdb-talk/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://johnpwood.net/2010/08/18/slides-from-my-intro-to-couchdb-talk/</feedburner:origLink></item>
		<item>
		<title>Speaking About CouchDB at Upcoming ChicagoDB Meeting</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/Nuc9-01iiEE/</link>
		<comments>http://johnpwood.net/2010/07/22/speaking-about-couchdb-at-upcoming-chicagodb-meeting/#comments</comments>
		<pubDate>Thu, 22 Jul 2010 15:33:11 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[couchdb]]></category>
		<category><![CDATA[nosql]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=892</guid>
		<description><![CDATA[I&#8217;m going to be speaking about CouchDB at the next ChicagoDB meeting, which will be held on August 16th, 2010. I&#8217;m currently putting together some slides that will (I hope) [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m going to be speaking about <a href="http://couchdb.apache.org/">CouchDB</a> at the next <a href="http://chicagodb.com/">ChicagoDB</a> meeting, which will be held on August 16th, 2010.  I&#8217;m currently putting together some slides that will (I hope) provide a good introduction to CouchDB and its features.  I also plan on doing a live demo at the end, so everybody can see CouchDB in action.</p>
<p>Information about the meeting can be found <a href="http://gathers.us/events/chicagodb-august-meeting/">here</a>.  I hope to see you there!</p>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/Nuc9-01iiEE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2010/07/22/speaking-about-couchdb-at-upcoming-chicagodb-meeting/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://johnpwood.net/2010/07/22/speaking-about-couchdb-at-upcoming-chicagodb-meeting/</feedburner:origLink></item>
		<item>
		<title>Thoughts on WindyCityDB</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/34tnsUZx35k/</link>
		<comments>http://johnpwood.net/2010/07/07/thoughts-on-windycitydb/#comments</comments>
		<pubDate>Wed, 07 Jul 2010 13:26:22 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Tech Industry]]></category>
		<category><![CDATA[conferences]]></category>
		<category><![CDATA[nosql]]></category>
		<category><![CDATA[windycitydb]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=881</guid>
		<description><![CDATA[WindyCityDB has been over for a while now, and I&#8217;ve had some time to collect my thoughts about the conference. First and foremost, this was an incredibly well organized and [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://windycitydb.org/">WindyCityDB</a> has been over for a while now, and I&#8217;ve had some time to collect my thoughts about the conference.</p>
<p>First and foremost, this was an incredibly well organized and wonderfully run conference.  <a href="http://twitter.com/rayhightower">Ray Hightower</a> and the WindyCityDB team did an amazing job.  The space at IIT was perfect.  The food and drink was ample and tasty.  And the talks were interesting and thought provoking.  To top it off, all proceeds from the conference, approximately $1,000, was donated to the <a href="http://www.chicagosfoodbank.org">Greater Chicago Food Depository</a>.</p>
<p>One theme that took me by surprise was the broad support for relational databases.  Almost all of the speakers stated that the relational database still has a place in this new world, along side all of the NoSQL technologies that we had gathered to discuss.  This is something I completely agree with, but being a NoSQL conference, I had imagined that there would be quite a bit of RDBMS bashing going on.  Nothing could have been further from the truth.  Everybody seemed to agree that the RDBMS is a very functional and reliable piece of software that is still very much needed.  Everybody also seemed to agree that there are some things the RDBMS doesn&#8217;t do too well based on how it is designed, and that the different NoSQL technologies aim to fill this gap.  Nobody was advocating their particular NoSQL database as a replacement to the RDBMS.  Instead, all were pitched as alternatives, created to solve different problems.</p>
<p>Another theme that sort of caught me off guard was the wide acceptance of Polyglot Persistence as &#8220;the future&#8221; of application persistence methods.  I say &#8220;sort of&#8221; because I was there to speak about Polyglot Persistence, which of course means that I think it can be a great tool if used properly.  However, I thought it was a bit more controversial of a topic.  As it turns out, there are many already taking a polyglot approach to solving their persistence needs.  This was great to hear, as made me feel a little less insane for wanting to use multiple databases within a single application.  Well, I could still be insane, but at least now I know I&#8217;m in good company :)  Several speakers mentioned Polyglot Persistence in their talks, and I spoke with several individuals at the conference, and after the conference, who are actively using a polyglot approach to address their persistence requirements.</p>
<p>I also found <a href="http://railstips.org/">John Nunemaker&#8217;s</a> prediction that hosted database solutions are going to be the future of application data persistence to be very interesting.  If you think about it, this is exactly what happened with web hosting.  Most companies, especially small companies, now use a 3rd party to host their production environments.  In fact, I&#8217;d bet that the thought of hosting your own environment doesn&#8217;t even cross the mind of the vast majority of small companies now-a-days.  I can easily see how hosted database solutions, such as <a href="https://mongohq.com">MongoHQ</a> or <a href="https://cloudant.com/">Cloudant</a>, could follow a similar path.  You would no longer need to worry about hardware issues, scaling issues, backups, maintenance, etc.  It would allow you to simply focus on your data.</p>
<p>Even though I didn&#8217;t get a chance to play with it, the WindyCityDB lab was a nice touch, especially for those who had never tinkered with some of the NoSQL databases on display.  The lab consisted of several iMacs, each loaded with a particular NoSQL database.  All of the databases were preloaded with the <a href="http://stackoverflow.com">Stack Overflow</a> data set.  Sharing a data set was a great idea, as it gave you a good way to compare and contrast how the different databases handled the same set of data.</p>
<p>I really enjoyed myself at this conference, and am already looking forward to next year&#8217;s.  At the rate the NoSQL movement is moving, I can only imagine what the landscape will look like a year from now.</p>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/34tnsUZx35k" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2010/07/07/thoughts-on-windycitydb/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://johnpwood.net/2010/07/07/thoughts-on-windycitydb/</feedburner:origLink></item>
		<item>
		<title>Slides From My WindyCityDB Talk On Polyglot Persistence</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/eFAv3d9Nkl4/</link>
		<comments>http://johnpwood.net/2010/06/29/slides-from-my-polyglot-persistence-talk-posted/#comments</comments>
		<pubDate>Tue, 29 Jun 2010 12:39:45 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[nosql]]></category>
		<category><![CDATA[polyglot persistence]]></category>
		<category><![CDATA[slides]]></category>
		<category><![CDATA[windycitydb]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=872</guid>
		<description><![CDATA[The slides from my WindyCityDB talk about Polyglot Persistence have been posted to Slideshare. You can see them here, or embedded in the post below. The case for Polyglot Persistence [...]]]></description>
			<content:encoded><![CDATA[<p>The slides from my <a href="http://windycitydb.org">WindyCityDB</a> talk about Polyglot Persistence have been posted to <a href="http://www.slideshare.net">Slideshare</a>.  You can see them <a href="http://www.slideshare.net/jwoodslideshare/polyglot-persistence-two-great-tastes-that-taste-great-together-4625004">here</a>, or embedded in the post below.</p>
<p>The case for Polyglot Persistence was made throughout the day by several of the speakers.  Most people seemed to acknowledge the fact that it is very likely one database may not have all of the tools you need to get your job done.  Instead of coercing a single database to try and do things it was never designed to do, it is becoming more common for applications to use multiple databases, utilizing each for their respective strengths.  But, of course, using multiple databases in a single application comes with its own set of issues, and you should make sure there is a real need for Polyglot Persistence before making that leap.</p>
<p>Thank you very much to the <a href="http://windycitydb.org">WindyCityDB</a> organizers for putting on such a great event.  I had a great time, learned a ton, met some interesting people, and participated in some great conversations.  What more can you ask for in a tech conference?</p>
<div style="width:425px" id="__ss_4625004"><strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/jwoodslideshare/polyglot-persistence-two-great-tastes-that-taste-great-together-4625004" title="Polyglot Persistence - Two Great Tastes That Taste Great Together">Polyglot Persistence &#8211; Two Great Tastes That Taste Great Together</a></strong><object id="__sse4625004" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=polyglotpersistence-100626195831-phpapp02&#038;stripped_title=polyglot-persistence-two-great-tastes-that-taste-great-together-4625004" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed name="__sse4625004" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=polyglotpersistence-100626195831-phpapp02&#038;stripped_title=polyglot-persistence-two-great-tastes-that-taste-great-together-4625004" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object>
<div style="padding:5px 0 12px">View more <a href="http://www.slideshare.net/">presentations</a> from <a href="http://www.slideshare.net/jwoodslideshare">John Wood</a>.</div>
</div>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/eFAv3d9Nkl4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2010/06/29/slides-from-my-polyglot-persistence-talk-posted/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://johnpwood.net/2010/06/29/slides-from-my-polyglot-persistence-talk-posted/</feedburner:origLink></item>
		<item>
		<title>“CouchDB: A Case Study” Posts Now Availalbe As A Whitepaper From Couchio</title>
		<link>http://feedproxy.google.com/~r/johnpwood/~3/ZzEyVC62pHY/</link>
		<comments>http://johnpwood.net/2010/06/22/couchio-whitepaper/#comments</comments>
		<pubDate>Tue, 22 Jun 2010 17:12:39 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[couchdb]]></category>
		<category><![CDATA[couchdb case study]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=842</guid>
		<description><![CDATA[In the spring of 2009, we were starting to run into some performance issues with the Interactive Mediums application (formerly known as TextMe). At the advice of a contractor and [...]]]></description>
			<content:encoded><![CDATA[<p>In the spring of 2009, we were starting to run into some performance issues with the <a href="http://www.interactivemediums.com">Interactive Mediums</a> application (formerly known as TextMe).  At the advice of a contractor and friend, we began looking into <a href="http://couchdb.apache.org">CouchDB</a> as a potential solution to these problems.  As with most young projects, documentation was a bit scarce.  The official CouchDB website and the <a href="http://wiki.apache.org/couchdb/">CouchDB wiki</a> had some good information, but after reading what was available we still had many questions.  Should I create a new database for each type of document I have?  How many views should I store in a design document?  What are the advantages and disadvantages of views sharing a design document?  How do I even begin migrating my relational database backed application to CouchDB?</p>
<p>So I started taking notes, documenting everything I could regarding what we learned about CouchDB, the design decisions we made for our application (and their respective trade-offs), and the migration of our application code to use CouchDB.  I organized those notes, and posted them on this blog as <a href="/2009/06/15/couchdb-a-case-study/">a case study</a>, hoping it would help others looking into CouchDB.</p>
<p>I received a lot of positive feedback from the posts, making me feel like I had in fact filled that need, at least to some degree.  Even better, earlier this year I was contacted by <a href="http://www.couch.io/">Couchio</a> about combining the series of posts into a white paper that would be posted on their site.  This would put the case study in front of a larger audience, potentially helping even more people.  I was thrilled.</p>
<p>Today, that white paper was released as the &#8220;Epic Interactive Mediums Whitepaper&#8221; (love the <em>Epic</em> :)).  You can get it <a href="http://johnpwood.net/wp-content/uploads/2010/06/Migrating-to-CouchDB.pdf">here</a>.  Many thanks to the kind folks over at Couchio for putting this together.  I hope people will find it helpful.</p>
<img src="http://feeds.feedburner.com/~r/johnpwood/~4/ZzEyVC62pHY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2010/06/22/couchio-whitepaper/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://johnpwood.net/2010/06/22/couchio-whitepaper/</feedburner:origLink></item>
	</channel>
</rss><!-- Dynamic page generated in 0.507 seconds. --><!-- Cached page generated by WP-Super-Cache on 2012-05-14 14:19:12 -->

