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

<channel>
	<title>Michael Brunton-Spall</title>
	<atom:link href="http://www.brunton-spall.co.uk/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.brunton-spall.co.uk</link>
	<description>The rants and musings of a developer advocate</description>
	<lastBuildDate>Wed, 22 Feb 2012 22:52:44 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Tech Weekly podcast: when books go social</title>
		<link>http://www.brunton-spall.co.uk/post/2012/01/09/tech-weekly-podcast-when-books-go-social/</link>
		<comments>http://www.brunton-spall.co.uk/post/2012/01/09/tech-weekly-podcast-when-books-go-social/#comments</comments>
		<pubDate>Mon, 09 Jan 2012 11:20:16 +0000</pubDate>
		<dc:creator>bruntonspall</dc:creator>
				<category><![CDATA[From the guardian]]></category>
		<category><![CDATA[Apps]]></category>
		<category><![CDATA[Audio]]></category>
		<category><![CDATA[Books]]></category>
		<category><![CDATA[Charles Arthur]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Internet]]></category>
		<category><![CDATA[iPlayer]]></category>
		<category><![CDATA[Josh Halliday]]></category>
		<category><![CDATA[Michael Brunton-Spall]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[Mobile phones]]></category>
		<category><![CDATA[Rupert Murdoch]]></category>
		<category><![CDATA[Social media]]></category>
		<category><![CDATA[Social networking]]></category>
		<category><![CDATA[Tablet computers]]></category>
		<category><![CDATA[Tech Weekly]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[Twitter]]></category>

		<guid isPermaLink="false">http://www.brunton-spall.co.uk/?p=1507</guid>
		<description><![CDATA[Charles Arthur meets the man behind Anobii.com, a social network for your bookshelf. Plus the curious case of Rupert Murdoch and Wendi Deng's Twitter accounts. Are they real?]]></description>
			<content:encoded><![CDATA[<p><p>I blathered on the podcast for a bit again.</p><hr /><!-- GUARDIAN WATERMARK --><p><a href="http://www.guardian.co.uk/technology/blog/audio/2012/jan/03/social-media-socialnetworking"><img class="alignright" src="http://image.guardian.co.uk/sys-images/Guardian/Pix/pictures/2010/03/01/poweredbyguardianBLACK.png" alt="Powered by Guardian.co.uk" width="140" height="45" />This article titled &#8220;Tech Weekly podcast: when books go social&#8221; was written by Presented by Charles Arthur and produced by Matt Hill, for guardian.co.uk on Tuesday 3rd January 2012 17.36 UTC</a></p><p>How do you discover new books? Matteo Berlucchi is tired of buying books from a certain online retailer only to be recommended very similar reads. He tells us why his <a href="http://www.anobii.com/">aNobii.com</a> is different.</p><p><strong><a href="http://twitter.com/charlesarthur">Charles Arthur</a></strong> sits in the presenter hotseat for this week&#8217;s programme. He&#8217;s joined by <strong>Josh Halliday</strong> and <strong>Michael Brunton-Spall</strong> to discuss the week&#8217;s developments, including what Rupert Murdoch&#8217;s supposed Twitter feed tells us about the verification process and why those network crashes on New Year&#8217;s Eve look to be a thing of the past.</p><p>Plus, <strong><a href="http://twitter.com/aleksk">Aleks Krotoski</a></strong> talks to Chad Dickerson from vintage retailer <a href="http://www.etsy.com/">Etsy</a> about its rise and rise, and how to scale up a business without losing your customer focus.</p><p><strong>Don&#8217;t forget to&#8230;</strong></p><p>• Comment below<br />• Mail the producer <a href="mailto:tech@guardian.co.uk">tech@guardian.co.uk</a><br />• Get our <a href="http://www.twitter.com/guardiantw">Twitter feed</a> for programme updates or follow our <a href="http://twitter.com/#!/guardiantw/guardian-tech-podders">Twitter list</a><br />• Like our <a href="http://www.facebook.com/techweekly">Facebook page</a><br />• See our <a href="http://www.flickr.com/photos/guardiantechweekly/">pics on Flickr</a>/Post <a href="http://www.flickr.com/groups/guardiantechweekly/">your tech pics</a></p><div class="gu_advert"></p>

<pre><code>      &lt;a rel="nofollow" href="http://oas.guardian.co.uk/RealMedia/ads/click_nx.ads/guardianapis.com/technology/oas.html/@Bottom"&gt;
          &lt;img alt="Ads by The Guardian" src="http://oas.guardian.co.uk/RealMedia/ads/adstream_nx.ads/guardianapis.com/technology/oas.html/@Bottom"&gt;&lt;/img&gt;
      &lt;/a&gt;

  &lt;/div&gt;&lt;img src='http://hits.guardian.co.uk/b/ss/guardiangu-api/1/H.20.3/98867?ns=guardian&amp;amp;pageName=Tech+Weekly+podcast%3A+when+books+go+social+Audio+1683419&amp;amp;ch=Technology&amp;amp;c2=67197&amp;amp;c4=Technology%2CSocial+media%2CSocial+networking%2CBooks%2CInternet%2CGoogle+%28Technology%29%2CTablet+computers%2CMicrosoft+%28Technology%29%2CRupert+Murdoch+%28Media%29%2CTwitter+%28Technology%29%2CMobile+phones+%28Technology%29%2CiPlayer%2CApps&amp;amp;c3=guardian.co.uk&amp;amp;c6=Presented+by+Charles+Arthur+and+produced+by+Matt+Hill&amp;amp;c7=12-Jan-03&amp;amp;c8=1683419&amp;amp;c9=Audio' width='1' height='1' /&gt;&lt;!-- Guardian Watermark: technology/blog/audio/2012/jan/03/social-media-socialnetworking|2012-02-22T22:52:42Z|9f867fb1d8513b53b12ab52271f8f417003ea9c1 --&gt;&lt;p&gt;guardian.co.uk &amp;#169; Guardian News &amp;amp; Media Limited 2010&lt;/p&gt; &lt;p&gt;Published via the &lt;a href="http://www.guardian.co.uk/open-platform/news-feed-wordpress-plugin" target="_blank" title="Guardian plugin page"&gt;Guardian News Feed&lt;/a&gt; &lt;a href="http://wordpress.org/extend/plugins/the-guardian-news-feed/" target="_blank" title="Wordress plugin page"&gt;plugin&lt;/a&gt; for WordPress.&lt;/p&gt;&lt;!-- END GUARDIAN WATERMARK --&gt;
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.brunton-spall.co.uk/post/2012/01/09/tech-weekly-podcast-when-books-go-social/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Map, map and flatMap in Scala</title>
		<link>http://www.brunton-spall.co.uk/post/2011/12/02/map-map-and-flatmap-in-scala/</link>
		<comments>http://www.brunton-spall.co.uk/post/2011/12/02/map-map-and-flatmap-in-scala/#comments</comments>
		<pubDate>Fri, 02 Dec 2011 10:56:39 +0000</pubDate>
		<dc:creator>bruntonspall</dc:creator>
				<category><![CDATA[Technical]]></category>
		<category><![CDATA[learning]]></category>
		<category><![CDATA[scala]]></category>

		<guid isPermaLink="false">http://www.brunton-spall.co.uk/?p=1348</guid>
		<description><![CDATA[One of the things I like about Scala is it&#8217;s collections framework. As a non CS graduate I only very lightly covered functional programming at university and I&#8217;d never come...]]></description>
			<content:encoded><![CDATA[<div id="attachment_1351" class="wp-caption alignnone" style="width: 310px"><a href="http://www.brunton-spall.co.uk/wp-content/uploads/2011/12/1464056042_169a1b6a14_z.jpeg"><img class="size-medium wp-image-1351" title="Scala (stairs) by Paolo Campioni" src="http://www.brunton-spall.co.uk/wp-content/uploads/2011/12/1464056042_169a1b6a14_z-300x199.jpg" alt="" width="300" height="199" /></a><p class="wp-caption-text">Scala (stairs) by Paolo Campioni</p></div>

<p>One of the things I like about Scala is it&#8217;s collections framework.  As a non CS graduate I only very lightly covered functional programming at university and I&#8217;d never come across it until Scala.  One the benefits of Scala is that the functional programming concepts can be introduced slowly to the programmer.  One of the first places you&#8217;ll start to use functional constructs is with the collections framework.</p>

<p>Chances are your first collection will be a list of items and we might want to apply a function to each item in the list in some way.</p>

<p>Map works by applying a function to each element in the list.</p>

<pre><code>scala&gt; val l = List(1,2,3,4,5)

scala&gt; l.map( x =&gt; x*2 )
res60: List[Int] = List(2, 4, 6, 8, 10)
</code></pre>

<p>So there are some occasions where you want to return a sequence or list from the function, for example an Option</p>

<pre><code>scala&gt; def f(x: Int) = if (v &gt; 2) Some(v) else None

scala&gt; l.map(x =&gt; f(x))
res63: List[Option[Int]] = List(None, None, Some(3), Some(4), Some(5))
</code></pre>

<p>flatMap works applying a function that returns a sequence for each element in the list, and flattening the results into the original list.  This is easier to show than to explain:</p>

<pre><code>scala&gt; def g(v:Int) = List(v-1, v, v+1)
g: (v: Int)List[Int]

scala&gt; l.map(x =&gt; g(x))
res64: List[List[Int]] = List(List(0, 1, 2), List(1, 2, 3), List(2, 3, 4), List(3, 4, 5), List(4, 5, 6))

scala&gt; l.flatMap(x =&gt; g(x))
res65: List[Int] = List(0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6)
</code></pre>

<p>This comes in really useful with the built in Option class because an option can be considered a sequence that is either empty or has 1 item.</p>

<pre><code>scala&gt; l.map(x =&gt; f(x))
res66: List[Option[Int]] = List(None, None, Some(3), Some(4), Some(5))

scala&gt; l.flatMap(x =&gt; f(x))
res67: List[Int] = List(3, 4, 5)
</code></pre>

<p>So with that all covered, lets look at how you can apply those concepts to a Map. Now a map can be implemented a number of different ways, but regardless of how it is implemented it can be thought of as a sequence of Tuples, where a tuple is a pair of items, the key and the value.</p>

<pre><code>scala&gt; val m = Map(1 -&gt; 2, 2 -&gt; 4, 3 -&gt; 6)
m: scala.collection.immutable.Map[Int,Int] = Map(1 -&gt; 2, 2 -&gt; 4, 3 -&gt; 6)

scala&gt; m.toList
res69: List[(Int, Int)] = List((1,2), (2,4), (3,6))
</code></pre>

<p>We can access a tuple by accessing the inner variables _1 and _2</p>

<pre><code>scala&gt; val t = (1,2)
t: (Int, Int) = (1,2)

scala&gt; t._1
res70: Int = 1

scala&gt; t._2
res71: Int = 2
</code></pre>

<p>So we want to think about using map and flatMap on our Map, but because of the way a map works it often doesn&#8217;t make quite the same sense, we probably don&#8217;t want to apply a function to the tuple, but to the value side of the tuple, leaving the key as is, so for example we might want to double all the values.  Map provides us with a function to do exactly that.</p>

<pre><code>scala&gt; m.mapValues(v =&gt; v*2)
res73: scala.collection.immutable.Map[Int,Int] = Map(1 -&gt; 4, 2 -&gt; 8, 3 -&gt; 12)

scala&gt; m.mapValues(v =&gt; f(v))
res74: scala.collection.immutable.Map[Int,Option[Int]] = Map(1 -&gt; None, 2 -&gt; Some(4), 3 -&gt; Some(6))
</code></pre>

<p>But in my case I wanted to do something more like flat map in this case, I want a map to come out that misses out the key 1 because it&#8217;s value is None.  flatMap doesn&#8217;t work on maps like mapValues, it get&#8217;s passed the tuple and if it returns a List single items you&#8217;ll get a list back, but if you return a tuple you&#8217;ll get a Map back.</p>

<pre><code>scala&gt; m.flatMap(e =&gt; List(e._2))
res85: scala.collection.immutable.Iterable[Int] = List(2, 4, 6)

scala&gt; m.flatMap(e =&gt; List(e))
res86: scala.collection.immutable.Map[Int,Int] = Map(1 -&gt; 2, 2 -&gt; 4, 3 -&gt; 6)
</code></pre>

<p>Ok so we are pretty close to using options with flatMap, we need to filter out our None&#8217;s, we can do returning a list with just e => f(e._2) and we&#8217;ll get the list of values without the None&#8217;s, but that isn&#8217;t really what I want.  What I need to do is return an Option containing a tuple.  So here&#8217;s our updated function:</p>

<pre><code>scala&gt; def h(k:Int, v:Int) = if (v &gt; 2) Some(k-&gt;v) else None
h: (k: Int, v: Int)Option[(Int, Int)]
</code></pre>

<p>and here&#8217;s how we might call it:</p>

<pre><code>scala&gt; m.flatMap ( e =&gt; h(e._1,e._2) )
res109: scala.collection.immutable.Map[Int,Int] = Map(2 -&gt; 4, 3 -&gt; 6)
</code></pre>

<p>but this is pretty ugly, all those _1 and _2&#8242;s make me sad.  If only there was a nice way of unapplying the tuple into variables.  Given that this works in python and in a number of places in scala I thought this code should work:</p>

<pre><code>scala&gt; m.flatMap ( (k,v) =&gt; h(k,v) )
&lt;console&gt;:10: error: wrong number of parameters; expected = 1
</code></pre>

<p>I spent way too long today looking at this (in 5 minute chunks broken by meetings to be fair), before I gave in and asked a coworker what the hell I was missing.  The answer is seems is that an unapply is normally only executed in a PartialFunction, which in scala is most easily defined as a case statement.  So this is the code that works as expected:</p>

<pre><code>scala&gt; m.flatMap { case (k,v) =&gt; h(k,v) }
res108: scala.collection.immutable.Map[Int,Int] = Map(2 -&gt; 4, 3 -&gt; 6)
</code></pre>

<p>Note that we switch to using curly braces, indicating a function block rather than parameters, and the function is a case statement.  This means that the function block we pass to flatMap is a partialFunction that is only invoked for items that match the case statement, and in the case statement the unapply method on tuple is called to extract the contents of the tuple into the variables.  This form of variable extraction is very common, and you&#8217;ll see it used a lot.</p>

<p>There is of course another way of writing that code that doesn&#8217;t use flatMap.  Since what we are doing is removing all members of the map that don&#8217;t match a predicate, this is a use for the filter method:</p>

<pre><code>scala&gt; m.filter( e =&gt; f(e._2) != None )
res114: scala.collection.immutable.Map[Int,Int] = Map(2 -&gt; 4, 3 -&gt; 6)

scala&gt; m.filter { case (k,v) =&gt; f(v) != None }
res115: scala.collection.immutable.Map[Int,Int] = Map(2 -&gt; 4, 3 -&gt; 6)

scala&gt; m.filter { case (k,v) =&gt; f(v).isDefined }
res116: scala.collection.immutable.Map[Int,Int] = Map(2 -&gt; 4, 3 -&gt; 6)
</code></pre>
<p><a class="a2a_button_instapaper" href="http://www.addtoany.com/add_to/instapaper?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F12%2F02%2Fmap-map-and-flatmap-in-scala%2F&amp;linkname=Map%2C%20map%20and%20flatMap%20in%20Scala" title="Instapaper" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/instapaper.png" width="16" height="16" alt="Instapaper"/></a><!--[if IE]><iframe frameborder="0" allowTransparency="true" class="addtoany_special_service twitter_tweet" src="http://platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F12%2F02%2Fmap-map-and-flatmap-in-scala%2F&amp;counturl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F12%2F02%2Fmap-map-and-flatmap-in-scala%2F&amp;count=none&amp;text=Map%2C%20map%20and%20flatMap%20in%20Scala" scrolling="no" style="border:none;overflow:hidden;width:55px;height:20px"></iframe><![endif]--><!--[if !IE]><!--><iframe class="addtoany_special_service twitter_tweet" src="http://platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F12%2F02%2Fmap-map-and-flatmap-in-scala%2F&amp;counturl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F12%2F02%2Fmap-map-and-flatmap-in-scala%2F&amp;count=none&amp;text=Map%2C%20map%20and%20flatMap%20in%20Scala" scrolling="no" style="border:none;overflow:hidden;width:55px;height:20px"></iframe><!--<![endif]--><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F12%2F02%2Fmap-map-and-flatmap-in-scala%2F&amp;linkname=Map%2C%20map%20and%20flatMap%20in%20Scala" title="Delicious" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F12%2F02%2Fmap-map-and-flatmap-in-scala%2F&amp;linkname=Map%2C%20map%20and%20flatMap%20in%20Scala" title="Reddit" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><!--[if IE]><iframe frameborder="0" allowTransparency="true" class="addtoany_special_service facebook_like" src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F12%2F02%2Fmap-map-and-flatmap-in-scala%2F&amp;layout=button_count&amp;show_faces=false&amp;width=75&amp;action=like&amp;colorscheme=light&amp;height=20&amp;ref=addtoany" scrolling="no" style="border:none;overflow:hidden;width:90px;height:21px"></iframe><![endif]--><!--[if !IE]><!--><iframe class="addtoany_special_service facebook_like" src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F12%2F02%2Fmap-map-and-flatmap-in-scala%2F&amp;layout=button_count&amp;show_faces=false&amp;width=75&amp;action=like&amp;colorscheme=light&amp;height=20&amp;ref=addtoany" scrolling="no" style="border:none;overflow:hidden;width:90px;height:21px"></iframe><!--<![endif]--><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F12%2F02%2Fmap-map-and-flatmap-in-scala%2F&amp;title=Map%2C%20map%20and%20flatMap%20in%20Scala" id="wpa2a_2"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://www.brunton-spall.co.uk/post/2011/12/02/map-map-and-flatmap-in-scala/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Annoyed by Guardian Facebook app?</title>
		<link>http://www.brunton-spall.co.uk/post/2011/11/12/annoyed-by-guardian-facebook-app/</link>
		<comments>http://www.brunton-spall.co.uk/post/2011/11/12/annoyed-by-guardian-facebook-app/#comments</comments>
		<pubDate>Sat, 12 Nov 2011 15:26:26 +0000</pubDate>
		<dc:creator>bruntonspall</dc:creator>
				<category><![CDATA[Technical]]></category>
		<category><![CDATA[bookmarklet]]></category>
		<category><![CDATA[Facebook]]></category>
		<category><![CDATA[guardian]]></category>

		<guid isPermaLink="false">http://www.brunton-spall.co.uk/?p=1238</guid>
		<description><![CDATA[Are your friends sharing links to the Guardian Facebook app in their twitter feeds but you don&#8217;t use Facebook and want to see the original guardian page? Unfortunately due to...]]></description>
			<content:encoded><![CDATA[<p>Are your friends sharing links to the Guardian Facebook app in their twitter feeds but you don&#8217;t use Facebook and want to see the original guardian page?</p>

<p>Unfortunately due to the way the web (and Facebook) works, if somebody copies the URL in their browser and posts it to twitter you&#8217;ll be looking at the same thing as them, and if that was a guardian Facebook app, but you haven&#8217;t logged into Facebook, Facebook wont tell us anything so we can&#8217;t redirect you back to the guardian.</p>

<p>However, most modern browsers support special bookmarks with javascript in them, called Bookmarklets, and since the original Facebook url has the important path data needed for the guardian site, it&#8217;s possible to manipulate the current url to take you back to the guardian site without logging into Facebook.</p>

<p>Basically, drag this link: <iframe style="width: 100%; height: 100px;" src="http://jsfiddle.net/bruntonspall/La9yG/embedded/result/" frameborder="0" width="320" height="240"></iframe>
to your bookmarks toolbar up above this page, then click this link: <a href="http://apps.facebook.com/theguardian/music/musicblog/2011/nov/10/10-heaviest-albums-all-time?fb_ref=U-204nodQNlQBg4kqpI36BV6-CFCONX01FRS-339eqXXX,U-1aSIB2sgy2SC4xtjLBhpeM-CFCONX01FRS-339eqXXX,U-aN5g4adecUQD4urFI4BbEC-CFCONX01FRS-339eqXXX,U-2kdBzVKet5QY4dqoIr7TOu-CFCONX01FRS-33992XXX,U-EozmILdgd7yv4xUCJhdt3F-CFCONX01FRS-339nqXXX&amp;fb_source=home_multiline&amp;fb_action_types=news.reads" target="_blank">Are these the 10 heaviest albums ever made?</a> and then click the new fb-&gt;g link on your toolbar and you should go to the guardian site.</p>

<p>This will only work if you are not logged into facebook.
<h2>How does it work?</h2>
The Bookmarklet is a link with a javascript protocol (before the colon).  Your browser will execute the code when you click the button, and it will do so within the context of your current page.</p>

<p>It gets the current url, and looks for a parameter called cancel_url.  That&#8217;s where the url for the original apps.facebook.com is.  Next we replace apps.facebook.com/theguardian with www.guardian.co.uk, then we update window.location with the result.  This causes your browser to load the new url, and you should be on the guardian site.</p>

<p>Note that it does no error checking, it does not handle unusual guardian urls (that might not start www.guardian.co.uk) nor does it check whether there is a cancel_url before acting.</p>

<p>You can view the source and repurpose for your own uses at <a title="jsfiddle" href="http://jsfiddle.net/bruntonspall/La9yG" target="_blank">jsfiddle</a>.</p>
<p><a class="a2a_button_instapaper" href="http://www.addtoany.com/add_to/instapaper?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fannoyed-by-guardian-facebook-app%2F&amp;linkname=Annoyed%20by%20Guardian%20Facebook%20app%3F" title="Instapaper" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/instapaper.png" width="16" height="16" alt="Instapaper"/></a><!--[if IE]><iframe frameborder="0" allowTransparency="true" class="addtoany_special_service twitter_tweet" src="http://platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fannoyed-by-guardian-facebook-app%2F&amp;counturl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fannoyed-by-guardian-facebook-app%2F&amp;count=none&amp;text=Annoyed%20by%20Guardian%20Facebook%20app%3F" scrolling="no" style="border:none;overflow:hidden;width:55px;height:20px"></iframe><![endif]--><!--[if !IE]><!--><iframe class="addtoany_special_service twitter_tweet" src="http://platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fannoyed-by-guardian-facebook-app%2F&amp;counturl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fannoyed-by-guardian-facebook-app%2F&amp;count=none&amp;text=Annoyed%20by%20Guardian%20Facebook%20app%3F" scrolling="no" style="border:none;overflow:hidden;width:55px;height:20px"></iframe><!--<![endif]--><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fannoyed-by-guardian-facebook-app%2F&amp;linkname=Annoyed%20by%20Guardian%20Facebook%20app%3F" title="Delicious" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fannoyed-by-guardian-facebook-app%2F&amp;linkname=Annoyed%20by%20Guardian%20Facebook%20app%3F" title="Reddit" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><!--[if IE]><iframe frameborder="0" allowTransparency="true" class="addtoany_special_service facebook_like" src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fannoyed-by-guardian-facebook-app%2F&amp;layout=button_count&amp;show_faces=false&amp;width=75&amp;action=like&amp;colorscheme=light&amp;height=20&amp;ref=addtoany" scrolling="no" style="border:none;overflow:hidden;width:90px;height:21px"></iframe><![endif]--><!--[if !IE]><!--><iframe class="addtoany_special_service facebook_like" src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fannoyed-by-guardian-facebook-app%2F&amp;layout=button_count&amp;show_faces=false&amp;width=75&amp;action=like&amp;colorscheme=light&amp;height=20&amp;ref=addtoany" scrolling="no" style="border:none;overflow:hidden;width:90px;height:21px"></iframe><!--<![endif]--><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fannoyed-by-guardian-facebook-app%2F&amp;title=Annoyed%20by%20Guardian%20Facebook%20app%3F" id="wpa2a_4"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://www.brunton-spall.co.uk/post/2011/11/12/annoyed-by-guardian-facebook-app/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Adding Google Plus redirect to your Nginx powered site</title>
		<link>http://www.brunton-spall.co.uk/post/2011/11/12/adding-google-plus-redirect-to-your-nginx-powered-site/</link>
		<comments>http://www.brunton-spall.co.uk/post/2011/11/12/adding-google-plus-redirect-to-your-nginx-powered-site/#comments</comments>
		<pubDate>Sat, 12 Nov 2011 12:55:02 +0000</pubDate>
		<dc:creator>bruntonspall</dc:creator>
				<category><![CDATA[Featured]]></category>
		<category><![CDATA[Technical]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Nginx]]></category>
		<category><![CDATA[plus]]></category>

		<guid isPermaLink="false">http://www.brunton-spall.co.uk/?p=1230</guid>
		<description><![CDATA[A quick one, this morning I&#8217;ve added the plus url to my website, so http://www.brunton-spall.co.uk/+ now redirects to my Google+ profile. This, it turns out, is really easy to implement...]]></description>
			<content:encoded><![CDATA[<p>A quick one, this morning I&#8217;ve added the plus url to my website, so <a href="http://www.brunton-spall.co.uk/+" target="_blank">http://www.brunton-spall.co.uk/+</a> now redirects to my Google+ profile.</p>

<p>This, it turns out, is really easy to implement if you run Nginx as the front to your website.</p>

<p>You simply need to update your server configuration to include the following snippet:
<pre>
location ~ ^/?&#43;$ {
  rewrite ^ https://plus.google.com/YOUR_NUMBER permanent;
}
</pre></p>

<p>This will ensure that your domain.tld/+ issues a redirect to your Google+ profile.</p>

<p>Hope this helps</p>
<p><a class="a2a_button_instapaper" href="http://www.addtoany.com/add_to/instapaper?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fadding-google-plus-redirect-to-your-nginx-powered-site%2F&amp;linkname=Adding%20Google%20Plus%20redirect%20to%20your%20Nginx%20powered%20site" title="Instapaper" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/instapaper.png" width="16" height="16" alt="Instapaper"/></a><!--[if IE]><iframe frameborder="0" allowTransparency="true" class="addtoany_special_service twitter_tweet" src="http://platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fadding-google-plus-redirect-to-your-nginx-powered-site%2F&amp;counturl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fadding-google-plus-redirect-to-your-nginx-powered-site%2F&amp;count=none&amp;text=Adding%20Google%20Plus%20redirect%20to%20your%20Nginx%20powered%20site" scrolling="no" style="border:none;overflow:hidden;width:55px;height:20px"></iframe><![endif]--><!--[if !IE]><!--><iframe class="addtoany_special_service twitter_tweet" src="http://platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fadding-google-plus-redirect-to-your-nginx-powered-site%2F&amp;counturl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fadding-google-plus-redirect-to-your-nginx-powered-site%2F&amp;count=none&amp;text=Adding%20Google%20Plus%20redirect%20to%20your%20Nginx%20powered%20site" scrolling="no" style="border:none;overflow:hidden;width:55px;height:20px"></iframe><!--<![endif]--><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fadding-google-plus-redirect-to-your-nginx-powered-site%2F&amp;linkname=Adding%20Google%20Plus%20redirect%20to%20your%20Nginx%20powered%20site" title="Delicious" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fadding-google-plus-redirect-to-your-nginx-powered-site%2F&amp;linkname=Adding%20Google%20Plus%20redirect%20to%20your%20Nginx%20powered%20site" title="Reddit" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><!--[if IE]><iframe frameborder="0" allowTransparency="true" class="addtoany_special_service facebook_like" src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fadding-google-plus-redirect-to-your-nginx-powered-site%2F&amp;layout=button_count&amp;show_faces=false&amp;width=75&amp;action=like&amp;colorscheme=light&amp;height=20&amp;ref=addtoany" scrolling="no" style="border:none;overflow:hidden;width:90px;height:21px"></iframe><![endif]--><!--[if !IE]><!--><iframe class="addtoany_special_service facebook_like" src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fadding-google-plus-redirect-to-your-nginx-powered-site%2F&amp;layout=button_count&amp;show_faces=false&amp;width=75&amp;action=like&amp;colorscheme=light&amp;height=20&amp;ref=addtoany" scrolling="no" style="border:none;overflow:hidden;width:90px;height:21px"></iframe><!--<![endif]--><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F11%2F12%2Fadding-google-plus-redirect-to-your-nginx-powered-site%2F&amp;title=Adding%20Google%20Plus%20redirect%20to%20your%20Nginx%20powered%20site" id="wpa2a_6"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://www.brunton-spall.co.uk/post/2011/11/12/adding-google-plus-redirect-to-your-nginx-powered-site/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Identifiers are not numbers</title>
		<link>http://www.brunton-spall.co.uk/post/2011/09/24/identifiers-are-not-numbers/</link>
		<comments>http://www.brunton-spall.co.uk/post/2011/09/24/identifiers-are-not-numbers/#comments</comments>
		<pubDate>Sat, 24 Sep 2011 12:13:14 +0000</pubDate>
		<dc:creator>bruntonspall</dc:creator>
				<category><![CDATA[Featured]]></category>
		<category><![CDATA[Rants]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[rants]]></category>

		<guid isPermaLink="false">http://www.brunton-spall.co.uk/?p=1026</guid>
		<description><![CDATA[&#8220;I am not a number, I am a free man&#8221; So something bugs me (I know, big shock), and it&#8217;s API documentation like this: The document will be returned as:...]]></description>
			<content:encoded><![CDATA[<p><em>&#8220;I am not a number, I am a free man&#8221;</em></p>

<p>So something bugs me (I know, big shock), and it&#8217;s API documentation like this:</p>

<pre><code>The document will be returned as:
ID: Integer
Name: String
Published: Date/Time
</code></pre>

<p>My position on this is simple, in an API (we&#8217;ll come back to internal design later), an ID should <em>always</em> be of String type.</p>

<p>So why is this a big deal?  I mean you implement this with a database table with autoincrement right, so it is a number underneath?</p>

<p>There&#8217;s a few issues with this and I&#8217;ll deal with them in reverse stupidity order.</p>

<h2>Numbers are more than identifiers</h2>

<p>A number represents a mathematical concept.  It implies strict ordering (2 is always > than 1), it implies a set of operations (1+2=3) and it implies an infinite set.
If you provide external developers with numbers they are going to assume that these implications are available and might make some form of sense.</p>

<p>You would hope that most people wouldn&#8217;t assume that something like twitter ids can be added or multiplied and expect anything sensible from them, but that comparison function, that&#8217;s a bit more tricky. It&#8217;s true that tweet 1 was sent before tweet 2, and even that tweet 1,000,000,000 was sent before tweet 2,000,000,000.  But as twitter scales guaranteeing that ordering becomes harder and harder in a massively parallel system, and there is no value in a client ordering tweets this way.  Each tweet has a date/time field on it indicating when it was sent, and ordering based on that field is far more semantically correct (and what the user expects).</p>

<h2>Numbers aren&#8217;t equal in all languages</h2>

<p>If you argue that representing something as a number is saving you memory, because an integer only occupies say 2 bytes, you are probably prematurely optimising.
Number representation is a very complex subject, one which I know only a little, but I know enough to know that I should never make assumptions about how a number is represented internally.</p>

<p>I come from a C background, and despite it being fairly common knowledge how big various numbers are stored, we have a sizeof operator for a reason.  Compiling your program on a slightly different architecture could change your integers from 2 bytes to 4 bytes to 8 bytes to anything inbetween. It&#8217;s a pretty common source of bugs for new developers to assume that unsigned int a is 4 bytes in size.</p>

<p>When you provide a number in an API, You have no idea who is consuming the API or what system they are running on.  It might be an x86 EC2 instance, or it might be a mobile device, it might be a super computer, or a graphic compute unit.</p>

<p>It&#8217;s fine in your system when you create your 4 billionth item and exceed the 32bit MAXINT (4,294,967,295 for unsigned ints), but will it be fine for all your users?  If your samples and current users get numbers in the low 100&#8242;s, but 2 years down the line you have a wildly successful product and your id&#8217;s are now into the billions upon billions?  Do you want people writing API libraries to have to worry and think about this?</p>

<p>One fantastic issue that <a href="https://dev.twitter.com/docs/twitter-ids-json-and-snowflake">twitter discovered</a> fairly recently is that javascript and therefore many json libraries are required to <a href="http://ecma262-5.com/ELS5_HTML.htm#Section_8.5">represent integers as a 53 bit number</a>, that&#8217;s a pretty large number, but it&#8217;s several orders of magnitude less than most people would expect.</p>

<h2>You make database scaling hard</h2>

<p>Autoincrementing primary keys are fine if your datastore is a single logical machine (a highly connected cluster), but as you add parallelism, there are a number of situations where you might want to be able to allocate id&#8217;s from more than one cluster at the same time without synchronisation between them.</p>

<p>If your ID&#8217;s in your table are not autoincrementing integers, but are an identifier that maybe looks like &#8220;DC1-DB1-0001&#8243; (or some other similar system), an identifier that takes into account the datacenter and database identifiers, reconciliation for tweet creation is perfectly possible.</p>

<h2>You lock yourself into technologies</h2>

<p>Autoincrementing primary keys are a feature of most relational databases.  Some support it natively, some support it with triggers and stored procedures.
However for scalability reasons, most non-relational databases don&#8217;t support them.  Mongo for example provides primary keys that are OID&#8217;s or Object ID&#8217;s.  These are a UUID4 type identifier that takes into account the host, the time and a few other things at creation time.  BigTable provides horrible &#8220;keys&#8221; that are totally opaque, I have no idea how a key is generated (nor should I need to know).</p>

<p>Adding an autoincrementing key on top of these technologies is often possible, but it is often not ideal.</p>

<h2>Conclusion</h2>

<p>So there is a number of good reasons not to use numbers as an identifier in your API.  Just use a string, trust me on this, you&#8217;ll use sightly more bytes, but you wont have to convert the integer into a string at API render time, and your client wont convert it back.  Your string will be opaque to the user, meaning you can change primary identifier format willy nilly and providing the old and new format don&#8217;t conflict it won&#8217;t matter.  Nobody will try to use false ordering on your ID&#8217;s when they should use a field instead, and it wont make me angry and ranty when I read your API documentation.</p>

<h2>Addendum</h2>

<p>I&#8217;m not saying don&#8217;t use a number in your database schema.  It&#8217;s entirely possible for your ID&#8217;s that you know you&#8217;ll only ever generate a few hundred ID&#8217;s and that you wont have a date/time field, but order of creation is important and ordering the ID&#8217;s is the simplest way to do so.  There are many valid reasons to use a numerical id internally for various things, but when you convert it into an API or external system, just turn that number into a string.</p>

<p>I strongly suspect that there will be people who will see your &#8220;{ &#8216;id&#8217;:&#8217;123&#8242; }&#8221; and will turn it into a number themselves, but then later if you return a document &#8220;{ &#8216;id&#8217;: &#8217;01-007&#8242;}&#8221; they can&#8217;t really complain, because you always told them it was an opaque string.</p>
<p><a class="a2a_button_instapaper" href="http://www.addtoany.com/add_to/instapaper?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F24%2Fidentifiers-are-not-numbers%2F&amp;linkname=Identifiers%20are%20not%20numbers" title="Instapaper" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/instapaper.png" width="16" height="16" alt="Instapaper"/></a><!--[if IE]><iframe frameborder="0" allowTransparency="true" class="addtoany_special_service twitter_tweet" src="http://platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F24%2Fidentifiers-are-not-numbers%2F&amp;counturl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F24%2Fidentifiers-are-not-numbers%2F&amp;count=none&amp;text=Identifiers%20are%20not%20numbers" scrolling="no" style="border:none;overflow:hidden;width:55px;height:20px"></iframe><![endif]--><!--[if !IE]><!--><iframe class="addtoany_special_service twitter_tweet" src="http://platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F24%2Fidentifiers-are-not-numbers%2F&amp;counturl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F24%2Fidentifiers-are-not-numbers%2F&amp;count=none&amp;text=Identifiers%20are%20not%20numbers" scrolling="no" style="border:none;overflow:hidden;width:55px;height:20px"></iframe><!--<![endif]--><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F24%2Fidentifiers-are-not-numbers%2F&amp;linkname=Identifiers%20are%20not%20numbers" title="Delicious" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F24%2Fidentifiers-are-not-numbers%2F&amp;linkname=Identifiers%20are%20not%20numbers" title="Reddit" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><!--[if IE]><iframe frameborder="0" allowTransparency="true" class="addtoany_special_service facebook_like" src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F24%2Fidentifiers-are-not-numbers%2F&amp;layout=button_count&amp;show_faces=false&amp;width=75&amp;action=like&amp;colorscheme=light&amp;height=20&amp;ref=addtoany" scrolling="no" style="border:none;overflow:hidden;width:90px;height:21px"></iframe><![endif]--><!--[if !IE]><!--><iframe class="addtoany_special_service facebook_like" src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F24%2Fidentifiers-are-not-numbers%2F&amp;layout=button_count&amp;show_faces=false&amp;width=75&amp;action=like&amp;colorscheme=light&amp;height=20&amp;ref=addtoany" scrolling="no" style="border:none;overflow:hidden;width:90px;height:21px"></iframe><!--<![endif]--><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F24%2Fidentifiers-are-not-numbers%2F&amp;title=Identifiers%20are%20not%20numbers" id="wpa2a_8"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://www.brunton-spall.co.uk/post/2011/09/24/identifiers-are-not-numbers/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Scala, lazy collections, streams and recursion</title>
		<link>http://www.brunton-spall.co.uk/post/2011/09/01/scala-lazy-collections-streams-and-recursion/</link>
		<comments>http://www.brunton-spall.co.uk/post/2011/09/01/scala-lazy-collections-streams-and-recursion/#comments</comments>
		<pubDate>Thu, 01 Sep 2011 17:08:22 +0000</pubDate>
		<dc:creator>bruntonspall</dc:creator>
				<category><![CDATA[Technical]]></category>
		<category><![CDATA[learning]]></category>
		<category><![CDATA[scala]]></category>

		<guid isPermaLink="false">http://www.brunton-spall.co.uk/?p=912</guid>
		<description><![CDATA[I&#8217;m currently rewriting the deployment system at the guardian in Scala, and although I&#8217;d say I know Scala, I&#8217;m learning lots of things as we go.  I&#8217;m lucky enough to...]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m currently rewriting the deployment system at the guardian in Scala, and although I&#8217;d say I know Scala, I&#8217;m learning lots of things as we go.  I&#8217;m lucky enough to be pairing with <a href="http://blog.tackley.net/" target="_blank">Graham Tackley</a>, our platform team lead and someone who knows Scala far better than I do, and this means that we often write a bit of code, then go back and improve it and so forth.</p>

<p>The bit of Scala code I&#8217;m particularly proud of is around waiting for a port to be opened.  During our deployment scripts we restart our application servers, then we want the scripts to wait until the server is up and able to serve requests before we move on.</p>

<p>Writing this kind of code is not terribly difficult, but testing that it works can be very tricky.  I decided to take the blackbox kind of approach to this, and my test spins up a second thread that listens on the port in question so that we can execute the real code that attempts to connect to the port.  In java or most other languages this would be something fairly long winded, but in my Scala test it looks like this:</p>

<p><script type="text/javascript" src="https://gist.github.com/1187013.js?file=spawn_test.scala"></script>The spawn function is provided by concurrent.ops and is built into Scala and represents a throwaway thread that we aren&#8217;t terribly interested in.  We don&#8217;t want to join on it, we just want it to start up and execute. The code inside creates a new java.net.ServerSocket, calls the blocking accept method which blocks until it gets a connection, closes the connection and then closes the original listening connection. We can then create and execute our port connection code and it should validly connect to the port 9998 on localhost and work beautifully.</p>

<p>The next test needed to be the failure case, if the waitForPort task cannot connect to a port after a specified timeout it should error, preferably by throwing an exception of some form. Testing this was also fairly easy, I wrote the traditional try { blah(); fail()} catch&#8230; stuff, then checked with Graham if there was a better way, and of course there was:<script type="text/javascript" src="https://gist.github.com/1187013.js?file=test_evaluating.scala"></script></p>

<p>ScalaTest with the ShouldMatchers makes for a simply beautiful test that is clear to read and was easy to write.</p>

<p>However when it came to implementing the method, I struggled a little bit.  I&#8217;ve never learnt functional programming, so closures and maps and so forth are still a bit alien to me, so I ended up writing some very java style code:</p>

<p><script type="text/javascript" src="https://gist.github.com/1187013.js?file=javaish.scala"></script>The isSocketOpen simply tries to open a socket and catches any exceptions to return false, and true if it opened successfully. Having written this, knowing there was a better solution, I asked again, and this prompted Phil Wills to upload his blogpost about the <a href="http://blog.phil-wills.com/streams-of-pleasure" target="_blank">Stream class and lazy evaluation</a>. I won&#8217;t cover the details here, but essentially using the return statement in Scala is frowned upon because of the way it works, and it&#8217;s more idiomatic Scala to use Options and flatMap, here&#8217;s the updated code:<script type="text/javascript" src="https://gist.github.com/1187013.js?file=range.scala"></script></p>

<p>Here we&#8217;ve changed isSocketOpen to checkSocketOpen and made it return an option.  At this point I also made checkSocketOpen do the sleep, which we could have done before, but is necessary now.</p>

<p>To explain, this code takes the range 1 to 10, which is a Seq of ints (1,2,3&#8230;) and we flatMap across it with a function that returns an option.  If you haven&#8217;t seen this before, here&#8217;s a quick overview of what the flatMap actually does:
<p style="padding-left: 30px;">map applies a function on each item in a collection and returns a transformed collection, essentially [a,b].map(f) is the same as calling [f(a), f(b)].</p>
<p style="padding-left: 30px;">If our function f returns an Option, this is either Some(true) or None.  An Option can really be thought of as a sequence that either contains 1 item or is empty, so Some(true) is the same as [true] and None is the same as [].</p>
<p style="padding-left: 30px;">This means our assuming that our function f returns None for everything but c, the map on [a, b, c, d] will return something like [ [], [], [true], [] ].  That&#8217;s often not a lot of use for us, and what we are interested in is stuff that returned something.</p>
<p style="padding-left: 30px;">flatMap is a function that essentially flattens the returned sequences catenated together, so if f(x) = [1,2] and f(y) = [4,5], then [x, y] map f == [ [1,2], [4,5] ] but [x, y] flatMap f == [ 1, 2, 3, 4 ].  This also has the handy shortcut that [] flattens into nothing, so essentially gets skipped, so our flatMap on [a, b, c, d] should return [true].</p>
We then use headOption, since it&#8217;s possible that we returned either [true], [true, true...] or [].  head would return us the first true, but on an empty list would die horribly, headOption returns an option that is Some(h) where the list is [h, t] and None where the list is [].  We then match on the option and throw an error if we got None (and therefore not a single call returned a true).</p>

<p>Our function checkSocketOpen has a side effect of pausing for an amount of time though, and since flatMap executes the function once for each item in the original list, this means that we will always pause for the entire connection timeout no matter whether we successfully connect the first time or not. (kind of, actually if we only pause on a failed connection, and if we successfully connect each time, we wont pause, but we will create 10 connections rather than just the one we need to confirm the port is open).</p>

<p>Phil in his blog has a very simple fix for this, which is to wrap our original sequence in a Stream class.  Stream is another Scala provided class, and in this case it is a sequence that is lazily evaluated.  Essentially this means that the flatMap doesn&#8217;t call the checkSocketOpen on all items immediately but instead only calls it when you try to get to the first item.  This means that headOption executes each function one after another until one of them returns true or it gets to the end of the sequence, it then returns the option based on what happened.</p>

<p>This means that if the first call returned none, it moved on and called f again over and over until it returned true, then the headOption stopped and never called the remaining calls, making this system stop as soon as the first connection was made.</p>

<p>Success! We&#8217;ve removed the return statement and made the code significantly harder to read and understand. <img src='http://www.brunton-spall.co.uk/wp-includes/images/smilies/icon_sad.gif' alt=':-(' class='wp-smiley' />   I&#8217;ve just taken several hundred words explaining why it works and I&#8217;m still not entirely clear on the details.</p>

<p>So we started wondering if there was an alternative way of doing this, something that would still be idiomatic Scala but that would be obvious to junior or learning Scala programmers like myself. Graham and I wondered if what a recursive solution would look like, so we came up with this code:</p>

<p><script src="https://gist.github.com/1187013.js?file=recursive.scala"></script>&nbsp;</p>

<p>We decided very early on to inline the checkSocketOpen function as recursive itself, so although this looks like more lines of code, it now combines both functions.</p>

<p>This function, when called at the bottom with checkOpen(0) initialises currentTry to 0, then tries to open a socket, if it is successful it just returns, if attempting to open a socket throws an exception it sleeps for a bit, then calls checkOpen with currentTry incremented by one.</p>

<p>All recursive functions have to have a guard, which is to say some statement that prevents infinite recursion (unless that&#8217;s what you want anyway), this function checks to find out if currentTry is greater than MAX<em>CONNECTION</em>ATTEMPTS and if it is, sys.error throws an exception which bubbles all the way back up through 10 copies of checkOpen before exiting properly.</p>

<p>If you know that your range is bounded fairly low, (such as 10 retry attempts) this is a perfectly acceptable way of writing this function.  We suspect because of the way we&#8217;ve written the try catch that it&#8217;s not possible to make this function tail recursive, which would effectively allow infinite (or lots) recursive calls, but I&#8217;m sure it&#8217;s possible to adjust the try catch into a subfunction that might make it tail recursive if you really wanted to.</p>

<p>&nbsp;</p>

<p>So there you go, that&#8217;s been a day of Scala learning for me, I now know the issues with the return statement, the Stream classes which might be useful later, and I&#8217;ve written my first truly recursive program in anger.  I&#8217;m feeling a little bit proud.</p>

<p>Of course there are improvements to make, making the function tail recursive would be education as I&#8217;m aware of tail recursion but am not entirely sure how to actually make a function tail recursive.  I&#8217;d also love to make that ServerSocket stuff into one line again, originally I had ServerSocket(port).accept().close() as one line, but I discovered in testing the stream stuff that the ServerSocket hangs around and keeps accepting connections if you don&#8217;t close it, which I can&#8217;t do in a single line without keeping a reference to it.</p>

<p>If you are interested in our new deploy system Magenta and the upcoming RifRaf web application for it, then I suggest you keep an eye out on our <a href="http://github.com/guardian" target="_blank">github repository</a> and feel free to send patches, advice and help our way.</p>
<p><a class="a2a_button_instapaper" href="http://www.addtoany.com/add_to/instapaper?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F01%2Fscala-lazy-collections-streams-and-recursion%2F&amp;linkname=Scala%2C%20lazy%20collections%2C%20streams%20and%20recursion" title="Instapaper" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/instapaper.png" width="16" height="16" alt="Instapaper"/></a><!--[if IE]><iframe frameborder="0" allowTransparency="true" class="addtoany_special_service twitter_tweet" src="http://platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F01%2Fscala-lazy-collections-streams-and-recursion%2F&amp;counturl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F01%2Fscala-lazy-collections-streams-and-recursion%2F&amp;count=none&amp;text=Scala%2C%20lazy%20collections%2C%20streams%20and%20recursion" scrolling="no" style="border:none;overflow:hidden;width:55px;height:20px"></iframe><![endif]--><!--[if !IE]><!--><iframe class="addtoany_special_service twitter_tweet" src="http://platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F01%2Fscala-lazy-collections-streams-and-recursion%2F&amp;counturl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F01%2Fscala-lazy-collections-streams-and-recursion%2F&amp;count=none&amp;text=Scala%2C%20lazy%20collections%2C%20streams%20and%20recursion" scrolling="no" style="border:none;overflow:hidden;width:55px;height:20px"></iframe><!--<![endif]--><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F01%2Fscala-lazy-collections-streams-and-recursion%2F&amp;linkname=Scala%2C%20lazy%20collections%2C%20streams%20and%20recursion" title="Delicious" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F01%2Fscala-lazy-collections-streams-and-recursion%2F&amp;linkname=Scala%2C%20lazy%20collections%2C%20streams%20and%20recursion" title="Reddit" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><!--[if IE]><iframe frameborder="0" allowTransparency="true" class="addtoany_special_service facebook_like" src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F01%2Fscala-lazy-collections-streams-and-recursion%2F&amp;layout=button_count&amp;show_faces=false&amp;width=75&amp;action=like&amp;colorscheme=light&amp;height=20&amp;ref=addtoany" scrolling="no" style="border:none;overflow:hidden;width:90px;height:21px"></iframe><![endif]--><!--[if !IE]><!--><iframe class="addtoany_special_service facebook_like" src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F01%2Fscala-lazy-collections-streams-and-recursion%2F&amp;layout=button_count&amp;show_faces=false&amp;width=75&amp;action=like&amp;colorscheme=light&amp;height=20&amp;ref=addtoany" scrolling="no" style="border:none;overflow:hidden;width:90px;height:21px"></iframe><!--<![endif]--><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F09%2F01%2Fscala-lazy-collections-streams-and-recursion%2F&amp;title=Scala%2C%20lazy%20collections%2C%20streams%20and%20recursion" id="wpa2a_10"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://www.brunton-spall.co.uk/post/2011/09/01/scala-lazy-collections-streams-and-recursion/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Google+ &#8211; A gentle introduction</title>
		<link>http://www.brunton-spall.co.uk/post/2011/07/01/googleplus-a-gentle-introduction/</link>
		<comments>http://www.brunton-spall.co.uk/post/2011/07/01/googleplus-a-gentle-introduction/#comments</comments>
		<pubDate>Fri, 01 Jul 2011 21:48:59 +0000</pubDate>
		<dc:creator>bruntonspall</dc:creator>
				<category><![CDATA[Featured]]></category>
		<category><![CDATA[Google]]></category>

		<guid isPermaLink="false">http://www.brunton-spall.co.uk/?p=639</guid>
		<description><![CDATA[So a couple of days ago Google launched Google+, a product that did not slip out quietly it seems despite Google’s intentions. You’ve probably read journalists who love it and...]]></description>
			<content:encoded><![CDATA[<p>So a couple of days ago Google launched <a href="http://plus.google.com" target="_blank">Google+</a>, a product that did not slip out quietly it seems despite Google’s intentions.</p>

<p>You’ve probably read journalists who love it and journalists that hated it, but since the cat appears to be out of the bag and the invitations are flowing fast and furious I thought I’d write a quick few bits about how to get started using it.</p>

<p>First of all your invitation.  If somebody you know has invited you, there’s a good chance that you didn’t realise it was an invitation.  There are 2 forms of invitation.</p>

<p>As a &#8220;member of the press&#8221; (by association not directly) I got a real invitation from the Google PR team, which appeared to come from one of our developers.  It looked a bit like this and it was a real invitation.</p>

<div id="attachment_660" class="wp-caption alignnone" style="width: 310px"><a href="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-An-Invite-4.jpg"><img class="size-medium wp-image-660" title="Google+ - An Invite" src="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-An-Invite-4-300x266.jpg" alt="" width="300" height="266" /></a><p class="wp-caption-text">A real full invite</p></div>

<p><a href="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-An-Invite-4.jpg"></a>You can see clearly that it is an invitation and you can sign up from the mail easily.</p>

<p>During the first few hours, Google ‘accidentally’ left a send invitation feature into Google+, which was quickly noticed by the press and the number of people coming in massively increased.  Google quickly disabled the send an invite functionality, and we don’t know when it’s coming back.</p>

<p>However there is another way to invite people to Google+ and they get an email that looks like this:</p>

<div id="attachment_641" class="wp-caption alignnone" style="width: 310px"><a href="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-An-Invite-1-1.jpg"><img class="size-medium wp-image-641 " title="Google+ An Invite" src="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-An-Invite-1-1-300x90.jpg" alt="" width="300" height="90" /></a><p class="wp-caption-text">This is actually an invite</p></div>

<p>Firstly this is not an official way of inviting someone, and may not work.  This email doesn’t say invitation except in tiny print at the bottom of the email, and instead looks like an opportunity to see the marketing bumph about Google+.</p>

<p>But clicking that button will result in your seeing one of two screens, if you are unlucky, it appears that Google are disabling signups globally for a few hours at a time, and you’ll get the “We are overloaded” page.</p>

<div id="attachment_642" class="wp-caption alignnone" style="width: 310px"><a href="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-Overloaded.jpg"><img class="size-medium wp-image-642" title="Google+ Overloaded" src="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-Overloaded-300x145.jpg" alt="" width="300" height="145" /></a><p class="wp-caption-text">When Google is not allowing invites, this is what you see</p></div>

<p>If you get this try again in a few hours (or minutes) and you might get lucky and get the “Make an account” page (I don&#8217;t have a screenshot I&#8217;m afraid, you can&#8217;t miss it, it asks for your name and has a big signup button).</p>

<p>&nbsp;
<h2>First steps in Google+</h2>
The first page you’ll see will contain a lot of welcome bumph.  Go ahead and read it, watch the videos and come back after you’ve experimented.  I’ll wait for you.If you are a popular kind of person with geeks, then there is a good chance that you will have a number of notifications that you are in peoples circles already.</p>

<div id="attachment_643" class="wp-caption alignnone" style="width: 310px"><a href="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-Sharing-with-me.jpg"><img class="size-medium wp-image-643" title="Google+ - Sharing with me" src="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-Sharing-with-me-300x115.jpg" alt="" width="300" height="115" /></a><p class="wp-caption-text">People are sharing with me</p></div>

<p>If you unlucky and only know one or two geeks then it’s possible that the only person who has added you is the person who invited you.  Never mind, you can find friends all on your own.</p>

<p>First of all, you can simply mine your gmail contact book.  This is particularly easy to do, and Google have put a lot of thought into it.</p>

<div id="attachment_648" class="wp-caption alignnone" style="width: 310px"><a href="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-Adding-friends-2.jpg"><img class="size-medium wp-image-648" title="Google+ Adding friends" src="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-Adding-friends-2-300x64.jpg" alt="" width="300" height="64" /></a><p class="wp-caption-text">A list of friends</p></div>

<p>In the section that looks like this under your circles, you can see all of your contacts who are not yet on Google+.  At first you will probably instinctively start dragging contacts down to the relevant circle one at a time.  This is a fairly inefficient way of sorting your contacts.</p>

<div id="attachment_645" class="wp-caption alignnone" style="width: 310px"><a href="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-Selecting-multiple-friends.jpg"><img class="size-medium wp-image-645" title="Google+ - Selecting multiple friends" src="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-Selecting-multiple-friends-300x67.jpg" alt="" width="300" height="67" /></a><p class="wp-caption-text">Click to select multiple friends</p></div>

<p>Instead simply click all of the contacts that you want in a single circle (say the 25 members of my family I wanted to select) and then drag one of them, you should see that they all come down and group together to be dropped into a circle.  This makes life a lot easier when sorting out those tricky large groups.</p>

<div id="attachment_649" class="wp-caption alignnone" style="width: 238px"><a href="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-Create-a-new-circle.jpg"><img class="size-full wp-image-649" title="Google+ Create a new circle" src="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-Create-a-new-circle.jpg" alt="" width="228" height="190" /></a><p class="wp-caption-text">Creating a new circle of friends</p></div>

<p>Create lots of circles, you can add people to multiple circles, and it’s very easy to rename, delete and even move people from one circle to another.  I currently have circles for my church, my friends from Milton Keynes, my friends from School, people I’ve worked with, and people I currently work with at the Guardian.</p>

<p>Speaking of people you went to school with, I don’t really use GMail (especially not my gmail account itself) to email people, so I only had 60 − 80 addresses in there.  For a number of my contacts, I instead use Facebook.  At the time I’m writing this, adding your Facebook contacts requires a couple of steps.  I expect this to change eventually, but for now, it turns out that Yahoo works as a very good middleman.</p>

<p><a href="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Yahoo-Mail.jpg"><img class="alignnone size-medium wp-image-652" title="Yahoo! Mail" src="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Yahoo-Mail-300x162.jpg" alt="" width="300" height="162" /></a></p>

<p><a href="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Yahoo-Mail.jpg"></a>Login to Yahoo Mail for your Yahoo account (or create one if you don’t have one).  Click the contacts link, and select the Import Contacts link from the Popular Tools that you should see.</p>

<p><a href="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Yahoo-Add-Facebook.jpg"><img class="alignnone size-medium wp-image-651" title="Yahoo Add Facebook" src="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Yahoo-Add-Facebook-300x169.jpg" alt="" width="300" height="169" /></a></p>

<p><a href="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Yahoo-Add-Facebook.jpg"></a>You can now select Facebook, enter your Facebook details and Yahoo will happily slurp all of your Facebook friends into your Yahoo email list.  Handy huh?</p>

<div id="attachment_653" class="wp-caption alignnone" style="width: 310px"><a href="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-Find-and-Invite-1.jpg"><img class="size-medium wp-image-653" title="Google+ - Find and Invite" src="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-Find-and-Invite-1-300x36.jpg" alt="" width="300" height="36" /></a><p class="wp-caption-text">Find and invite from Yahoo</p></div>

<p>Now back in Google+, Under the circles tab, select the Find and Invite link and you should be able to add your Yahoo contacts.  Authorise it from your Yahoo account by following the instructions and suddenly all of your Facebook friends should be available in the Find and Invite tab.</p>

<div id="attachment_647" class="wp-caption alignnone" style="width: 157px"><a href="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-Someones-circles.jpg"><img class="size-medium wp-image-647" title="Google+ - Someones circles" src="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-Someones-circles-147x300.jpg" alt="" width="147" height="300" /></a><p class="wp-caption-text">People who are in paul&#39;s circles</p></div>

<p>Finally, once you’ve got a few friends who are on Google+, you can start looking at their circles to see if there are any friends that you are missing.  In pretty much any section of Google+ you can click somebodies name and get to their profile page.  On the left is a quick view of a few people in their circle, and people who have added them to their circles (and people you’ve got in common).</p>

<div id="attachment_644" class="wp-caption alignnone" style="width: 310px"><a href="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-People-in-circles.jpg"><img class="size-medium wp-image-644" title="Google+ - People in circles" src="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-People-in-circles-300x173.jpg" alt="" width="300" height="173" /></a><p class="wp-caption-text">People in pauls circles</p></div>

<p>If you hit the view more, you can get a nice view of all their contacts with an easy way to add them to your own circles.  With this you can very easily start adding a large number of people who are already on Google circles.</p>

<p>Doing all of this should give you a fair number of people into your circles, but you might be missing a number of the less geeky people in your life.  You might have them in your circles, but they don’t have a Google+ profile yet.  How disappointing.  So lets discuss invites.I cannot guarantee that this works, the only sure way to send invites is to wait for Google to fully enable invites.   For now the following seems to send out the magic email.</p>

<div id="attachment_654" class="wp-caption alignnone" style="width: 310px"><a href="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-Email-friends.jpg"><img class="size-medium wp-image-654" title="Google+ Email friends" src="http://www.brunton-spall.co.uk/wp-content/uploads/2011/07/Google+-Email-friends-300x116.jpg" alt="" width="300" height="116" /></a><p class="wp-caption-text">Email your friends</p></div>

<p>Go to your stream and start sharing something.  Remove the Public/All Circles users that are currently in the list and add a circle that has members you want to invite.  You should see an option to email people not on Google+, tick it and confirm the pop up that comes up when you click share.</p>

<p>This will cause each of those people to get an email indicating that they’ve been mentioned, and they can click the link in the email and hopefully they’ll get in and sign up.</p>

<p>I hope you found this useful, and feel free to <a href="https://plus.google.com/100589676414609537192/posts?tab=XX">find me on Google+</a> and say hello or thanks.</p>
<p><a class="a2a_button_instapaper" href="http://www.addtoany.com/add_to/instapaper?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F07%2F01%2Fgoogleplus-a-gentle-introduction%2F&amp;linkname=Google%2B%20%26%238211%3B%20A%20gentle%20introduction" title="Instapaper" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/instapaper.png" width="16" height="16" alt="Instapaper"/></a><!--[if IE]><iframe frameborder="0" allowTransparency="true" class="addtoany_special_service twitter_tweet" src="http://platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F07%2F01%2Fgoogleplus-a-gentle-introduction%2F&amp;counturl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F07%2F01%2Fgoogleplus-a-gentle-introduction%2F&amp;count=none&amp;text=Google%2B%20%26%238211%3B%20A%20gentle%20introduction" scrolling="no" style="border:none;overflow:hidden;width:55px;height:20px"></iframe><![endif]--><!--[if !IE]><!--><iframe class="addtoany_special_service twitter_tweet" src="http://platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F07%2F01%2Fgoogleplus-a-gentle-introduction%2F&amp;counturl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F07%2F01%2Fgoogleplus-a-gentle-introduction%2F&amp;count=none&amp;text=Google%2B%20%26%238211%3B%20A%20gentle%20introduction" scrolling="no" style="border:none;overflow:hidden;width:55px;height:20px"></iframe><!--<![endif]--><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F07%2F01%2Fgoogleplus-a-gentle-introduction%2F&amp;linkname=Google%2B%20%26%238211%3B%20A%20gentle%20introduction" title="Delicious" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F07%2F01%2Fgoogleplus-a-gentle-introduction%2F&amp;linkname=Google%2B%20%26%238211%3B%20A%20gentle%20introduction" title="Reddit" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><!--[if IE]><iframe frameborder="0" allowTransparency="true" class="addtoany_special_service facebook_like" src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F07%2F01%2Fgoogleplus-a-gentle-introduction%2F&amp;layout=button_count&amp;show_faces=false&amp;width=75&amp;action=like&amp;colorscheme=light&amp;height=20&amp;ref=addtoany" scrolling="no" style="border:none;overflow:hidden;width:90px;height:21px"></iframe><![endif]--><!--[if !IE]><!--><iframe class="addtoany_special_service facebook_like" src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F07%2F01%2Fgoogleplus-a-gentle-introduction%2F&amp;layout=button_count&amp;show_faces=false&amp;width=75&amp;action=like&amp;colorscheme=light&amp;height=20&amp;ref=addtoany" scrolling="no" style="border:none;overflow:hidden;width:90px;height:21px"></iframe><!--<![endif]--><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F07%2F01%2Fgoogleplus-a-gentle-introduction%2F&amp;title=Google%2B%20%26%238211%3B%20A%20gentle%20introduction" id="wpa2a_12"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://www.brunton-spall.co.uk/post/2011/07/01/googleplus-a-gentle-introduction/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Google&#8217;s Chrome browser hits 160m users &#8211; but what does it mean for the web?</title>
		<link>http://www.brunton-spall.co.uk/post/2011/06/14/googles-chrome-browser-hits-160m-users-but-what-does-it-mean-for-the-web/</link>
		<comments>http://www.brunton-spall.co.uk/post/2011/06/14/googles-chrome-browser-hits-160m-users-but-what-does-it-mean-for-the-web/#comments</comments>
		<pubDate>Tue, 14 Jun 2011 12:30:38 +0000</pubDate>
		<dc:creator>bruntonspall</dc:creator>
				<category><![CDATA[From the guardian]]></category>
		<category><![CDATA[Article]]></category>
		<category><![CDATA[Blogposts]]></category>
		<category><![CDATA[Chrome]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[Michael Brunton-Spall]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[Technology blog]]></category>

		<guid isPermaLink="false">http://www.brunton-spall.co.uk/?p=566</guid>
		<description><![CDATA[Search giant's browser gets automatically updated, yet there's a hint that it might be shifting towards the territory that made Internet Explorer so divisive]]></description>
			<content:encoded><![CDATA[<p>&nbsp;</p>

<!-- GUARDIAN WATERMARK --><p><a href="http://www.guardian.co.uk/technology/blog/2011/may/11/html5-google"><img class="alignright" src="http://image.guardian.co.uk/sys-images/Guardian/Pix/pictures/2010/03/01/poweredbyguardianBLACK.png" alt="Powered by Guardian.co.uk" width="140" height="45" />This article titled &#8220;Google&#8217;s Chrome browser hits 160m users &#8211; but what does it mean for the web?&#8221; was written by Michael Brunton-Spall, for guardian.co.uk on Wednesday 11th May 2011 19.38 UTC</a></p><p>We have decisively moved from a world of one browser provided by your operating system to a world where people install the browser of their choice. The increasing growth of Firefox usage has risen to the point where 31% of users on the Guardian website use Firefox and only 37% use Microsoft&#8217;s Internet Explorer (split into 22% IE8, 9% IE7 and approx 3% each for IE6 and IE9). </p><p>Google&#8217;s entry into the market has increased usage over time: one year ago at the previous Google IO, there were 70m Chrome users worldwide; today we were told there are over 160m. Unusually for a browser, Google has committed to a forced upgrade process and releases updates every six weeks. </p><p>Why? To combat the problem that more than 50% of Windows computers around the world are still running Windows XP – an operating system that is approaching 10 years old &#8211; and a browser that&#8217;s often the same age.</p><p>Google has been trumpeting its <a href="http://www.google.com/chrome/">Chrome browser</a> achievements at Google IO, showing off what a wonderful platform it is for the web. Over the last year, the team has improved the performance of the browser when running complex web applications by improving the performance of the underlying Javascript engine that web developers use to add logic to a simple web page. Additional improvements in security have made the &#8220;Google web&#8221; a safer place to be &#8211; which can only be good for the web, and for users.</p><p>The biggest announcements they have made is massive improvements to the graphics functionality in your browser. Implementations of the <a href="http://diveintohtml5.org/canvas.html">HTML5 Canvas</a> technology that powers many of the graphical bits of the web are 10 times faster than they were in previous versions, and those improvements don&#8217;t require the developers to upgrade their application. </p><p>This is good news for game developers such as Rovio, who are able to build Angry Birds for the web, and on new builds of the Chrome browser it will run faster.</p><p>If developers want much, <em>much</em> faster graphics, the Chrome browser team has spent time vastly improving the <a href="http://en.wikipedia.org/wiki/WebGL">WebGL graphics library</a> available in Chrome. This is a subsystem of the browser, often called part of the HTML5 specification, but actually a separate specification in its own right, that allows developers to build 3D applications and utilise the high performance graphics hardware available in your machine. </p><p>Google&#8217;s improvements in Chrome means that WebGL applications can run up to 10 times faster than the equivalent Canvas implementation.</p><p>The dark side of all of these improvements is that we are likely to start seeing &#8220;Optimized for Google Chrome&#8221; badges on the web, as many of these features won&#8217;t work across all browsers. That wasn&#8217;t good with Internet Explorer in the bad old days of the Netscape wars, and wouldn&#8217;t be good today. Some things such as Canvas work in most browsers (but not in the most common versions of Internet Explorer), but WebGL&#8217;s implementation is inconsistent across all the browsers. </p><p>Google&#8217;s advancements are welcome for experimental applications, and developers building applications for the Chrome App Store, but their impact on the wider web will probably be negligible, since cross-browser implementation is considered so important by many organisations.</p><div class="gu_advert">
        
          <a rel="nofollow" href="http://oas.guardian.co.uk/RealMedia/ads/click_nx.ads/guardianapis.com/technology/oas.html/@Bottom">
              <img alt="Ads by The Guardian" src="http://oas.guardian.co.uk/RealMedia/ads/adstream_nx.ads/guardianapis.com/technology/oas.html/@Bottom"></img>
          </a>
        
      </div><img src='http://hits.guardian.co.uk/b/ss/guardiangu-api/1/H.20.3/98867?ns=guardian&amp;pageName=Google%27s+Chrome+browser+hits+160m+users+-+but+what+does+it+mean+for+the+web%3F+Article+1557091&amp;ch=Technology&amp;c2=67197&amp;c4=HTML5%2CGoogle+%28Technology%29%2CChrome+%28technology%29%2CTechnology&amp;c3=guardian.co.uk&amp;c6=Michael+Brunton-Spall&amp;c7=11-May-11&amp;c8=1557091&amp;c9=Article' width='1' height='1' /><!-- Guardian Watermark: technology/blog/2011/may/11/html5-google|2012-02-22T22:52:41Z|1e1d6392b91e133e64e95cf9acc2c13c86bffb5a --><p>guardian.co.uk &#169; Guardian News &amp; Media Limited 2010</p> <p>Published via the <a href="http://www.guardian.co.uk/open-platform/news-feed-wordpress-plugin" target="_blank" title="Guardian plugin page">Guardian News Feed</a> <a href="http://wordpress.org/extend/plugins/the-guardian-news-feed/" target="_blank" title="Wordress plugin page">plugin</a> for WordPress.</p><!-- END GUARDIAN WATERMARK -->
]]></content:encoded>
			<wfw:commentRss>http://www.brunton-spall.co.uk/post/2011/06/14/googles-chrome-browser-hits-160m-users-but-what-does-it-mean-for-the-web/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Clearing up some myths about AV</title>
		<link>http://www.brunton-spall.co.uk/post/2011/04/28/av-myths/</link>
		<comments>http://www.brunton-spall.co.uk/post/2011/04/28/av-myths/#comments</comments>
		<pubDate>Thu, 28 Apr 2011 00:10:24 +0000</pubDate>
		<dc:creator>bruntonspall</dc:creator>
				<category><![CDATA[Featured]]></category>
		<category><![CDATA[Rants]]></category>
		<category><![CDATA[No2AV]]></category>
		<category><![CDATA[Politics]]></category>
		<category><![CDATA[Yes2AV]]></category>

		<guid isPermaLink="false">http://www.brunton-spall.co.uk/?p=414</guid>
		<description><![CDATA[I&#8217;ve been thinking about the AV question a lot recently, partly because it&#8217;s happening here and now, partly because it&#8217;s naturally been the talk of the office and partly because...]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been thinking about the AV question a lot recently, partly because it&#8217;s happening here and now, partly because it&#8217;s naturally been the talk of the office and partly because I find the whole area quite fascinating.</p>

<p>People seem to fall into one of 2 categories, either they have a strong opinion, or they aren&#8217;t interested.  Those of a strong opinion appear to refuse to be convinced either way, and those who don&#8217;t really care probably wont vote anyway.  Despite this there has been a lot of media coverage, political talking and leaflets that have tried to convince people one way or the other.</p>

<p>Personally, I&#8217;m all for AV and leaflets like the <a href="http://imgur.com/a/hgmbQ" target="_blank">recent no to av leaflet</a> made me even more convinced to vote yes to AV, but a discussion with one of my brighter friends who is opposed has made me realise that I need more than a gut feeling for why I&#8217;m voting yes.</p>

<p>Firstly, much of what the politicians are saying is complete rubbish, and that&#8217;s sadly from both sides.  Both systems give each person one vote, both have possibilities to be unfair and I&#8217;m not really sure what that word &#8220;fair&#8221; even means for a voting system.</p>

<p>However there are a few things that I think are valid criticisms, and deserve to be addressed
<h2>1. AV will negate tactical voting</h2>
This is one from the pro-AV side, they claim that tactical voting wont happen under AV.  For those who need a reminder, tactical voting is where you want to vote Lib-Dem, but in your area that&#8217;s a &#8220;wasted vote&#8221;, so you vote Conservative to prevent the Labour candidate from getting in.</p>

<p>The claim is that you can validly order your votes 1- Lib-Dem, 2-Conservative, 3-Labour which doesn&#8217;t waste your vote, but if Lib-Dems really have no chance of winning means your vote will be transfered to the Conservative candidate over the Labour one.</p>

<p>The problem is that lets imagine a far more realistic example, say Bennelong, Australia which has 11 candidates from One Nation, Christian Democratic Party, Australian Sex Party, Carers Alliance, Family First, The Climate Sceptics, The Greens, Labor, Building Australia, Liberal and Liberal Democrats.  In 2010, the initial votes varied from 170 to 41,582 depending on the party.</p>

<p>Ignoring the smaller parties, the race was set between Liberal and Labor with 48.53% and 37.12% respectively (the next party had 7.95%).  By the time we reach the 8th preference it&#8217;s a 4 horse race, only Liberal (49.55%), Labor(37.9%), Green(9.17%) and CDP(3.38%) are left in, the CDP is knocked out and it&#8217;s votes transfer about 50% to Liberal, 30% to green and 20% to Labor.  That tips Liberal over the edge and into the 50% margin needed to win.</p>

<p>In this race, tactical voting on behalf of the green party if you support labour is worth it, your vote for green could have given labor the win if you&#8217;d picked them as a higher preference.  I picked this constituency more or less at random (it had a large number of candidates and is early in the alphabet), but in even closer races you can imagine tactical voting still being needed.
<h2>2. It&#8217;s not good enough AV</h2>
This follows on from the above point.  My vague understanding of the british AV proposal is that the voting and reducing stops as soon as someone reaches 50%.  The australian system appears (and I could be wrong here, but the data seems to suggest that this is the case) to continue transfering votes until only 2 parties remain.  In the above case, the data doesn&#8217;t stop at the 9th vote with 3 parties (one over 50%), it runs a 10th vote where the Green party is transfered, 80% (or 7000 votes) goes to Labor, but it&#8217;s not quite enough to beat out the Liberals who win with 53% of the vote.</p>

<p>In this case even if Labor had got 100% of Green votes it wouldn&#8217;t have changed the outcome, but you can imagine states where this might be the case, and running those last rounds could change the result of the election.</p>

<p>However, in those rather rare cases, I believe that first past the post, which is in essence taking the first round of AV and declaring the person with the most the winner, would also suffer from the same problem.
<h2>3. Not everybodies vote will count</h2>
The AV we use is also called Optional Preferential Voting, which means you don&#8217;t have to vote for everybody, you can just say put a single vote for the australian sex party and your vote won&#8217;t be transfered if they get knocked out.</p>

<p>In the australian system, most states use full preferential voting, where you must rank the candidates in order at all times, but Queensland and New South Wales use the same as we are suggesting.</p>

<p>This is actually the best objection I&#8217;ve heard against our form of AV, so I&#8217;ll explain the problem:</p>

<p>If we start with 5 parties, but lets say that everybody voting for Party A is fanatical and wont vote for any other party.  Let&#8217;s also say that party A is 10% of the first round of voting and they get knocked out.  In the next round of voting, the total number of votes counted is 10% less than in round 1, thus getting 50% of that vote does not mean you got 50% of the people who voted in the first round, and therefore the electorate as a whole.</p>

<p>It&#8217;s a pretty good argument, and it&#8217;s the thing that inspired me to go get the <a href="http://results.aec.gov.au/15508/Website/Default.htm" target="_blank">raw data</a> from australia and start researching into this.  Because lets face it, even if some people get knocked out, most races just aren&#8217;t that close that the knocked out ones who didn&#8217;t transfer are that relevant right?</p>

<p>Well for that I needed 2 core pieces of data, the total people in each round and the transfer votes, and I couldn&#8217;t find them (the australian data always seems to have the same total as ending number making me suspect I&#8217;ve got the wrong data source), but <a href="http://blogs.lse.ac.uk/politicsandpolicy/2011/04/07/australia-and-av/" target="_blank">Antony Green for the London School of Economics</a> has crunched the data and the results kind of surprised me.</p>

<p>In essence in Queensland of the 90 odd elections that happen each year, theres normally about 3 &#8211; 6 where the candidate didn&#8217;t have 50% of the original voters when they got their 50% at the end.  That&#8217;s a small enough error margin for me.  However for some reason New South Wales has a rather bad looking trendline, from 1981 through to now, it&#8217;s gone from 1-2 in 100 to around 20 in 100. Thats enough to potentially be significant, but without access to the raw stats, it&#8217;s difficult to calculate how disenfranchised the votes have become and posit any reason why this is happening</p>

<p>The fact remains that while it&#8217;s possible with our slightly limited AV to get results that might not be the true majority, it&#8217;s still in most cases going to produce a representative who has a majority backing to far more of an extent than FPTP will.</p>

<p>&nbsp;</p>

<p>When it boils down to it my reason for Voting Yes to AV is that I believe that change is a necessary part of life, the act of changing will resolve some of these issues in some peoples minds, and will clear out some of the electoral cobwebs.  Sure I&#8217;d rather have full preferential voting AV, or Proportional Representation than the AV we&#8217;re being offered, but I suspect that a No to AV result will be argued successfully that it&#8217;s a no to electoral reform of any sort, and I&#8217;d rather have AV electoral reform than nothing.</p>

<p>References:</p>

<p><a href="http://blogs.lse.ac.uk/politicsandpolicy/2011/04/07/australia-and-av/" target="_blank">Australia and AV</a></p>

<p><a href="http://www.guardian.co.uk/world/audio/2011/apr/12/guardian-focus-podcast-av-referendum" target="_blank">Guardian Focus podcast: The AV Referendum </a></p>

<p><a href="http://results.aec.gov.au/15508/Website/Default.htm" target="_blank">Australian Voting Records and Data</a></p>

<p><a href="http://www.electoral-reform.org.uk/" target="_blank">Campaign for Electoral Reform</a></p>

<p><a href="http://imgur.com/a/hgmbQ" target="_blank">That No to AV leaflet explained</a></p>

<p><a href="http://www.guardian.co.uk/commentisfree/2011/apr/03/observer-editorial-electoral-reform" target="_blank">Do we want a fairer election system? Then the only answer is a Yes vote</a></p>

<p><a href="http://abigq.tumblr.com/post/4987023583/a-graph-titled-where-shall-we-go-to-drink" target="_blank">AV vs FPTP as Beer vs Coffee</a></p>

<p>&nbsp;</p>
<p><a class="a2a_button_instapaper" href="http://www.addtoany.com/add_to/instapaper?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F04%2F28%2Fav-myths%2F&amp;linkname=Clearing%20up%20some%20myths%20about%20AV" title="Instapaper" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/instapaper.png" width="16" height="16" alt="Instapaper"/></a><!--[if IE]><iframe frameborder="0" allowTransparency="true" class="addtoany_special_service twitter_tweet" src="http://platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F04%2F28%2Fav-myths%2F&amp;counturl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F04%2F28%2Fav-myths%2F&amp;count=none&amp;text=Clearing%20up%20some%20myths%20about%20AV" scrolling="no" style="border:none;overflow:hidden;width:55px;height:20px"></iframe><![endif]--><!--[if !IE]><!--><iframe class="addtoany_special_service twitter_tweet" src="http://platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F04%2F28%2Fav-myths%2F&amp;counturl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F04%2F28%2Fav-myths%2F&amp;count=none&amp;text=Clearing%20up%20some%20myths%20about%20AV" scrolling="no" style="border:none;overflow:hidden;width:55px;height:20px"></iframe><!--<![endif]--><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F04%2F28%2Fav-myths%2F&amp;linkname=Clearing%20up%20some%20myths%20about%20AV" title="Delicious" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F04%2F28%2Fav-myths%2F&amp;linkname=Clearing%20up%20some%20myths%20about%20AV" title="Reddit" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><!--[if IE]><iframe frameborder="0" allowTransparency="true" class="addtoany_special_service facebook_like" src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F04%2F28%2Fav-myths%2F&amp;layout=button_count&amp;show_faces=false&amp;width=75&amp;action=like&amp;colorscheme=light&amp;height=20&amp;ref=addtoany" scrolling="no" style="border:none;overflow:hidden;width:90px;height:21px"></iframe><![endif]--><!--[if !IE]><!--><iframe class="addtoany_special_service facebook_like" src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F04%2F28%2Fav-myths%2F&amp;layout=button_count&amp;show_faces=false&amp;width=75&amp;action=like&amp;colorscheme=light&amp;height=20&amp;ref=addtoany" scrolling="no" style="border:none;overflow:hidden;width:90px;height:21px"></iframe><!--<![endif]--><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F04%2F28%2Fav-myths%2F&amp;title=Clearing%20up%20some%20myths%20about%20AV" id="wpa2a_14"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://www.brunton-spall.co.uk/post/2011/04/28/av-myths/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Failure at scale</title>
		<link>http://www.brunton-spall.co.uk/post/2011/02/04/failure-at-scale/</link>
		<comments>http://www.brunton-spall.co.uk/post/2011/02/04/failure-at-scale/#comments</comments>
		<pubDate>Fri, 04 Feb 2011 14:01:16 +0000</pubDate>
		<dc:creator>bruntonspall</dc:creator>
				<category><![CDATA[Featured]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[Rant]]></category>
		<category><![CDATA[Scalability]]></category>
		<category><![CDATA[Web Development]]></category>

		<guid isPermaLink="false">http://www.brunton-spall.co.uk/?p=158</guid>
		<description><![CDATA[When you launch a high profile website, it sometimes will spectacularly fail for reasons of scale.  Since this is an area of professional interest I thought I&#8217;d have a look to see...]]></description>
			<content:encoded><![CDATA[<p><img class="alignnone size-full wp-image-159" title="Police.uk website" src="http://www.brunton-spall.co.uk/wp-content/uploads/2011/02/Dashboard-‹-Michael-Brunton-Spall-—-WordPress.png" alt="Snap of the front page of police.uk" width="588" height="302" />
When you launch a <a href="http://www.police.uk" target="_blank">high profile website</a>, it sometimes will <a href="http://www.guardian.co.uk/uk/2011/feb/01/crime-map-website-crashes" target="_blank">spectacularly fail</a> for reasons of scale.  Since this is an area of professional interest I thought I&#8217;d have a look to see whether there was anything obvious, and it was apparent that the developers didn&#8217;t appear to think at scale (and still haven&#8217;t fixed the issues).</p>

<p>When I visit the crime maps website, the very first thing I see is a landing page that I can type my postcode into.  The HTML generated for this page must be the same for every user, so this page should be cached right?  Unfortunately, not only is this page uncached but it doesn&#8217;t return any cache headers, last modified or expires headers of any form.  The static files for this page do return a Last-Modified header, but I suspect that&#8217;s because they are hosted on S3.  Of slight note is that the developers didn&#8217;t obey any of the website performance rules, there is no gzip and there is multiple CSS/JS files rather than a combined file.</p>

<p>Once I enter a postcode I get redirected to a dynamically generated page based on my post code.  Again there are no cache headers here, and since it&#8217;s a dynamically generated page you might think it doesn&#8217;t matter, but since the page is entirely javascript driven, this HTML is could be cached easily.  There are a few links that have the same query parameter passed to them, but again once you&#8217;ve decided to ban all non-javascript browsers you may as well rewrite those urls in the clients browser using javascript rather than server side.</p>

<p>Furthermore the data that is loaded by the javascript from /crime/radius which contains the crime data for my area, in monthly groups doesn&#8217;t have any new data since 2010-12, not a huge suprise I&#8217;d assume these are quartaly data sets, but that means that json response should also have cache headers, and of course it doesn&#8217;t.</p>

<p>In this case a cache wouldn&#8217;t help with scale for multiple users, but would for returning users, because the developer has specified the area they are interested in by a long/lat that is accurate to 7 decimal places &#8211; an accuracy that should be down to the millimeter of so I believe (4 places gives you accuracy to 6-10 meters or so).  Since they&#8217;ve merely geolocated my postcode, accuracy that close is completely wasted and would destroy any cache that is there.  My postcode is probably a hundred meters or so in size, so having a long/lat that is more accurate than 2 or 3 decimal places is probably wasted.  Decreasing the accuracy of the center point could potentially give you less accurate data, but since what you are interested in is crimes within a 0.5 mile radius, you could simply increase the radius a bit and drop some of the outlying results in the clients browser, making for a much more scalable backend server.</p>

<p>At this point of the analysis I got just to depressed to even continue, it was clearly obvious that the developers had given very little thought as to how this website would scale.  I suspect that since it is hosted on Amazon&#8217;s EC2 they figured they would scale by firing up new servers, but I contend that even at that 18m hits an hour (approx 4k a second) about 4 or 5 caches with good hit rates would have been able to serve most of the traffic without too much issue (I&#8217;d probably have suggested a cache warming for data for the major metropolises as well).</p>

<p>Remember kids, EC2 allows you to add new machines easily, but you still need to think about scale and performance when writing your application.  EC2 doesn&#8217;t solve your scaling problems, it solves your capacity planning problems, you still need to build a site that scales without needed 1000&#8242;s of servers.</p>
<p><a class="a2a_button_instapaper" href="http://www.addtoany.com/add_to/instapaper?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F02%2F04%2Ffailure-at-scale%2F&amp;linkname=Failure%20at%20scale" title="Instapaper" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/instapaper.png" width="16" height="16" alt="Instapaper"/></a><!--[if IE]><iframe frameborder="0" allowTransparency="true" class="addtoany_special_service twitter_tweet" src="http://platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F02%2F04%2Ffailure-at-scale%2F&amp;counturl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F02%2F04%2Ffailure-at-scale%2F&amp;count=none&amp;text=Failure%20at%20scale" scrolling="no" style="border:none;overflow:hidden;width:55px;height:20px"></iframe><![endif]--><!--[if !IE]><!--><iframe class="addtoany_special_service twitter_tweet" src="http://platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F02%2F04%2Ffailure-at-scale%2F&amp;counturl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F02%2F04%2Ffailure-at-scale%2F&amp;count=none&amp;text=Failure%20at%20scale" scrolling="no" style="border:none;overflow:hidden;width:55px;height:20px"></iframe><!--<![endif]--><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F02%2F04%2Ffailure-at-scale%2F&amp;linkname=Failure%20at%20scale" title="Delicious" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F02%2F04%2Ffailure-at-scale%2F&amp;linkname=Failure%20at%20scale" title="Reddit" rel="nofollow" target="_blank"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><!--[if IE]><iframe frameborder="0" allowTransparency="true" class="addtoany_special_service facebook_like" src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F02%2F04%2Ffailure-at-scale%2F&amp;layout=button_count&amp;show_faces=false&amp;width=75&amp;action=like&amp;colorscheme=light&amp;height=20&amp;ref=addtoany" scrolling="no" style="border:none;overflow:hidden;width:90px;height:21px"></iframe><![endif]--><!--[if !IE]><!--><iframe class="addtoany_special_service facebook_like" src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F02%2F04%2Ffailure-at-scale%2F&amp;layout=button_count&amp;show_faces=false&amp;width=75&amp;action=like&amp;colorscheme=light&amp;height=20&amp;ref=addtoany" scrolling="no" style="border:none;overflow:hidden;width:90px;height:21px"></iframe><!--<![endif]--><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fwww.brunton-spall.co.uk%2Fpost%2F2011%2F02%2F04%2Ffailure-at-scale%2F&amp;title=Failure%20at%20scale" id="wpa2a_16"><img src="http://www.brunton-spall.co.uk/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://www.brunton-spall.co.uk/post/2011/02/04/failure-at-scale/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

