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

<channel>
	<title>Painfully Obvious</title>
	<atom:link href="http://andrewdupont.net/feed/" rel="self" type="application/rss+xml" />
	<link>http://andrewdupont.net</link>
	<description>The weblog of Andrew Dupont, web designer and writer.</description>
	<pubDate>Mon, 17 Jun 2013 15:22:05 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.7</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Quotation: Dave Rupert</title>
		<link>http://andrewdupont.net/2013/06/17/quotation-dave-rupert/</link>
		<comments>http://andrewdupont.net/2013/06/17/quotation-dave-rupert/#comments</comments>
		<pubDate>Mon, 17 Jun 2013 15:22:05 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
		
		<category><![CDATA[Design]]></category>

		<category><![CDATA[Development]]></category>

		<category><![CDATA[Quotations]]></category>

		<guid isPermaLink="false">http://andrewdupont.net/?p=984</guid>
		<description><![CDATA[<blockquote>

Just use your brain. I’m not sure our industry says this often enough. You’re smart, you make the internet, and you can make good decisions. Pay attention to your craft, weigh the good against the bad, and check your assumptions as you go.</blockquote>
<p><cite>— <a href='http://alistapart.com/article/mo-pixels-mo-problems'>Dave Rupert</a></cite></p>

]]></description>
			<content:encoded><![CDATA[<blockquote>

Just use your brain. I’m not sure our industry says this often enough. You’re smart, you make the internet, and you can make good decisions. Pay attention to your craft, weigh the good against the bad, and check your assumptions as you go.</blockquote>
<p><cite>— <a href='http://alistapart.com/article/mo-pixels-mo-problems'>Dave Rupert</a></cite></p>
<br />
]]></content:encoded>
			<wfw:commentRss>http://andrewdupont.net/2013/06/17/quotation-dave-rupert/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Quotation: Bill Barnwell</title>
		<link>http://andrewdupont.net/2013/02/04/quotation-bill-barnwell/</link>
		<comments>http://andrewdupont.net/2013/02/04/quotation-bill-barnwell/#comments</comments>
		<pubDate>Mon, 04 Feb 2013 21:17:24 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
		
		<category><![CDATA[Quotations]]></category>

		<category><![CDATA[Sports]]></category>

		<guid isPermaLink="false">http://andrewdupont.net/2013/02/04/quotation-bill-barnwell/</guid>
		<description><![CDATA[<blockquote>


In the playoffs, every story line is <i>ex post facto</i>, with the process graded after the fact by whatever the outcome was. You know the stories. A team with a first-round bye is refreshed and full of energy if they blow out their opponents (often as big favorites at home), but rusty and lost their timing if they lose to their opponents, who don't have anybody believing in them but themselves. It's one of the laziest bits of analysis you'll see about sports.</blockquote>
<p><cite>— <a href='http://www.grantland.com/story/_/id/8912868/bill-barnwell-puts-ravens-win-perspective'>Bill Barnwell</a></cite></p>

]]></description>
			<content:encoded><![CDATA[<blockquote>


In the playoffs, every story line is <i>ex post facto</i>, with the process graded after the fact by whatever the outcome was. You know the stories. A team with a first-round bye is refreshed and full of energy if they blow out their opponents (often as big favorites at home), but rusty and lost their timing if they lose to their opponents, who don't have anybody believing in them but themselves. It's one of the laziest bits of analysis you'll see about sports.</blockquote>
<p><cite>— <a href='http://www.grantland.com/story/_/id/8912868/bill-barnwell-puts-ravens-win-perspective'>Bill Barnwell</a></cite></p>
<br />
]]></content:encoded>
			<wfw:commentRss>http://andrewdupont.net/2013/02/04/quotation-bill-barnwell/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Hypermedia APIs, Part Two</title>
		<link>http://andrewdupont.net/2012/12/28/hypermedia-apis-part-two/</link>
		<comments>http://andrewdupont.net/2012/12/28/hypermedia-apis-part-two/#comments</comments>
		<pubDate>Fri, 28 Dec 2012 18:38:04 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
		
		<category><![CDATA[Development]]></category>

		<category><![CDATA[Ruby]]></category>

		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://andrewdupont.net/?p=974</guid>
		<description><![CDATA[Last time, I treated you guys to some Solomonesque baby-splitting between Steve Klabnik and DHH, and then spent two dozen paragraphs talking about how Gowalla&#8217;s API was pretty groundbreaking and how Scott Raymond was like the Lou Reed of hypermedia APIs.
To balance out all this ridiculous self-praise, I&#8217;ll talk about one of our screwups: how <span class="ellipsis">&#8230;</span>]]></description>
			<content:encoded><![CDATA[<p><a href="http://andrewdupont.net/2012/12/21/hypermedia-apis-part-one">Last time</a>, I treated you guys to some Solomonesque baby-splitting between Steve Klabnik and DHH, and then spent two dozen paragraphs talking about how Gowalla&#8217;s API was pretty groundbreaking and how <a href="http://www.scottraymond.net">Scott Raymond</a> was like the Lou Reed of hypermedia APIs.</p>
<p>To balance out all this ridiculous self-praise, I&#8217;ll talk about one of our screwups: how a well-intentioned decision to add a feature led us into a practical dilemma within our API. This wasn&#8217;t something I worked on myself, but this is my rough recollection of how it happened.</p>
<h4 id="the_idea">The idea</h4>
<p>As you&#8217;ll recall from part one, our spot URLs were quite simple: <code>http://gowalla.com/spots/16384</code>. One day, one naïve day, someone said, &#8220;Hey, wouldn&#8217;t it be great if our spot URLs had the name of the spot in them? If the URL for the Wahoo&#8217;s down the street was <code>/spots/wahoos-fish-tacos-austin</code> instead of <code>/spots/1019</code>?&#8221;</p>
<p>This is easily justifiable as an SEO decision &mdash; when someone searches for a venue, you&#8217;d want the Gowalla page for that venue to be on the first page of results, and that&#8217;s more likely to happen when the venue name is in the URL. It&#8217;s also justifiable on usability grounds; if I&#8217;ve been to the Wahoo&#8217;s page on Gowalla, and I&#8217;m trying to get back to it, I know I can type <code>gowalla.com/spots</code> into my browser and let the URL auto-complete tell me where to go from there. (Though today&#8217;s <a href="http://support.mozilla.org/en-US/kb/awesome-bar-find-your-bookmarks-history-and-tabs">Firefox-style address bars</a> make this a less compelling argument.)</p>
<p>We decided to generate a unique <a href="http://en.wikipedia.org/wiki/Clean_URL#Slug">slug</a> for each spot by downcasing, removing punctuation, replacing spaces with hyphens, adding the city name to the end, and (if necessary) adding further components to the end until the slug was unique: state, address (if we knew it), postal code, and finally the spot ID, should all else fail to disambiguate it.</p>
<p>We had some logic in our routes so that when the app saw <code>/spots/1019</code>, it would know to look the spot up by its ID, but when it saw <code>/spots/wahoos-fish-tacos-austin</code>, it would know to look the spot up by its slug. So that we had a hard-and-fast rule to separate these cases, we made sure that a slug wouldn&#8217;t start with a number. As I recall, this was all <a href="http://mattt.me">Mattt Thompson&#8217;s</a> handiwork, and he brought his usual brilliance to the task.</p>
<p>We&#8217;d generate the slug immediately after spot creation. I&#8217;m guessing we wrote a <a href="https://github.com/defunkt/resque#resque">Resque</a> job to add slugs to spots that already existed. And when all spots had slugs, we triumphantly turned it on. And then, a few hours later, we turned it off, somewhat less triumphantly.</p>
<h4 id="our_blunder">Our blunder</h4>
<p>If you read part one, you might already suspect what happened. In our API, URLs are the primary identifiers of resources. If you asked the API for a particular checkin at Wahoo&#8217;s, the response would have a <code>spot</code> object whose URL would be <code>/spots/1019</code>. When we changed a spot URL, we changed that unique identifier.</p>
<p>Now, because we were big on <a href="http://andrewdupont.net/2012/12/21/hypermedia-apis-part-one/#hateoas">HATEOAS</a>, this wasn&#8217;t a huge deal. And spots, in particular, were natually linked to from other resources. You wouldn&#8217;t ever start an API-related task on an individual spot page; you&#8217;d probably start at <code>/users/me</code> (the magical URL for getting info about the logged-in user) or at <code>/spots</code> (the URL for doing a spot search). And even if you had an old spot URL lying around, it would work fine, because we still responded to the ID-based URLs. So as long as we made the switch <em>all at once</em>, this was not a big deal.</p>
<p>Unfortunately, we didn&#8217;t make the switch all at once. Though we had updated the logic on the spot model so that asking for the URL would return the slug-based URL, we forgot about the one place where we took a shortcut.</p>
<h5 id="visited_spots">Visited spots</h5>
<p>When I gave you the excerpt from a sample call to <code>/users/savetheclocktower</code> in part one, I left in a weird property: something called <code>visited_spots_urls_url</code>. This was a URL that, when requested, would return a list of URLs for all the spots that a user had checked into.</p>
<pre><code class="javascript">// GET /users/savetheclocktower/visited_spots_urls
{
  "urls": [
    "/spots/1019",
    "/spots/15555",
    "/spots/91142",
    // ...
  ]
}
</code></pre>
<p>Why on earth did we have this? Because there are plenty of use cases for a unique list of all the unique places a user has been without any time/frequency context. For one, we used it in the mobile clients so that, in a list of spots, we could mark the spots you&#8217;d checked into before, hopefully helping you pick the correct spot out of a list so that you could check in just a bit faster.</p>
<p>Maybe we deserved it for having a magical API resource that returned a list of <em>indeterminate size</em>, but this was a solution that had worked for us so far.</p>
<p>The first time someone asked for this resource, we made a database query (on the <code>stamps</code> table, not the gigantic <code>checkins</code> table, thank god) to obtain the result. Then we stuffed it into <a href="http://memcached.org">memcached</a> so that we wouldn&#8217;t have to make that awful database query again until the user checked into a new spot.</p>
<p>Except, well, not quite. We didn&#8217;t put <em>spot URLs</em> into memcached; we put <em>spot IDs</em> into memcached, because we wanted that collection around for server-side purposes, too, and on the server side you&#8217;d rather be working with IDs than URLs. To go from spot IDs to spot URLs, we did this:</p>
<pre><code class="ruby">def uniq_visited_spot_urls
  uniq_visited_spot_ids.map { |id| "/spots/#{id}" }
end
</code></pre>
<p>I took the scenic route, but here&#8217;s our problem: to make a switch to slug-based URLs, we needed the whole API to use the same logic for generating spot URLs. Except that logic lived on the spot model itself, and if you had <em>just a spot ID</em> (as in this example) there was no quick way to turn it into a spot slug.</p>
<p>So even after we flipped the switch, <code>/users/me/visited_spot_urls</code> returned old-style spot URLs because it was taking a shortcut for performance reasons. A couple people complained on the API mailing list; someone in the office said, &#8220;hey, the app isn&#8217;t showing check marks next to spots I&#8217;ve checked into&#8221;; and then we figured out the problem and switched back to the old spot URLs until we could think this whole thing through.</p>
<h4 id="stuck">Stuck</h4>
<p>The problem I described above wasn&#8217;t a deal-breaker. We could&#8217;ve figured out a better way to cache spots, or we could&#8217;ve just maintained separate cache keys for visited spot IDs versus visited spot URLs.</p>
<p>We never did go back to the slug URLs, though. As far as I know, we just got busy with other stuff. But I know that the more I thought about this problem, the more I felt that there wasn&#8217;t a good way around it. Three concerns fenced us in:</p>
<ol class="indented">
<li>
<p>In the API, a resource&#8217;s URL was both its unique identifier and a hyperlink. It was impossible to change one without changing the other.</p>
</li>
<li>
<p>For the purposes of browsing gowalla.com in a web browser, it was in our interest for spots to have long, informative URLs.</p>
</li>
<li>
<p>For the purposes of using Gowalla as an app, it was in our interest for spots to have short, bare-bones identifiers.</p>
</li>
</ol>
<p>This was the main tension. There are some clever ways around this, but none that are quite clever enough:</p>
<ol class="indented">
<li>
<p><em>Try to satisfy all three concerns at once. Stick with the short URLs for the API. But if someone requests <code>/spots/1019</code> in a browser, just do a 302 redirect to <code>/spots/wahoos-fish-tacos-austin</code>.</em> Assuming this would have the same impact on SEO, it certainly seems like a prudent solution. But to do a proper redirect, we&#8217;d first have to look up the spot to find its slug. So we&#8217;re talking about a round trip to either a database or a caching layer before we know which URL to redirect to. Even in the best-case scenario, it would add a perceptible lag to page load, and that&#8217;s no fun.</p>
</li>
<li>
<p><em>Forget about concern #1; separate the hyperlink from the unique identifier. Stick with the short URLs for the API. But put a <code>full_url</code> property in the JSON response for spots.</em> OK, but now there&#8217;s just one resource for which we have to maintain this dichotomy, and I doubt API consumers would bother with the full URL if the short URL still worked.</p>
</li>
<li>
<p><em>Forget about concern #3; just move to long URLs everywhere.</em> Except a slug-based URL would be twice as long as an ID-based URL, maybe more. For most API calls, that&#8217;s a small (but still significant) increase. But our <code>/users/me/visited_spots_urls</code> response would be twice as large. This is probably the least distasteful of these three options, but mobile apps always have to be sensitive about the amount of data they&#8217;re sending over the wire, and it would make no one happy to bite that bullet.</p>
</li>
</ol>
<p>Let&#8217;s also remember that any client-side caching involving spot URLs (and, yes, the Gowalla app did a lot of that) is invalid now that we&#8217;ve made this switch.</p>
<p>And one more groan-inducing thing: what happens when a spot&#8217;s name changes? Gowalla spots could be created by anyone, and it wasn&#8217;t uncommon for someone to make a typo when creating a spot name. We had a network of volunteer super-users who spent their time renaming <em>Starbuck&#8217;s</em> to <em>Starbucks</em>, <em>Barnes &amp; Nobles</em> to <em>Barnes &amp; Noble</em>, <em>Chervon</em> to <em>Chevron</em>. And the name is part of a spot slug. So we either change the slug when the name changes, even though <a href="http://www.w3.org/Provider/Style/URI.html">cool URIs don&#8217;t change</a>; or we say that the first slug is the only slug, and that gas station will forever bear the URL <code>/spots/chervon-giddings-tx</code> through no fault of its own.</p>
<p>Or we forget about putting the spot name in the URL &mdash; and then our problem goes away.</p>
<h4 id="the_answer">The answer</h4>
<p>So, yeah, maybe the answer was not to do the thing in the first place. Or maybe the answer was to do it, but not until we released the next version of the API in order to break stuff as little as possible. Or maybe the answer was to plow ahead, consequences be damned.</p>
<p>None of this exposes a critical flaw with hypermedia APIs. These were mundane practical problems caused by previous decisions that we ourselves made. This is merely a story about how you can do all the right stuff and still get bitten by <a href="http://en.wikipedia.org/wiki/There_are_known_knowns">those Rumsfeldian <i>unknown unknowns</i></a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://andrewdupont.net/2012/12/28/hypermedia-apis-part-two/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Hypermedia APIs, Part One</title>
		<link>http://andrewdupont.net/2012/12/21/hypermedia-apis-part-one/</link>
		<comments>http://andrewdupont.net/2012/12/21/hypermedia-apis-part-one/#comments</comments>
		<pubDate>Sat, 22 Dec 2012 03:11:28 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
		
		<category><![CDATA[Articles]]></category>

		<category><![CDATA[Development]]></category>

		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://andrewdupont.net/?p=966</guid>
		<description><![CDATA[Part of me bristles when I hear someone say &#8220;Hypermedia API.&#8221; I worry it&#8217;ll become the sort of phrase, like &#8220;semantic web,&#8221; that means different things to different people, and ends up covering such a breadth of ideas that it&#8217;s impossible to argue for or against without specifying which flavor you&#8217;re addressing.
Nonetheless, when I see <span class="ellipsis">&#8230;</span>]]></description>
			<content:encoded><![CDATA[<p>Part of me bristles when I hear someone say &#8220;Hypermedia API.&#8221; I worry it&#8217;ll become the sort of phrase, like &#8220;semantic web,&#8221; that means different things to different people, and ends up covering such a breadth of ideas that it&#8217;s impossible to argue <em>for</em> or <em>against</em> without specifying which flavor you&#8217;re addressing.</p>
<p>Nonetheless, when I see <a href="http://37signals.com/svn/posts/3373-getting-hyper-about-hypermedia-apis">DHH arguing against Hypermedia APIs</a>, I worry that we&#8217;re in serious <a href="http://cmgm.stanford.edu/~lkozar/EmoPhillips.html">&#8220;die, heretic scum&#8221; territory</a>. I&#8217;m no expert, but the difference between REST and Hypermedia really doesn&#8217;t seem that large, especially in a universe where SOAP is a thing. Moreover, Rails deserves a lot of credit for demonstrating that web APIs could work within HTTP rather than try to reinvent it. Out of the box, <a href="http://timelessrepo.com/haters-gonna-hateoas">Rails checks three out of four of Steve Klabnik&#8217;s boxes</a>, and all we&#8217;re arguing over is that last one.</p>
<p>Anyway, what prompted this was <a href="http://weblog.therealadam.com/2012/12/20/hypermedia-opinions/">a post by Adam Keys</a>, my former Gowalla colleague. I agree with most of what he&#8217;s saying here. My gut reaction to Hypermedia APIs is this:</p>
<p>Roughly 90% of it is sensible stuff that I&#8217;ve already seen in the wild and which is demonstrably a Good Idea. The remaining 10% is the stuff that (at this early stage) <em>seems</em> non-intuitive, or overkill, or YAGNI, or whatever the word is for a thing that you think is awesome but which your users won&#8217;t give a damn about.</p>
<p>In fact, that last thing is my chiefest concern. The final 10% seems to require nontrival re-education on the part of consumers. I don&#8217;t mean they&#8217;d have to be brainwashed; I just mean that some of the stated benefits only come to pass if the consumers buy in, and in my experience an API consumer wants to do the simplest thing that could possibly work. I believe this is what Adam is getting at in <a href="http://weblog.therealadam.com/2012/12/21/why-im-down-on-hypermedia-containers/">his follow-up post</a>.</p>
<h4 id="the_gowalla_api">The Gowalla API</h4>
<p>Adam&#8217;s opinions on hypermedia are informed, in part, by his time at Gowalla, and so are mine. Before I convince myself it&#8217;s a bad idea, let&#8217;s take a retrospective look at the Gowalla API (which, by the way, was started in 2008–2009) and see how it measures up against a hypermedia rubric.</p>
<h4 id="things_we_did_right">Things we did right</h4>
<h5 id="addressability">Addressability</h5>
<p>URLs identified resources. A spot had the same URL whether you were requesting an HTML representation in a web browser or a JSON representation from <code>curl</code>. If a form could create a resource by <code>POST</code>ing some multipart form data to a URL, odds are a client could create the same resource by <code>POST</code>ing some JSON to that same URL.</p>
<p>This was less like a <em>knowing philosophical decision</em> and more like a thing that <em>Rails just does by default</em>. Until rather late in the game, if you were using the Gowalla API, your requests were hitting the same controllers and actions as web users&#8217; requests. (Eventually we decided to move API stuff into dedicated controllers for maintainability&#8217;s sake, but <em>that</em> tilting-at-a-windmill saga will have to be told on another day.)</p>
<h5 id="content_negotiation">Content negotiation</h5>
<p>As implied above, the API was driven by content negotiation. If you asked for HTML, you got a browser representation; if you asked for JSON, you got a pure data representation. (If you asked for XML, we pretended we didn&#8217;t hear you.)</p>
<h5 id="hateoas">HATEOAS</h5>
<p>We endeavored to practice what Steve calls HATEOAS: Hypertext As The Engine Of Application State. To over-simplify: a response should publicize the URLs of any resources that are reasonably related to it.</p>
<p>(By the way: I do not come down on one side or the other here. If there&#8217;s a natural workflow to your API, as there was for Gowalla&#8217;s, it obviously makes sense to publicize related resources rather than force a user to memorize your URL-making conventions. On the other hand, odds are high that your API consumers will make assumptions about your URL schemes anyway. So I&#8217;m not sure what HATEOAS gets you in the real world, except for the ability to say &#8220;I told you so.&#8221; Which, admittedly, is underrated.)</p>
<p>But back to Gowalla. If you were authenticated with Gowalla and requested the resource for your own user profile, this is a snapshot of what you saw:</p>
<pre><code class="javascript">// GET /users/savetheclocktower
{
  "stamps_count": 14,
  "stamps_url": "/users/savetheclocktower/stamps",

  "pins_count": 11,
  "pins_url": "/users/savetheclocktower/pins",

  "top_spots_url": "/users/savetheclocktower/top_spots",

  "friends_count": 44,
  "friends_url": "/users/savetheclocktower/friends",

  // ...

  "visited_spots_urls_url": "/users/savetheclocktower/visited_spots_urls"
}</code></pre>
<p>Nearly every meaningful kind of resource is discoverable by starting at this response and navigating through the various URLs. (Of course, not every API use case would start with loading a specific user&#8217;s profile. For instance, those that were interested mainly in the place database would probably start with the result set of spots from a geographical search.) Though the URL conventions were simple enough that a client could build URLs on their own, we tried to make it so that building URLs was <em>harder</em> than just using the URLs that we&#8217;d given you in the response. This gave us a <em>theoretical</em> freedom to change URLs in the future (<i>not that we&#8217;d ever want to do so</i>, we thought).</p>
<p>This style &mdash; in which everything ending in <code>url</code> points to another resource &mdash; is just one version of what <a href="http://stateless.co/hal_specification.html">HAL</a> or <a href="http://amundsen.com/media-types/collection/">Collection+JSON</a> are trying to formalize. It&#8217;s a pattern that worked very well for us. It made our API very &#8220;surfable,&#8221; and though I doubt we had machine discovery in mind when we were doing it, it did mean that the API explorer I built was a lot of fun to use &mdash; anything that looked like a URL was hyperlinked, and clicking on it would load that new resource in the explorer. We updated the URL hash, too, so the back button would return you to the previous resource.</p>
<p>Crucial to all of this is that the API used a resource&#8217;s URL as its unique identifier, rather than a raw ID. This is the part that Rails didn&#8217;t give you out of the box, so credit to <a href="http://www.scottraymond.net">Scott</a> for designing it this way.</p>
<h4 id="what_we_could8217ve_done_better">What we could&#8217;ve done better</h4>
<h5 id="api_versioning">API Versioning</h5>
<p>Rather than <a href="http://barelyenough.org/blog/2008/05/versioning-rest-web-services/">version our API with MIME types</a>, we used a separate <code>X-Gowalla-API-Version</code> header, defaulting to the most recent version if a JSON-requesting client omitted this header.</p>
<p>I don&#8217;t necessarily think that our approach was wrong &mdash; only that if we&#8217;d <em>made</em> people opt into a particular MIME-type, rather than just the generic <code>application/json</code>, and if the MIME-type was tied to a particular API version, we likely would&#8217;ve had fewer incidents where changes we made inadvertently broke third-party tools.</p>
<h5 id="discoverability">Discoverability</h5>
<p>When I said that every resource was discoverable, I was lying. Nearly all <code>GET</code> requests were discoverable. Anything that required a <code>POST</code> (and any <code>GET</code> that involved query parameters) wasn&#8217;t documented within the API itself, so you&#8217;d have to dig into the API documentation to figure out exactly how they worked. If we were doing it over again now, it&#8217;s possible that we&#8217;d toss in <a href="http://amundsen.com/media-types/collection/format/#query-templates">query templates</a> or something like it, but I suspect we wouldn&#8217;t have bothered.</p>
<h5 id="sub_resources">Sub-resources</h5>
<p>We never really figured out the best way to do sub-resources. Consider a checkin, which referenced one user and one spot:</p>
<pre><code class="javascript">// GET /checkins/131072

{
  "created_at": "2010-12-21T01:03:15-06:00",
  "message": "I am eating here under protest.",
  "url": "/checkins/131072",

  "user": {
    "first_name": "Andrew",
    "last_name": "Dupont",
    "url": "/users/savetheclocktower",
    "image_url": "http://some.crazy.cdn.url/jklyjksljkrewus.jpg",
    "hometown": "Austin, TX",
    "photos_url": "/users/savetheclocktower/photos"
  },

  "spot": {
    "name": "Red Lobster",
    "url": "/spots/15555",
    "image_url": "http://some.crazy.cdn.url/jjkpwopresas.jpg",
    "lat": -90.105324,
    "lng": 30.448674,
    "address": {
      "street_address": "123 Fake St.",
      "locality": "New Orleans",
      "region": "LA",
      "iso3166": "US"
    }
  }

  // ...
}</code></pre>
<p>Now, we don&#8217;t want to dump the whole user resource into our response, but neither do we want to force someone to follow a URL to learn anything about the person who checked in. So we chose the middle ground: include a &#8220;concise&#8221; representation of the resource. In this case, the properties we show from the sub-resources are the things we&#8217;d need to know if we were rendering the checkin in a list; with this response, I can render the sentence &#8220;Andrew checked in at Red Lobster,&#8221; along with a user avatar and a spot icon, without having to make any other requests.</p>
<p>This eventually got crazy, though, because a sub-resource could plausibly have a half-dozen representations of varying lengths, each of which could be justified from context. For instance, if you requested a user&#8217;s checkins, you&#8217;d get a list of these:</p>
<pre><code class="javascript">// GET /users/savetheclocktower/checkins
{
  "checkins": [
    {
      "created_at": "2010-12-21T01:03:15-06:00",
      "message": "I am eating here under protest.",
      "url": "/checkins/131072",

      "user": {
        "first_name": "Andrew",
        "last_name": "Dupont",
        "url": "/users/savetheclocktower"
      },

      "spot": {
        "name": "Red Lobster",
        "url": "/spots/15555",
        "image_url": "http://some.crazy.cdn.url/jjkpwopresas.jpg",
        "lat": -90.105324,
        "lng": 30.448674,
        "address": {
          "street_address": "123 Fake St.",
          "locality": "New Orleans",
          "region": "LA",
          "iso3166": "US"
        }
      }
    },
    {
      "created_at": "2010-12-21T01:02:44-06:00",
      "message": "I am in need of fuel for my car.",
      "url": "/checkins/130808",

      "user": {
        "first_name": "Andrew",
        "last_name": "Dupont",
        "url": "/users/savetheclocktower"
      },

      "spot": {
        "name": "Chevron",
        "url": "/spots/91142",
        "image_url": "http://some.crazy.cdn.url/oahkhjs.jpg",
        "lat": -90.105416,
        "lng": 30.444994,
        "address": {
          "street_address": "919 Fake St.",
          "locality": "New Orleans",
          "region": "LA",
          "iso3166": "US"
        }
      }
    },

    // ...
  ]
}</code></pre>
<p>Here, the spot resource is using the same representation that it did for an individual checkin, but the user resource is much more sparse. Why? Because (a) in this response, all the checkins are guaranteed to be from the same user, and the redundancy bothered the hell out of me; (b) chances are you followed this URL from the response for <code>/users/savetheclocktower</code> and thus already have the full representation of this user.</p>
<p>If you were to ask for a single spot&#8217;s checkins, the situation would be reversed &mdash; the user representation would be the same as for a single checkin, but the spot representation would be as minimal as possible.</p>
<p>We managed this complexity as best we could. First we added a <code>to_public_json</code> method on models &mdash; so named because it wasn&#8217;t trying to be exhaustive like <code>to_json</code>; it merely wanted to expose properties that would be relevant for a public API. It optionally took a symbol argument that would specify a named represenation, much like <code>DateTime#to_formatted_s</code> lets you choose between date formats. When even <em>that</em> got too complicated, <a href="http://h3h.net">Brad Fults</a> wrote an awesome thing called <a href="https://github.com/gowalla/boxer">Boxer</a> that centralized all this logic in a place that was neither a controller nor a model.</p>
<p>I&#8217;d always wished for YAML-style anchors and references in JSON, but I didn&#8217;t want to do anything crazy with our JSON responses that put an extra burden on API consumers. Still, if I were to do it over again, I&#8217;d probably do something like this:</p>
<pre><code class="javascript">// (hypothetically)
// GET /users/savetheclocktower/checkins

{
  "includes": {
    "users": {
      "savetheclocktower": {
        "first_name": "Andrew",
        "last_name": "Dupont",
        "url": "/users/savetheclocktower",
        "image_url": "http://some.crazy.cdn.url/jklyjksljkrewus.jpg",
        "hometown": "Austin, TX",
        "photos_url": "/users/savetheclocktower/photos"
      }
    },
    "spots": {
      "15555": {
        "name": "Red Lobster",
        "url": "/spots/15555",
        "image_url": "http://some.crazy.cdn.url/jjkpwopresas.jpg",
        "lat": -90.105324,
        "lng": 30.448674,
        "address": {
          "street_address": "123 Fake St.",
          "locality": "New Orleans",
          "region": "LA",
          "iso3166": "US"
        }
      },
      "91142": {
        "name": "Chevron",
        "url": "/spots/91142",
        "image_url": "http://some.crazy.cdn.url/oahkhjs.jpg",
        "lat": -90.105416,
        "lng": 30.444994,
        "address": {
          "street_address": "919 Fake St.",
          "locality": "New Orleans",
          "region": "LA",
          "iso3166": "US"
        }
      }
    }
  },

  "checkins": [
    {
      "created_at": "2010-12-21T01:03:15-06:00",
      "message": "I am eating here under protest.",
      "url": "/checkins/131072",

      "user": { "include": "/users/savetheclocktower" },
      "spot": { "include": "/spots/15555" }
    },
    {
      "created_at": "2010-12-21T01:02:44-06:00",
      "message": "I am in need of fuel for my car.",
      "url": "/checkins/130808",

      "user": { "include": "/users/savetheclocktower" },
      "spot": { "include": "/spots/91142" }
    },
    // ...
  ]
}</code></pre>
<p>All sub-resources would get put into a hierarchical repository at the root of the response, and the structure of that repository would mirror the URL structure, so that when you saw an object with an &#8220;include&#8221; property, you could try to look it up locally and then fall back to another HTTP request if necessary. This is probably overkill, but dammit, if I&#8217;m going to introduce an extra-language convention into JSON, I&#8217;m going to give it some style.</p>
<h4 id="the_verdict">The Verdict</h4>
<p>On reflection, I think we did pretty well, especially considering that these decisions were made incrementally over the course of two years. I can think of only one instance when the API design painted us into a corner, and that&#8217;s the story I&#8217;ll save for next time.</p>
]]></content:encoded>
			<wfw:commentRss>http://andrewdupont.net/2012/12/21/hypermedia-apis-part-one/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Review: Mass Effect 3</title>
		<link>http://andrewdupont.net/2012/04/20/review-mass-effect-3/</link>
		<comments>http://andrewdupont.net/2012/04/20/review-mass-effect-3/#comments</comments>
		<pubDate>Fri, 20 Apr 2012 23:34:13 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
		
		<category><![CDATA[Reviews]]></category>

		<category><![CDATA[Video Games]]></category>

		<guid isPermaLink="false">http://andrewdupont.net/?p=960</guid>
		<description><![CDATA[I think so many things about the Mass Effect series, far too many to corral into a focused thought. I know because I&#8217;ve tried to write this review several times already and I have nothing to show for it except thirty paragraphs of ramblings.
My brain lies to me sometimes. Because I&#8217;d loved Mass Effect, and <span class="ellipsis">&#8230;</span>]]></description>
			<content:encoded><![CDATA[<p>I think so many things about the <cite>Mass Effect</cite> series, far too many to corral into a focused thought. I know because I&#8217;ve tried to write this review several times already and I have nothing to show for it except thirty paragraphs of ramblings.</p>
<p>My brain lies to me sometimes. Because I&#8217;d loved <cite>Mass Effect</cite>, and because <cite>Mass Effect 2</cite> changed several of the things I&#8217;d loved about the first game, I convinced myself that the sequel wasn&#8217;t quite as good. It had been a year and a half since I&#8217;d touched either game, so after I finished <cite>Mass Effect 3</cite> I decided to go back to the very beginning and do a marathon playthrough with a fresh character. Not only would it be a plot refresher, but it&#8217;d let me undo all of the dumb mistakes I made in games one and two that I ended up having to pay for in game three.</p>
<p>So <cite>ME3</cite> deserves credit for that, at the very least. When you play <cite>ME1</cite> and <cite>ME2</cite> back-to-back, it&#8217;s clear which is the better game. Yes, I like <cite>ME1</cite>&#8217;s skills tree better; yes, I like being able to customize weapons and earn tiny amounts of XP for every little thing I do. But there&#8217;s no way I can go back to <cite>ME1</cite>&#8217;s unwieldy combat or awkward pacing. I&#8217;d like to apologize to <cite>ME2</cite> for <a href="http://andrewdupont.net/2010/02/01/review-mass-effect-2/">being so mean to it</a>.</p>
<p>I was expecting not to like <cite>Mass Effect 3</cite>. As it turns out, <cite>ME3</cite> contains several of the most emotionally poignant moments I&#8217;ve had playing video games. It has the hard task of incorporating decisions you&#8217;ve made in previous games &mdash; and making the player feel that those decisions were significant &mdash; while maintaining an economy of story and a clarity of plot. It&#8217;s done that amazingly well. Until <cite>ME3</cite> I don&#8217;t think I&#8217;d ever played a game that added replay value to its <em>prequel</em>. Have you?</p>
<p>My favorite parts of <cite>ME3</cite> happen <em>hours</em> before the game&#8217;s notorious and infuriating ending. Yes, the ending is bad. Even when you&#8217;re <em>expecting</em> it to be bad it manages to underwhelm. It&#8217;s bad in almost all the ways a sci-fi ending can be bad. It&#8217;s thematically disjoint, arbitrary, and derivative. It offers a false choice. It violates continuity. It violates canon. And it&#8217;s so maddeningly ambiguous that it doesn&#8217;t feel much like an ending to <em>anything</em>.</p>
<p>The upcoming free DLC promises to provide more context to each ending. But at best it&#8217;ll address only some of this. I seriously doubt this whole thing can be un-fucked.</p>
<p>Here&#8217;s the thing: I never cared much about this whole galaxy-wide threat. For me, the Reapers were a glorified MacGuffin, an excuse for me to ride around on a ship and meet cool alien races and shoot robots with sniper rifles. A <cite>Mass Effect</cite> game is at its best during side missions, when you can pretend you&#8217;re dealing with a Star Trek–style episodic threat and forget about that thing that&#8217;s trying to destroy the universe.</p>
<p>That&#8217;s why the ending, awful as it is, doesn&#8217;t ruin this game for me. I&#8217;ve played it for at least forty hours now and only <em>three</em> of those hours have been unenjoyable. Does the underwhelming finale of <cite>Seinfeld</cite> ruin the entire series, or even just the final season? What about <cite>The Sopranos</cite>? <a href="http://www.metacritic.com/game/xbox-360/mass-effect-3">On Metacritic</a>, <cite>Mass Effect 3</cite> has a score of 93/100 according to critics, but only 50/100 according to Metacritic users. I understand their frustrations, but I&#8217;m on the critics&#8217; side here.</p>
<p>But the <cite>ME3</cite> ending disappoints me in a deeper way. I would play at least six more games just like <cite>ME3</cite>. I had been <em>looking forward</em> to playing those games someday. I had thought that Bioware was looking for their own rich universe for sci-fi storytelling. The ending of <cite>ME3</cite> &mdash; no matter which one you pick &mdash; seems to salt the earth, as if they wanted to rule out any future stories that would take place after the events of the game. They could do prequels, but there&#8217;s only a thirty-year window between humans&#8217; first contact with other races and the events of <cite>ME3</cite>.</p>
<p>Aside from a spin-off game or two, I doubt Bioware is interested in telling more <cite>Mass Effect</cite> stories. And <em>that&#8217;s</em> profoundly disappointing. I&#8217;m most of the way through my second <cite>ME3</cite> playthrough, but I&#8217;m playing it more and more slowly. I&#8217;m in no hurry to get to an ending that reminds me, clearly and bluntly, that it&#8217;s all over.</p>
]]></content:encoded>
			<wfw:commentRss>http://andrewdupont.net/2012/04/20/review-mass-effect-3/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Review: Infamous 2</title>
		<link>http://andrewdupont.net/2012/02/11/review-infamous-2/</link>
		<comments>http://andrewdupont.net/2012/02/11/review-infamous-2/#comments</comments>
		<pubDate>Sun, 12 Feb 2012 05:07:03 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
		
		<category><![CDATA[Reviews]]></category>

		<category><![CDATA[Video Games]]></category>

		<guid isPermaLink="false">http://andrewdupont.net/2012/02/11/review-infamous-2/</guid>
		<description><![CDATA[I&#8217;m a tough man to write video games for. Any triple-A title released for a major console is the result of so much craftsmanship from so many talented people, such that you can find genuinely good things to say about even the mediocre ones.
Why do I want a game to have long-lasting impact? Why does <span class="ellipsis">&#8230;</span>]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m a tough man to write video games for. Any triple-A title released for a major console is the result of so much craftsmanship from so many talented people, such that you can find genuinely good things to say about even the mediocre ones.</p>
<p>Why do I want a game to have long-lasting impact? Why does it have to be profound to me after I&#8217;m done with it? I played the original <cite>Infamous</cite> for at least fifteen hours &mdash; doesn&#8217;t that say more about its quality than whatever I feel about it eighteen months later?</p>
<p>Because I feel the same way about <cite>Infamous 2</cite>. It&#8217;s just as good as <cite>Infamous</cite> in all the ways that <cite>Infamous</cite> was good, and bad in all the ways its predecessor was bad.</p>
<p>At the beginning of <cite>Infamous 2</cite>, plot contrivances lead Cole and his annoying sidekick Zeke to make their way from Fake New York (Empire City, the setting of <cite>Infamous</cite>) to Fake New Orleans (New Marais). A giant evil thing called The Beast is heading in their direction, slowly and surely, but in the meantime there&#8217;s a new city to play in. The open world is full of open-worldy stuff; there are scattered things to collect, and territory missions, and even user-generated side missions.</p>
<p>And there&#8217;s the main storyline, a set of missions where you meet other people with crazy superpowers and fight generic monsters and find blast cores (which function as rather blatant <a href="http://tvtropes.org/pmwiki/pmwiki.php/Main/PlotCoupon">plot coupons</a>; The Beast gets closer each time you collect one). All of this is fine, I suppose, and I&#8217;ve loved games with plots just as ridiculous as this.</p>
<p>Maybe I&#8217;m just getting tired of games that don&#8217;t seem to care about their own plots. There&#8217;s no sense of pacing or cresecendo; Cole seems to react to everything that happens with the same sort of grizzled nonchalance. There&#8217;s no attempt to make exposition seamless or elegant; if there&#8217;s something you need to know, a supporting character will give you a clumsy info-dump <em>the moment</em> you need to know, and no sooner. (Midway through the game, Lucy tells Cole about a plague that&#8217;s killing half the city. If it&#8217;s that large, shouldn&#8217;t Cole know about it already?)</p>
<p>Or maybe it&#8217;s that I can&#8217;t think of a single thing <cite>Infamous 2</cite> does that hasn&#8217;t been done better by another game. Maybe it&#8217;s good in a way that&#8217;s too <em>balanced</em>, and if it had tried hard to excel in a certain aspect I&#8217;d at least be left with something to grab onto.</p>
]]></content:encoded>
			<wfw:commentRss>http://andrewdupont.net/2012/02/11/review-infamous-2/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Quotation: Sam Sparks</title>
		<link>http://andrewdupont.net/2011/08/31/quotation-sam-sparks/</link>
		<comments>http://andrewdupont.net/2011/08/31/quotation-sam-sparks/#comments</comments>
		<pubDate>Wed, 31 Aug 2011 19:43:19 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
		
		<category><![CDATA[Politics]]></category>

		<category><![CDATA[Quotations]]></category>

		<guid isPermaLink="false">http://andrewdupont.net/2011/08/31/quotation-sam-sparks/</guid>
		<description><![CDATA[<blockquote>
<a href="http://articles.cnn.com/2011-05-20/politics/texas.abortion.sonogram_1_sonogram-procedure-abortion?_s=PM:POLITICS">The Act</a> does not compel physicians to apprise women of the risks inherent in abortion, inform the women of available alternatives, and facilitate access to additional information if the women wish to review it before making their decisions; existing Texas law already compels such speech by physicians… Instead, the Act compels physicians to advance an ideological agenda with which they may not agree, regardless of any medical necessity, and irrespective of whether the pregnant women wish to listen.</blockquote>
<p><cite>— <a href='http://courtweb.pamd.uscourts.gov/courtwebsearch/txwd/08342349.pdf'>Judge Sam Sparks</a></cite></p>

]]></description>
			<content:encoded><![CDATA[<blockquote>
<a href="http://articles.cnn.com/2011-05-20/politics/texas.abortion.sonogram_1_sonogram-procedure-abortion?_s=PM:POLITICS">The Act</a> does not compel physicians to apprise women of the risks inherent in abortion, inform the women of available alternatives, and facilitate access to additional information if the women wish to review it before making their decisions; existing Texas law already compels such speech by physicians… Instead, the Act compels physicians to advance an ideological agenda with which they may not agree, regardless of any medical necessity, and irrespective of whether the pregnant women wish to listen.</blockquote>
<p><cite>— <a href='http://courtweb.pamd.uscourts.gov/courtwebsearch/txwd/08342349.pdf'>Judge Sam Sparks</a></cite></p>
<br />
]]></content:encoded>
			<wfw:commentRss>http://andrewdupont.net/2011/08/31/quotation-sam-sparks/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Quotation: Sam Harris</title>
		<link>http://andrewdupont.net/2011/08/27/quotation-sam-harris/</link>
		<comments>http://andrewdupont.net/2011/08/27/quotation-sam-harris/#comments</comments>
		<pubDate>Sat, 27 Aug 2011 05:55:57 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
		
		<category><![CDATA[Politics]]></category>

		<category><![CDATA[Quotations]]></category>

		<guid isPermaLink="false">http://andrewdupont.net/2011/08/27/quotation-sam-harris/</guid>
		<description><![CDATA[<blockquote>

Many readers were enraged that I could support taxation in <em>any</em> form. It was as if I had proposed this mad scheme of confiscation for the first time in history. Several cited my framing of the question &mdash; “how much wealth can one person be allowed to keep?” &mdash; as especially sinister, as though I had asked, “how many of his internal organs can one person be allowed to keep?”</blockquote>
<p><cite>— <a href='http://www.samharris.org/blog/item/how-to-lose-readers-without-even-trying/'>Sam Harris</a></cite></p>

]]></description>
			<content:encoded><![CDATA[<blockquote>

Many readers were enraged that I could support taxation in <em>any</em> form. It was as if I had proposed this mad scheme of confiscation for the first time in history. Several cited my framing of the question &mdash; “how much wealth can one person be allowed to keep?” &mdash; as especially sinister, as though I had asked, “how many of his internal organs can one person be allowed to keep?”</blockquote>
<p><cite>— <a href='http://www.samharris.org/blog/item/how-to-lose-readers-without-even-trying/'>Sam Harris</a></cite></p>
<br />
]]></content:encoded>
			<wfw:commentRss>http://andrewdupont.net/2011/08/27/quotation-sam-harris/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Quotation: Stanley B. Greenberg</title>
		<link>http://andrewdupont.net/2011/08/01/quotation-stanley-b-greenberg/</link>
		<comments>http://andrewdupont.net/2011/08/01/quotation-stanley-b-greenberg/#comments</comments>
		<pubDate>Mon, 01 Aug 2011 17:58:40 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
		
		<category><![CDATA[Politics]]></category>

		<category><![CDATA[Quotations]]></category>

		<guid isPermaLink="false">http://andrewdupont.net/2011/08/01/quotation-stanley-b-greenberg/</guid>
		<description><![CDATA[<blockquote>


In analyzing these polls in the United States, I see clearly that voters feel ever more estranged from government &mdash; and that they associate Democrats with government. If Democrats are going to be encumbered by that link, they need to change voters’ feelings about government. They can recite their good plans as a mantra and raise their voices as if they had not been heard, but voters will not listen to them if government is disreputable.</blockquote>
<p><cite>— <a href='http://www.nytimes.com/2011/07/31/opinion/sunday/tuning-out-the-democrats.html?_r=1'>Stanley B. Greenberg</a></cite></p>

]]></description>
			<content:encoded><![CDATA[<blockquote>


In analyzing these polls in the United States, I see clearly that voters feel ever more estranged from government &mdash; and that they associate Democrats with government. If Democrats are going to be encumbered by that link, they need to change voters’ feelings about government. They can recite their good plans as a mantra and raise their voices as if they had not been heard, but voters will not listen to them if government is disreputable.</blockquote>
<p><cite>— <a href='http://www.nytimes.com/2011/07/31/opinion/sunday/tuning-out-the-democrats.html?_r=1'>Stanley B. Greenberg</a></cite></p>
<br />
]]></content:encoded>
			<wfw:commentRss>http://andrewdupont.net/2011/08/01/quotation-stanley-b-greenberg/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Quotation: Charles P. Pierce</title>
		<link>http://andrewdupont.net/2011/06/10/quotation-charles-p-pierce-2/</link>
		<comments>http://andrewdupont.net/2011/06/10/quotation-charles-p-pierce-2/#comments</comments>
		<pubDate>Fri, 10 Jun 2011 20:27:04 +0000</pubDate>
		<dc:creator>Andrew</dc:creator>
		
		<category><![CDATA[Quotations]]></category>

		<category><![CDATA[Sports]]></category>

		<guid isPermaLink="false">http://andrewdupont.net/2011/06/10/quotation-charles-p-pierce-2/</guid>
		<description><![CDATA[<blockquote>


[H]ad <cite>The National</cite> not spent money the way that it did [...] Peter Richmond wouldn't have had the chance to go to a Cubs game with Bill Murray and then hang out with Fleetwood Mac afterward. Which would have meant that we wouldn't have had the great scene several months later when Murray showed up in the New York offices to see Peter. Not long before that, a guy not many people liked had been fired, and Murray wandered into the daily editorial meeting, propped his flip-flops up on the table, and asked, "Show of hands. How many people thought [blank] was an asshole?" The world would be a poorer place without that moment.</blockquote>
<p><cite>— <a href='http://www.grantland.com/story/_/id/6626434/my-memories-national'>Charles P. Pierce</a></cite></p>

]]></description>
			<content:encoded><![CDATA[<blockquote>


[H]ad <cite>The National</cite> not spent money the way that it did [...] Peter Richmond wouldn't have had the chance to go to a Cubs game with Bill Murray and then hang out with Fleetwood Mac afterward. Which would have meant that we wouldn't have had the great scene several months later when Murray showed up in the New York offices to see Peter. Not long before that, a guy not many people liked had been fired, and Murray wandered into the daily editorial meeting, propped his flip-flops up on the table, and asked, "Show of hands. How many people thought [blank] was an asshole?" The world would be a poorer place without that moment.</blockquote>
<p><cite>— <a href='http://www.grantland.com/story/_/id/6626434/my-memories-national'>Charles P. Pierce</a></cite></p>
<br />
]]></content:encoded>
			<wfw:commentRss>http://andrewdupont.net/2011/06/10/quotation-charles-p-pierce-2/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
