<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7846496</id><updated>2022-03-07T22:44:24.854+00:00</updated><category term="nosql"/><category term="qcon"/><category term="seo"/><category term="internet"/><category term="development"/><category term="social"/><category term="google"/><category term="biztalk debatch"/><category term="design"/><category term="dtr"/><category term="ecommerce"/><category term="microsoft"/><category term="search"/><category term="security"/><title type='text'>Hugo+</title><subtitle type='html'>Drawing the owl, circles not included.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default?alt=atom&amp;redirect=false'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default?alt=atom&amp;start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>247</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7846496.post-2637844101206420178</id><published>2015-09-10T11:16:00.003+01:00</published><updated>2015-09-10T11:19:28.390+01:00</updated><title type='text'>What I want from an ad network</title><content type='html'>&lt;br /&gt;There’s been a lot of fuss made recently about the inclusion (or possibility of inclusion) of ad-blockers in iOS9, and what that might mean for the web as a whole. On the one hand, no one wants a Flash ad banner that morphs into a dinosaur and then rampages across the screen and up onto the companion skyscraper ad**; on the other hand, ads do pay for most of the content on the web that we, as consumers, get for free. It’s &amp;nbsp;the Faustian pact of the web.&lt;br /&gt;&lt;br /&gt;So, thinking about this, and my responsibility, as a nice chap, to accept ads as the price for free content, I came up with this (totally impractical) solution.&lt;br /&gt;&lt;br /&gt;I would happily accept adverts on the proviso that I could log in to the ad serving network behind the ads, and:&lt;br /&gt;&lt;br /&gt;1. View all of the ads that I have been served&lt;br /&gt;2. View all of the sites I have visited (as recorded by them)&lt;br /&gt;3. Modify what content I want to see (no more car ads)&lt;br /&gt;4. Wipe all of my stored data (even if that was restricted to once per month)&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Is that too much to ask?&lt;br /&gt;&lt;br /&gt;** Of course you do - agencies, you can have this idea for free, no royalties required&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/2637844101206420178/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=2637844101206420178' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/2637844101206420178'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/2637844101206420178'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2015/09/what-i-want-from-ad-network.html' title='What I want from an ad network'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/05414854112203177328</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-2678400620899493885</id><published>2014-08-21T08:24:00.000+01:00</published><updated>2014-08-21T08:24:44.945+01:00</updated><title type='text'>On natural lighting</title><content type='html'>Short post this - I just wanted to share something that I&#39;ve noticed over the past three months that gets very little attention in the office workspace press, which is currently obsessing over whether to sit or stand whilst at work.&lt;br /&gt;&lt;br /&gt;Natural light.&lt;br /&gt;&lt;br /&gt;I&#39;ve started getting in to the office earlier, and I have an east-facing window next to my desk. When I get in to the office, the first thing I used to do was turn on the lights. Now I don&#39;t and I sit at my desk for at least an hour with just the natural daylight coming in. And I can&#39;t tell you how jarring it is when someone from the &#39;west-side&#39; (i.e. dark in the morning) comes in and turns the lights on.&lt;br /&gt;&lt;br /&gt;Daylight is far easier on the eyes, and more relaxing and comfortable than standard office strip lights - and it&#39;s surprising how dark it can be outside whilst continuing to work. Give it a try.</content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/2678400620899493885/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=2678400620899493885' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/2678400620899493885'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/2678400620899493885'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2014/08/on-natural-lighting.html' title='On natural lighting'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/05414854112203177328</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-5736279129684770517</id><published>2014-07-29T17:41:00.003+01:00</published><updated>2014-07-29T17:44:38.105+01:00</updated><title type='text'>Joining the Tour</title><content type='html'>YunoJuno is a very bicycle-friendly company. All three co-founders cycle to work, we&#39;ve done London-Brighton and Oxford-Henley-via-the-Chilterns, and we&#39;re avid followers of Le Tour.&lt;br /&gt;&lt;br /&gt;So much so, in fact, that this time last year Shib (our CEO) announced that in 2014 he was going to head to Paris for the final stage. Fast forward twelve months, and we&#39;ve moved the office over to Paris for the week in order to make this happen. Airbnb sorted us out with a great apartment in the 7th, complete with discordant jazz clarinet coming from the neighbouring flat.&lt;br /&gt;&lt;br /&gt;On the morning before the final we took to the Vélib&#39; (the original city bike share) and cycled much of the final lap - including the full length of the Champs Elysee (up and down) and a circuit around the Arc de Triomphe.&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-YsfEy01nUmQ/U9fOWWzIUfI/AAAAAAAAWRk/hiQfFRBU5lM/s1600/IMG_20140726_112624817_HDR.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-YsfEy01nUmQ/U9fOWWzIUfI/AAAAAAAAWRk/hiQfFRBU5lM/s1600/IMG_20140726_112624817_HDR.jpg&quot; height=&quot;400&quot; width=&quot;223&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Since we had to do a fair bit of research to work out where to watch, I thought I&#39;d share our experience...&lt;br /&gt;&lt;br /&gt;First thing is when to arrive. The timetable for the stage (available online) is pretty accurate, and the first &#39;event&#39; is the arrival of the &#39;caravan&#39; - basically a bunch of honking, hooting, carnival floats that precede the peloton by a couple of hours. We figured we should get to our spot at about the same time they did (so anticipating a two hour wait for the action). This is about right - we found a spot on the river, within the lap (so we got to see the peloton eight times), and the crowds were pretty thin at this time.&lt;br /&gt;&lt;br /&gt;&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-6H66v9YxjKY/U9VVbPm8Z4I/AAAAAAAAWGo/4lffm1zra6w/s1600/DSC_0704.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-6H66v9YxjKY/U9VVbPm8Z4I/AAAAAAAAWGo/4lffm1zra6w/s1600/DSC_0704.JPG&quot; height=&quot;428&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Barely-there crowds a hour before the action&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;Shib wanted to be close to the action, so he took the river side of the road, whilst I stayed on top in the Tuileries gardens, with a view down on to the road.&lt;br /&gt;&lt;br /&gt;And then we waited. And I fell asleep. And then it all kicked off.&lt;br /&gt;&lt;br /&gt;We had a fantastic view - I&#39;ve posted some of my pics on &lt;a href=&quot;https://www.flickr.com/photos/hugorodgerbrown/sets/72157645969039386/&quot; target=&quot;_blank&quot;&gt;Flickr&lt;/a&gt;, and Shib has some spectacular roadside action shots - we had about £4k worth of camera equipment between us, and of course the best shots came from an iPhone.&lt;br /&gt;&lt;br /&gt;&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-88FmUljT4XA/U9VWA_au3hI/AAAAAAAAWKA/eWNgn42a1k0/s1600/DSC_0731.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-88FmUljT4XA/U9VWA_au3hI/AAAAAAAAWKA/eWNgn42a1k0/s1600/DSC_0731.JPG&quot; height=&quot;427&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;The Yellow Jersey on his way to victory&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;And then it all ended. Being a little way from the end we weren&#39;t entirely sure it had happened - you lose count of the laps, and then suddenly you realise they&#39;re not coming round again. The medal ceremony is short and snappy, and an hour later the whole thing&#39;s disappeared. And throughout it all the crowds really never became a problem - someone came and stood by me and had a perfect view and he only turned up halfway through the race.&lt;br /&gt;&lt;br /&gt;Altogether it&#39;s a great experience - definitely to be repeated.&lt;br /&gt;&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/5736279129684770517/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=5736279129684770517' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/5736279129684770517'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/5736279129684770517'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2014/07/joining-tour.html' title='Joining the Tour'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/05414854112203177328</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-YsfEy01nUmQ/U9fOWWzIUfI/AAAAAAAAWRk/hiQfFRBU5lM/s72-c/IMG_20140726_112624817_HDR.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-3320203790454705629</id><published>2014-05-22T17:58:00.004+01:00</published><updated>2014-05-25T14:17:13.428+01:00</updated><title type='text'>The current state of TV</title><content type='html'>A couple of weeks ago I decided I needed to get up to speed with GoT, and set about watching series 1-3, so that I could catch up to the latest series as it played out live.&lt;br /&gt;&lt;br /&gt;This was my experience.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Sign up to NowTV, Sky&#39;s pay-as-you-go monthly streaming service, in order to access the old series.&lt;/li&gt;&lt;li&gt;Watch half of series 1 using Chromecast from Android app - it&#39;s a great experience.&lt;/li&gt;&lt;li&gt;Enjoy it so much I fork out for a NowTV box, so I don&#39;t even need to cast. Brilliant. This is future.&lt;/li&gt;&lt;li&gt;Sky&#39;s licensing window with HBO expires - all episodes disappear from Sky. I am bereft.&lt;/li&gt;&lt;li&gt;Cannot find old GoT on any other streaming service in the UK.&lt;/li&gt;&lt;li&gt;Purchase US VPN to access HBO-Go.&lt;/li&gt;&lt;li&gt;Discover that in the US, you have to sign up via your &quot;TV Provider&quot; - as I live in the UK, my &quot;TV provider&quot; is a metal stick on my roof. It doesn&#39;t support oauth.&lt;/li&gt;&lt;li&gt;Send an SOS out on Twitter for solutions. Nothing comes back.&lt;/li&gt;&lt;li&gt;Give in, and download series 2 via BitTorrent. I&#39;ve already spent about £40 just to watch 6 episodes of first series, so figure I&#39;m owed something.&lt;/li&gt;&lt;li&gt;Wait about four days for torrent to download.&lt;/li&gt;&lt;li&gt;Watch downloads using Videostream chrome app and Chromecast - it too is brilliant, but feel guilty about my crime.&lt;/li&gt;&lt;li&gt;Guilt takes control, eventually finding series 3 available on Blinkbox, from Tesco. This is ironic, given that I spent two years building Tesco&#39;s first streaming service, before it was folded into Blinkbox.&lt;/li&gt;&lt;li&gt;Buy series 3.&lt;/li&gt;&lt;li&gt;Find out that BlinkBox&#39;s Android app doesn&#39;t support casting, so have to watch from laptop. There&#39;s a terrible lag casting from a Silverlight app running in chrome, so I switch to mirroring via AppleTV. The Silverlight app threatens to melt my laptop.&lt;/li&gt;&lt;/ol&gt;So far, I&#39;ve watched (most of) three series of GoT, and along the way:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;I&#39;ve subscribed to two new services (NowTV and VPN).&lt;/li&gt;&lt;li&gt;I&#39;ve bought one new set-top box (NowTV).&lt;/li&gt;&lt;li&gt;I&#39;ve used a further two set-top boxes (Chromecast and AppleTV).&lt;/li&gt;&lt;li&gt;I&#39;ve used two separate device (laptop and tablet)&lt;/li&gt;&lt;li&gt;I&#39;ve downloaded two new apps (NowTV, Blinkbox, Videocast)&lt;/li&gt;&lt;li&gt;I&#39;ve used two further apps (TunnelBlick, uTorrent)&lt;/li&gt;&lt;li&gt;I&#39;ve broken various Terms of Service agreements&lt;/li&gt;&lt;li&gt;I may even have broken the law?&lt;/li&gt;&lt;/ul&gt;This the current state of TV.&lt;br /&gt;&lt;br /&gt;It&#39;s a clusterf*ck.&lt;br /&gt;&lt;br /&gt;**UPDATE**&lt;br /&gt;&lt;br /&gt;Despite my best efforts, I wasn&#39;t fast enough - so although I have now finished series three, and series four is available via my NowTV subscription - the first three episodes have already expired. And because the series is still in progress, it&#39;s not available for streaming anywhere else.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/3320203790454705629/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=3320203790454705629' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/3320203790454705629'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/3320203790454705629'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2014/05/the-current-state-of-tv.html' title='The current state of TV'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/05414854112203177328</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-4216391305787075137</id><published>2013-12-17T09:06:00.001+00:00</published><updated>2013-12-17T09:06:33.492+00:00</updated><title type='text'>How did we end up with this?</title><content type='html'>I, along with several million other people, got caught up in both the LinkedIn and Adobe account hacks, and I&#39;ve had &quot;stop using &#39;password&#39; as my password for every online account&quot; in my to-do list for at least a year.&lt;br /&gt;&lt;br /&gt;A couple of weeks ago I decided to do something about it. I activated two-factor auth on every account that supported it, clicked the &#39;generate temporary password for devices&#39; link on Twitter, and then hit the nuclear button by downloading LastPass and beginning to upgrade my passwords (I don&#39;t really use &#39;password&#39; as my password - I also use &#39;P@ssword&#39;, &#39;Passw0rd!&#39; and &#39;pa55w0rd&#39; - I&#39;m always thinking, see).&lt;br /&gt;&lt;br /&gt;It was a PITA changing old passwords, but I just about got around to changing the important things (accounts attached to credit cards, and core identity providers) when my new phone arrived (Moto-G, which is fantastic, btw, although I am comparing it to a 2.5 yr old HTC).&lt;br /&gt;&lt;br /&gt;What a clusterf*k. Activating two-factor auth and various device checks means that I am constantly being asked to re-enter passwords, only this time I have no idea what those passwords are, as I&#39;ve changed them from the ones I know to the brilliantly uncrackable long, complex, passwords generated by LastPass.&lt;br /&gt;&lt;br /&gt;On the plus side I&#39;ve discovered that Google Keep is a very efficient way of copying complicated passwords in plain text between devices. On the downside, lots of apps don&#39;t support &#39;Paste&#39; on Android, so I have to write them down instead (hence a pocketful of Post-It notes). I think I&#39;m now technically less secure, as I&#39;m far more prone to social engineering hacks that I was before.&lt;br /&gt;&lt;br /&gt;I really hope that those who hold our secrets (no, not the NSA) are working on solutions to the secure login conundrum, because no one outside of the tech industries would use a computer if it was as complicated as the situation I find myself in.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/4216391305787075137/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=4216391305787075137' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/4216391305787075137'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/4216391305787075137'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2013/12/how-did-we-end-up-with-this.html' title='How did we end up with this?'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/05414854112203177328</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-4403456764761383106</id><published>2013-12-13T09:30:00.002+00:00</published><updated>2013-12-13T09:30:34.639+00:00</updated><title type='text'>Marketing reality distortion - a case study.</title><content type='html'>It&#39;s a slow day at work (warming up for the Christmas party), so I thought I&#39;d blog about Accenture instead. Actually, this is a rant about marketing departments, not Accenture per se, they just happen to have riled me this morning.&lt;br /&gt;&lt;br /&gt;A friend of mine works at Accenture, and this morning retweeted an image from their current recruitment marketing campaign. It&#39;s a great pic (see below), and contains a very clear message. They&#39;re looking for Big Thinkers, the ambitious, those who aspire to a Better Future. The message itself is literally spelled out, &quot;Future &amp;gt; Present&quot;, and visually it taps into something many men (possibly a tad too gender specific, but maybe I&#39;m being over-sensitive) will immediately understand: I want to be an astronaut. That&#39;s my dream.&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-u5RxZ0GoHHM/UqrSMineDyI/AAAAAAAAS0I/P6Ozl9UO01U/s1600/Screen+Shot+2013-12-13+at+08.48.56.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;352&quot; src=&quot;http://3.bp.blogspot.com/-u5RxZ0GoHHM/UqrSMineDyI/AAAAAAAAS0I/P6Ozl9UO01U/s640/Screen+Shot+2013-12-13+at+08.48.56.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;It&#39;s a seductive message - and for anyone remotely ambitious just starting out (it&#39;s aimed at people just starting their working lives) it makes Accenture look like somewhere you&#39;d like to work. Maybe not quite as cool as Google (who do genuinely have a &quot;Moonshot&quot; division), but a good option nonetheless. So you click through.&lt;br /&gt;&lt;br /&gt;And that&#39;s when the wheels come off. Apparently the recruitment budget was spent, in its entirety, on the campaign. Having nothing left they got the guy in Accounts&#39; brother to knock up the campaign microsite. He&#39;s jazzed it up with a carousel of incredibly happy people in grey suits (and ties - really, who wears a tie these days) - but I&#39;m pretty sure none of them are &lt;a href=&quot;http://en.wikipedia.org/wiki/Felix_Baumgartner&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Felix Baumgartner&lt;/a&gt; or &lt;a href=&quot;http://en.wikipedia.org/wiki/Chris_Hadfield&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Commander Hadfield&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-OznB-LDLUVE/UqrST2KouXI/AAAAAAAAS0Q/XdOSXeOBn78/s1600/Screen+Shot+2013-12-13+at+08.48.37.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;508&quot; src=&quot;http://2.bp.blogspot.com/-OznB-LDLUVE/UqrST2KouXI/AAAAAAAAS0Q/XdOSXeOBn78/s640/Screen+Shot+2013-12-13+at+08.48.37.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Pretty much everything on this page (font, use of white space, formatting, images) is bad. I imagine it&#39;s just the default Sharepoint &quot;Add new microsite&quot; template, but that&#39;s not really the point. This page, which is where things get real, also has a very clear message: we&#39;re accountants, or possibly management consultants, but either way you&#39;re going to spending a lot of your time in front of a spreadsheet, and it&#39;s not memorising launch code sequences. In addition, we really don&#39;t give a sh*t about design, the process of creation, attention to detail, refinement or finesse. We&#39;re robots. With spreadsheets. And sometimes we hand out awards to other spreadsheet-toting robots.&lt;br /&gt;&lt;br /&gt;In reality I&#39;ve known lots of people who&#39;ve passed through the Accenture machine, and they&#39;re not all number-crunching automatons - they&#39;re generally intelligent, hard-working, interesting people, with a lot to say, on a lot of subjects.&lt;br /&gt;&lt;br /&gt;This is a masterclass of old-school marketing-reality distortion, and rather than making Accenture an attractive place to work, it makes them look desperate (&quot;we couldn&#39;t think what to do, so we just made it up&quot;). There are lots of good reasons to work at Accenture - wanting to be an astronaut isn&#39;t one of them.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/4403456764761383106/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=4403456764761383106' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/4403456764761383106'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/4403456764761383106'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2013/12/marketing-reality-distortion-case-study.html' title='Marketing reality distortion - a case study.'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/05414854112203177328</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-u5RxZ0GoHHM/UqrSMineDyI/AAAAAAAAS0I/P6Ozl9UO01U/s72-c/Screen+Shot+2013-12-13+at+08.48.56.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-8309121701965292727</id><published>2013-08-14T09:05:00.004+01:00</published><updated>2013-08-14T09:05:55.114+01:00</updated><title type='text'>Spies stole my laptop. Possibly.</title><content type='html'>I&#39;ve bought a tin-foil hat.&lt;br /&gt;&lt;br /&gt;I was burgled last month. Nothing dramatic, they broke a window, &amp;nbsp;stole a laptop. They didn&#39;t make any mess (suspiciously neat as it happens, I&#39;d almost claim they vacuumed the place), and it was more of an inconvenience than anything else.&lt;br /&gt;&lt;br /&gt;The police came round and dusted for fingerprints - nothing - no blood where they&#39;d crashed the window, no greasy fingerprints on anything. They may have worn gloves (professional thieves, in Stockwell?)&lt;br /&gt;&lt;br /&gt;And then I realised that a couple of other things were missing - &amp;nbsp;principally two other (old, not used) laptops (resale value &amp;lt; £50), and rather bizarrely, and most annoyingly, my v. expensive Logitech wireless keyboard.&lt;br /&gt;&lt;br /&gt;At about the same time the Snowden story was unfolding, and I was tweeting and posting occasionally on the subject.&lt;br /&gt;&lt;br /&gt;Oh, and I live less than a mile from MI6, the UK&#39;s foreign intelligence service (007).&lt;br /&gt;&lt;br /&gt;So here&#39;s my tin foil hat theory - spies stole my laptops, and more than that, they stole my keyboard as they have backdoor key-logging software installed on popular wireless keyboards. Don&#39;t say I didn&#39;t warn you.&lt;br /&gt;&lt;br /&gt;Over and out - heading off-grid for a while...</content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/8309121701965292727/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=8309121701965292727' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/8309121701965292727'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/8309121701965292727'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2013/08/spies-stole-my-laptop-possibly.html' title='Spies stole my laptop. Possibly.'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-7279846039760433002</id><published>2013-04-03T13:03:00.002+01:00</published><updated>2013-04-04T18:25:04.777+01:00</updated><title type='text'>Status codes are not for humans.</title><content type='html'>Last week I finally managed to provoke our tech lead into using the &#39;F-word&#39; in conversation. The trigger for this outburst was none other than HTTP status codes, and my desire to invent a new one. This was clearly beyond the pale.&lt;br /&gt;&lt;br /&gt;I have since retreated from my original stance (a custom status code), but have committed a change to our dev branch to use an existing, but rarely seen, status code - 422 &quot;Unprocessable Entity&quot; (more on the situation in which I return this later).&lt;br /&gt;&lt;br /&gt;This code is an extension to the base set of &lt;a href=&quot;http://www.iana.org/assignments/http-status-codes/http-status-codes.xml&quot;&gt;codes&lt;/a&gt;, proposed as part of the WebDAV extensions to HTTP 1.1 in June 2007 (&lt;a href=&quot;http://tools.ietf.org/html/rfc4918&quot;&gt;citation&lt;/a&gt;):&lt;br /&gt;&lt;blockquote class=&quot;tr_bq&quot;&gt;&lt;blockquote class=&quot;tr_bq&quot;&gt;The 422 (Unprocessable Entity) status code means the server understands   the content type of the request entity (hence a 415(Unsupported Media Type)   status code is inappropriate),    and the syntax of the request entity is   correct (thus a 400 (Bad Request) status code is inappropriate) but was unable   to process the contained instructions. For example, this error condition may   occur if an XML request body contains well-formed (i.e., syntactically correct),   but semantically erroneous, XML instructions.&lt;/blockquote&gt;&lt;/blockquote&gt;This status code has seen increasing adoption within the HATEOS / REST community, however it&#39;s not without its detractors - as the comment thread with &lt;a href=&quot;http://www.bennadel.com/blog/2434-HTTP-Status-Codes-For-Invalid-Data-400-vs-422.htm&quot;&gt;this post&lt;/a&gt;illustrates.&lt;br /&gt;&lt;br /&gt;My particular crime is not just to use this code, but &lt;strong&gt;to use it in a classic vanilla HTML scenario - in the event of form POST validation errors.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;It has always seemed to me a peculiar anomaly that whilst the PRG pattern and the use of 3xx status codes (albeit typically the &#39;wrong&#39; code, &lt;a href=&quot;http://tools.ietf.org/html/rfc2616#section-10.3.3&quot;&gt;302&lt;/a&gt;, instead of the code specifically designed for this situation - &lt;a href=&quot;http://tools.ietf.org/html/rfc2616#section-10.3.4&quot;&gt;303&lt;/a&gt;) has become ubiquitous, it is perfectly acceptable to return a 200 OK status when the form POST is rejected (because some validation has failed).&lt;br /&gt;&lt;br /&gt;The common argument against this is that form validation is an application specific issue, and therefore not the responsibility of the protocol used to the transmit the data, but I think that&#39;s a thin argument at best. Additional, accepted, status codes exist for &quot;Payment Required (402)&quot;, and &quot;Forbidden (403)&quot;, both of which relate to the underlying application, and not HTTP per se.&lt;br /&gt;&lt;br /&gt;The 2xx code block specifically states:&lt;br /&gt;&lt;blockquote&gt;This class of status code indicates that the client&#39;s request was   successfully received, understood, &lt;strong&gt;and accepted&lt;/strong&gt;.&lt;/blockquote&gt;But what if the request was successfully received, understood, and &lt;strong&gt;not&lt;/strong&gt;accepted. Surely this is a valid use case - in fact I would suggest that it&#39;s one of the canonical HTML/HTTP use cases. And whilst the specific rules around acceptance are application specific, the generic concept of &#39;Request Declined&#39; is ubiquitous - in 15 years of web development I&#39;ve never seen an application that does not support this scenario. And the rules around this are no more application-specific than the &#39;Payment Required&#39; example.&lt;br /&gt;&lt;br /&gt;It is (IMO) unfortunate that people who are looking to support this as a valid 4xx exception are having to shoe-horn this into the 422 WebDAV extension, but my suggestion for a new 421 code (421 on the basis that it&#39;s between 422, which is the closest formal code, and 420, which Twitter appropriated and therefore also made-up) seems to have really upset people, so I&#39;m happy to stand down on that. (&lt;em&gt;And in fact, re-reading 422 now, it is basically what I&#39;m looking for - just would rather it wasn&#39;t within the context of a WebDAV submission. My ideal outcome would be to rewrite the description of 422 in a more generic sense, and have it adopted as the de facto response in the event of a request validation error.&lt;/em&gt;)&lt;br /&gt;&lt;br /&gt;All of which brings me to the real purpose of this post. In researching 422, I have come across endless discussion around the rights and wrongs of HTTP code assignments, but not one post or comment on what, to me, is the main reason for adopting status codes at all (beyond the functioning of the web, of course) - which is to facilitate testing and analytics, i.e. making it easier for computers to understand what is going on without having to read HTML.&lt;br /&gt;&lt;br /&gt;Status codes are transparent and invisible to end users, but stick out like a sore thumb in logs, which makes them invaluable for analysis. A log file that includes nothing but 200s or the occasional 404 provides very little insight in to what is really going on.&lt;br /&gt;&lt;br /&gt;Similarly, having to interrogate the HTML body of a response to understand whether the HTTP request was &quot;received, understood and accepted&quot; or not is painful at best, and often misleading.&lt;br /&gt;HTTP status codes aren&#39;t there for humans, they are there for non-humans,  whether that be the routing infrastructure (understanding what to cache),  or HTTP clients (understanding what to do next). &lt;br /&gt;&lt;br /&gt;And remember, browsers are not the only clients.&lt;br /&gt;&lt;br /&gt;(PS - to everyone who disagrees with me, bear in mind that I do understand your objections, I just don&#39;t agree with them. I&#39;m trying to make the web more useful, not studying for my Masters.)&lt;br /&gt;&lt;br /&gt;[UPDATE]&lt;br /&gt;&lt;br /&gt;I found this in the original HTTP 1.1 specification:&lt;br /&gt;&lt;br /&gt;&lt;blockquote class=&quot;tr_bq&quot;&gt;HTTP status codes are extensible. HTTP applications are not required to understand the meaning of all registered status codes, though such understanding is obviously desirable. However, applications MUST understand the class of any status code, as indicated by the first digit, and treat any unrecognized response as being equivalent to the x00 status code of that class, with the exception that an unrecognized response MUST NOT be cached. For example, if an unrecognized status code of 431 is received by the client, it can safely assume that there was something wrong with its request and treat the response as if it had received a 400 status code. In such cases, user agents SHOULD present to the user the entity returned with the response, since that entity is likely to include human- readable information which will explain the unusual status.&lt;/blockquote&gt;&lt;div&gt;You can make up your own mind from this (I have).&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/7279846039760433002/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=7279846039760433002' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/7279846039760433002'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/7279846039760433002'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2013/04/status-codes-are-not-for-humans.html' title='Status codes are not for humans.'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-861454203631349277</id><published>2013-02-21T11:40:00.001+00:00</published><updated>2013-02-21T11:40:15.687+00:00</updated><title type='text'>Programmers are not interchangeable</title><content type='html'>&lt;span style=&quot;background-color: white; font-family: Arial, sans-serif; font-size: 13px; line-height: 18px;&quot;&gt;&lt;i&gt;[&lt;b&gt;UPDATE&lt;/b&gt;: just found this in my drafts folder from a few months ago - not sure why I never published it at the time, but since YJ is out-and-proud, I thought I should push the button.]&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: white; font-family: Arial, sans-serif; font-size: 13px; line-height: 18px;&quot;&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/span&gt;&lt;b style=&quot;font-family: Arial, sans-serif; font-size: 13px; line-height: 18px;&quot;&gt;This essay by Paul Graham (YC founder) from back in 2007 has been doing the rounds recently (trending on HN), and it struck a chord with me because of the following extract and the new project with which I am involved - YunoJuno - &lt;a href=&quot;http://www.yunojuno.com/&quot;&gt;www.yunojuno.com&lt;/a&gt;.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: white; font-family: Arial, sans-serif; font-size: 13px; line-height: 18px;&quot;&gt;YJ is founded on precisely this belief - that certain &#39;craft skills&#39; (designers, developers, ux etc.) are &lt;/span&gt;&lt;b style=&quot;font-family: Arial, sans-serif; font-size: 13px; line-height: 18px;&quot;&gt;not&lt;/b&gt;&lt;span style=&quot;background-color: white; font-family: Arial, sans-serif; font-size: 13px; line-height: 18px;&quot;&gt; interchangeable, and that the success of a project is very largely dependent on the specific individuals involved.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: white; font-family: Arial, sans-serif; font-size: 13px; line-height: 18px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: white; font-family: Arial, sans-serif; font-size: 13px; line-height: 18px;&quot;&gt;To that end, we want to create exactly the kind of relationship that Paul outlines below - &lt;/span&gt;&lt;i style=&quot;font-family: Arial, sans-serif; font-size: 13px; line-height: 18px;&quot;&gt;&quot;Maybe we could define a new kind of organization that combined the efforts of individuals without requiring them to be interchangeable.&quot;&lt;/i&gt;&lt;span style=&quot;background-color: white; font-family: Arial, sans-serif; font-size: 13px; line-height: 18px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br style=&quot;background-color: white; font-family: Arial, sans-serif; font-size: 13px; line-height: 18px;&quot; /&gt;&lt;span style=&quot;background-color: white; font-family: Arial, sans-serif; font-size: 13px; line-height: 18px;&quot;&gt;It&#39;s a vision we share, and one that I hope YunoJuno will be a part of (specifically the part that looks after the individuals). If you have a skill, and you want to treated as an individual, but to feel part of something greater, then head on over and &#39;Join the Family&#39;.&lt;/span&gt;&lt;br /&gt;&lt;br style=&quot;background-color: white; font-family: Arial, sans-serif; font-size: 13px; line-height: 18px;&quot; /&gt;&lt;span style=&quot;background-color: white; font-family: Arial, sans-serif; font-size: 13px; line-height: 18px;&quot;&gt;&lt;strike&gt;(We&#39;re in beta at the moment, launching in earnest in the new year.&lt;/strike&gt; Not any more - we are open for business.)&lt;/span&gt;&lt;br /&gt;&lt;div style=&quot;background-color: white; font-family: Arial, sans-serif; font-size: 13px; line-height: 18px;&quot;&gt;&lt;blockquote class=&quot;tr_bq&quot;&gt;One of the defining qualities of organizations since there have been such a thing is to treat individuals as interchangeable parts. This works well for more parallelizable tasks, like fighting wars. For most of history a well-drilled army of professional soldiers could be counted on to beat an army of individual warriors, no matter how valorous. But having ideas is not very parallelizable.&amp;nbsp;&lt;/blockquote&gt;&lt;blockquote class=&quot;tr_bq&quot;&gt;And that&#39;s what programs are: ideas.&amp;nbsp;&lt;/blockquote&gt;&lt;blockquote class=&quot;tr_bq&quot;&gt;It&#39;s not merely true that organizations dislike the idea of depending on individual genius, it&#39;s a tautology. It&#39;s part of the definition of an organization not to. Of our current concept of an organization, at least.&amp;nbsp;&lt;/blockquote&gt;&lt;blockquote class=&quot;tr_bq&quot;&gt;Maybe we could define a new kind of organization that combined the efforts of individuals without requiring them to be interchangeable. Arguably a market is such a form of organization, though it may be more accurate to describe a market as a degenerate case—as what you get by default when organization isn&#39;t possible.&amp;nbsp;&lt;/blockquote&gt;&lt;blockquote class=&quot;tr_bq&quot;&gt;Probably the best we&#39;ll do is some kind of hack, like making the programming parts of an organization work differently from the rest. Perhaps the optimal solution is for big companies not even to try to develop ideas in house, but simply to buy them. But regardless of what the solution turns out to be, the first step is to realize there&#39;s a problem. There is a contradiction in the very phrase &quot;software company.&quot; The two words are pulling in opposite directions. Any good programmer in a large organization is going to be at odds with it, because organizations are designed to prevent what programmers strive for.&lt;/blockquote&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/861454203631349277/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=861454203631349277' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/861454203631349277'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/861454203631349277'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2013/02/programmers-are-not-interchangeable.html' title='Programmers are not interchangeable'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-5432097797237713625</id><published>2013-02-21T11:28:00.000+00:00</published><updated>2013-02-21T11:28:10.295+00:00</updated><title type='text'>Desktop ambivalence (in a post-OS world)</title><content type='html'>&lt;br /&gt;&lt;b&gt;There was a time when your choice of desktop OS said something about you - you were a Windows nerd or an Apple hipster - hell, Apple even based an entire advertising campaign on it. That was then. This is now.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;A couple of years ago I was exclusively a Windows person. I wrote software for the .NET platform, which made Visual Studio my tool of choice, and the software I wrote ran on Windows servers. Even if I was working with VMs, I was running Windows Server VMs inside my Windows desktop OS.&lt;br /&gt;&lt;br /&gt;I was always aware of Apple, but as I wasn&#39;t a hipster, and had never opened Photoshop, I left them to one side. When I was forced to use OSX I felt quite strongly that it was inferior to Windows.&lt;br /&gt;&lt;br /&gt;Then I went to work at a creative agency, where I was swimming in an ocean of Mac-luvin. I started using one on the odd occasions when I need to borrow an office laptop. I still didn&#39;t like OSX, but I could get along with it.&lt;br /&gt;&lt;br /&gt;At the same time I started playing around with Python, and I decided to scratch an itch I had around my complete lack of understanding of Linux as an OS. So I installed Ubuntu on a VM, and started playing with that.&lt;br /&gt;&lt;br /&gt;Then I was given an iPad by work. And I bought myself a Windows 7 phone. So now I had Windows 7, Windows Phone 7, iOS, OSX, and Ubuntu in my life. I even installed Windows 8 when it came out (and paid for the upgrade!), but uninstalled it a week later (you can read about that &lt;a href=&quot;http://blog.rodger-brown.com/2012/11/windows-8-first-thoughts.html&quot;&gt;here&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;I finally realised that I had become ambivalent to the OS - and started concentrating on the tools I was using instead. I dropped Visual Studio in favour of Sublime Text, and dropped Word in favour of anything that worked with Markdown (which is anything).&lt;br /&gt;&lt;br /&gt;I still don&#39;t like OSX - the fact that maximising a window doesn&#39;t, you know, maximise it, and the woeful Finder being my pet hates - but I bought myself a MacBook Air as my primary computer simply because I like the hardware, and frankly, I don&#39;t really care about the OS.&lt;br /&gt;&lt;br /&gt;A beautifully designed piece of hardware running beautifully designed, and simple, software (stand up iA Writer), running on an OS that just gets out of my way, is all I need these days. Are we in the post-OS-as-brand world?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Postscript&lt;/b&gt;: the logical conclusion of this is no (discernible) OS at all. Over Christmas I thought I&#39;d be a good son and back up the laptop I bought for my mother a couple of years ago. She uses it infrequently, but enough, and I figured she probably had something of value on it worth saving. In fact there was nothing - at all - on it. Every single thing she does on her computer is web-based. She is the Chrome OS poster child (grandmother).&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/5432097797237713625/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=5432097797237713625' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/5432097797237713625'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/5432097797237713625'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2013/02/desktop-ambivalence-in-post-os-world.html' title='Desktop ambivalence (in a post-OS world)'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-1060003892269620529</id><published>2012-12-01T12:01:00.003+00:00</published><updated>2013-01-24T10:30:05.693+00:00</updated><title type='text'>Testing HTML emails</title><content type='html'>Someone asked me yesterday if I could send the contents of an HTML file as an HTML-formatted email to a client, as they wanted to see what it looked like.&amp;nbsp;Sending HTML emails is usually something that&#39;s either done through an online service (MailChimp et al.) or from the internals of a platform build where you (as a dev) have access to templates and SMTP servers and all that that entails.&lt;br /&gt;&lt;br /&gt;Just sending a file as an HTML-formatted email is surprisingly hard to do (i.e. you can&#39;t type raw HTML into your email client and have it render in the recipient&#39;s inbox.)&lt;br /&gt;&lt;br /&gt;So I put a script together to do it - &lt;a href=&quot;https://gist.github.com/4176850&quot;&gt;https://gist.github.com/4176850&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It requires Python (it&#39;s a Python script), but runs from the command line, thus:&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;div&gt;&lt;code&gt;&lt;code&gt;&lt;br /&gt;&lt;/code&gt;&lt;/code&gt;&lt;/div&gt;&lt;code&gt;$ python send_html_email.py --filename=&#39;test_file.html&#39; --recipient=hugo@example.com&lt;/code&gt;&lt;br /&gt;&lt;div&gt;&lt;span style=&quot;font-family: monospace;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;It runs through Gmail&#39;s SMTP service, so you will need a Gmail account - and you&#39;ll be prompted for your email and password when you run the script.&lt;br /&gt;&lt;br /&gt;Feel free to use, feedback, fork, etc.&lt;br /&gt;&lt;br /&gt;[UPDATE]&lt;br /&gt;&lt;br /&gt;I have recently come across the service mailtrap (mailtrap.io), which is a brilliant companion to this sort of solution. Mailtrap operates an SMTP service that doesn&#39;t go anywhere. Sound crazy, but it&#39;s great - you set up an account, get a set of SMTP credentials, and then everything you send to that server gets parked in the Mailtrap inbox - where you can see the emails received as HTML, plain text and even source. Try it out.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/1060003892269620529/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=1060003892269620529' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/1060003892269620529'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/1060003892269620529'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2012/12/testing-html-emails.html' title='Testing HTML emails'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-4251359058501097410</id><published>2012-11-03T23:53:00.003+00:00</published><updated>2012-11-03T23:53:49.288+00:00</updated><title type='text'>Windows 8 - first thoughts</title><content type='html'>&lt;br /&gt;tl;dr I&#39;ve been through the pain so you don&#39;t have to - please do not install&lt;br /&gt;Windows 8 on a desktop, non-touch, computer - you can thank me later.&lt;br /&gt;&lt;br /&gt;Last weekend I installed (and paid for) a copy of Windows 8 on my home desktop.&lt;br /&gt;I complained about it at the time (display driver issues), but persevered, so I&lt;br /&gt;thought I&#39;d share my experience.&lt;br /&gt;&lt;br /&gt;Before starting, and to give context to my story, here&#39;s my history with MSFT:&lt;br /&gt;for many years I made my living building application with Microsoft technologies.&lt;br /&gt;I&#39;ve created Excel spreadsheets, Access databases, VB6 applications, eMbedded&lt;br /&gt;Visual Basic mobile applications, .NET applications (Windows and Web, from 1.0&lt;br /&gt;through to the current period), and large enterprise implementations of their&lt;br /&gt;core server products; I worked for a year with Redmond on the &#39;Jupiter&#39; project,&lt;br /&gt;which became BizTalk 2004, and earned a living as a BizTalk consultant for some&lt;br /&gt;time after that. I&#39;ve owned mobile Windows products since the early days of&lt;br /&gt;WinCE - I had black and white Cassioepia PDA, and all the terrible phones in&lt;br /&gt;between. I&#39;m not a fanboy, but it&#39;s not far off to say that Microsoft and their&lt;br /&gt;platform have paid for my house, my holidays and most of my belongings. I owe&lt;br /&gt;them.&lt;br /&gt;&lt;br /&gt;Recently I&#39;ve branched out (not entirely of my own volition): I was given an&lt;br /&gt;iPad (1) as a present a couple of years ago, which is great; I bought myself a&lt;br /&gt;Nexus 7, which is better (for me, as reading is my main activity on a tablet).&lt;br /&gt;I have an Android phone (which I hate), but only because I lost my Windows Phone&lt;br /&gt;7 (which I loved). At work I switch between a MacBook Pro laptop (pretty good),&lt;br /&gt;and a desktop&amp;nbsp;which runs both Windows 7 and Ubuntu, which I use for development.&lt;br /&gt;&lt;br /&gt;So I think it&#39;s fair to say that I have a pretty good grasp of the main desktop,&lt;br /&gt;tablet and phone operating systems.&lt;br /&gt;&lt;br /&gt;Which makes my experience with the Windows 8 all the more extraordinary. Quite&lt;br /&gt;simply it&#39;s the worst operating system I&#39;ve yet encountered. And one week after&lt;br /&gt;installing it I have returned to Windows 7, which is the best desktop operating&lt;br /&gt;system to date (IMO - I know Mac-lovers think OSX is great, but really, it&lt;br /&gt;isn&#39;t. Finder, anyone?)&lt;br /&gt;&lt;br /&gt;Windows Phone 7 is fantastic, a real break from the competition, and a bold,&lt;br /&gt;imaginative statement from Redmond. It was clearly created by a team with great&lt;br /&gt;talent, given the freedom to do what they are good at.&amp;nbsp;WP7 is what happens when&lt;br /&gt;you&amp;nbsp;get the right people in the room, and let them do their best work. The problem&lt;br /&gt;is that on the back its critical (if not commercial) acclaim, something&amp;nbsp;incredible&lt;br /&gt;appears to have happened.&lt;br /&gt;&lt;br /&gt;It feels (and I have no inside knowledge on this) as if the WP7 team were fired,&lt;br /&gt;and then their work was handed to a group with less talent, no background on the&lt;br /&gt;project, and simply told to &quot;make Windows look like this.&quot;&lt;br /&gt;&lt;br /&gt;I cannot imagine any circumstances under which the new start screen would seem&lt;br /&gt;appropriate on a desktop, operated by a mouse. Everything about it screams&lt;br /&gt;&quot;touch me&quot;, which is quite annoying on a 22&quot; non-touch screen. On which subject,&lt;br /&gt;ENORMOUS touch-friendly buttons looking f*king ridiculous on a large screen.&lt;br /&gt;The&amp;nbsp;installed apps (Photos, People etc.) look absurd.&lt;br /&gt;&lt;br /&gt;All of which I could forgive, if they hadn&#39;t also messed with the &#39;old-style&#39;&lt;br /&gt;desktop. There is no start button, for which the person responsible should be&lt;br /&gt;fired, and the Metro-style translated to the rich windowed multi-tasking envir-&lt;br /&gt;onment that is Windows, looks like they just forgot to finish the product.&lt;br /&gt;&lt;br /&gt;Windows 8 is a catastrophic downgrade in User Experience, which is a shame as I&lt;br /&gt;believe it covers a bunch of really quite important internal changes. Most&lt;br /&gt;companies use pointless eye-candy to distract from the lack of real features,&lt;br /&gt;but it appears as if MSFT have pulled off a first - drawing attention away from&lt;br /&gt;the good stuff to concentrate on the terrible UI/UX.&lt;br /&gt;&lt;br /&gt;Do not upgrade.&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/4251359058501097410/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=4251359058501097410' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/4251359058501097410'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/4251359058501097410'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2012/11/windows-8-first-thoughts.html' title='Windows 8 - first thoughts'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-5071421801466660934</id><published>2012-10-31T11:10:00.000+00:00</published><updated>2012-10-31T11:10:31.877+00:00</updated><title type='text'>Why Google should get behind Markdown</title><content type='html'>&lt;br /&gt;In the war against complexity, Markdown has emerged as the winner in the&lt;br /&gt;simple-text-formatting battle. There are others - notably Textile (supported&lt;br /&gt;by Basecamp prior to its refresh) and reStructuredText (heavily supported by the&lt;br /&gt;Python community) - but Markdown is the clear leader in its field.&lt;br /&gt;&lt;br /&gt;In recent days there&#39;s been a bit of a hoo-hah about the lack of a formal spec.&lt;br /&gt;for it (@gruber, the BDFL of Markdown doesn&#39;t seem that bothered), and the pro-&lt;br /&gt;liferation of different variants / flavours. For instance, StackOverflow and&lt;br /&gt;GitHub both have subtle extensions that favour their own specific use case.&lt;br /&gt;&lt;br /&gt;I don&#39;t want to get involved in that debate, but now that it has appeared in&lt;br /&gt;the &#39;public&#39; domain, I thought I&#39;d add one idea that occurred to me this&lt;br /&gt;morning (whilst writing and sharing some notes).&lt;br /&gt;&lt;br /&gt;I use iA&#39;s writer for taking notes in meetings, which supports .md, and I use&lt;br /&gt;Sublime as my general purpose writing / coding tool, which also has a great&lt;br /&gt;&#39;preview in browser&#39; function for .md files. What I&#39;m missing however is a good&lt;br /&gt;online tool for a.) making notes online, and b.) sharing those notes as HTML&lt;br /&gt;pages (as per the preview in browser function) with other people.&lt;br /&gt;&lt;br /&gt;At the same time, I use Google Docs for sharing docs online; one of the&lt;br /&gt;ongoing complaints about gdocs is that it&#39;s such a poor relation to Office. So&lt;br /&gt;here&#39;s what I suggest (to Google) - instead of chasing Office and adding&lt;br /&gt;features, how about removing features, and simply becoming the de facto Markdown&lt;br /&gt;editor / browser / publishing platform of choice. Put your weight behind the&lt;br /&gt;specification effort (i.e. write a bunch of formal compliance tests for .md),&lt;br /&gt;then allow people to write in .md in gdocs, and to publish them as HTML for&lt;br /&gt;sharing.&lt;br /&gt;&lt;br /&gt;They already have partial support for *bold* and _italic_ markup on Google+&lt;br /&gt;posts, so they clearly have thought about this.&lt;br /&gt;&lt;br /&gt;(And yes, I know that people who understand Markdown are a tiny niche audience,&lt;br /&gt;but then I&#39;m not expecting anyone from Google to actually read this, so I don&#39;t&lt;br /&gt;need to be practical.)&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/5071421801466660934/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=5071421801466660934' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/5071421801466660934'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/5071421801466660934'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2012/10/why-google-should-get-behind-markdown.html' title='Why Google should get behind Markdown'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-8700784843754559745</id><published>2012-10-22T09:46:00.002+01:00</published><updated>2012-10-22T09:46:13.903+01:00</updated><title type='text'>[draft] Estimation is guesswork.</title><content type='html'>Estimation (in software development circles) is neither an art nor a science - it&#39;s guesswork. And yet we continue to perpetuate the myth that it&#39;s possible to predict how long something will take to build.&lt;br /&gt;&lt;br /&gt;I have a post in me about the &#39;immutablility of developers&#39; and their inability to change the rate at which they work, a fact of life that very few project managers seems to have grasped.&lt;br /&gt;&lt;br /&gt;This is just a placeholder (note to self to write it!)</content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/8700784843754559745/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=8700784843754559745' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/8700784843754559745'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/8700784843754559745'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2012/10/draft-estimation-is-guesswork.html' title='[draft] Estimation is guesswork.'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-2461290872441135561</id><published>2012-09-13T09:29:00.004+01:00</published><updated>2012-09-13T09:30:02.581+01:00</updated><title type='text'>Experiments in rebase.</title><content type='html'>Thought I&#39;d post this as a visual for anyone else getting their head around the difference between rebase and merge.&lt;br /&gt;&lt;br /&gt;I created an project, and then created a series of commits to that project (by &#39;touch&#39;ing a text file), across two branches, &#39;master&#39; and &#39;branch&#39;:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;- [master] touch readme1 &#39;first commit to master&#39;&lt;br /&gt;- [master] touch readme2 &#39;second commit to master&#39;&lt;br /&gt;- git checkout -b branch&lt;br /&gt;- [branch] touch readme3 &#39;first commit to rebase&#39;&lt;br /&gt;- [branch] touch readme4 &#39;second commit to rebase&#39;&lt;br /&gt;- git checkout &#39;master&#39;&lt;br /&gt;- [master] touch readme5 &#39;third commit to master&#39;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;At this point I had two diverged branches.&lt;br /&gt;&lt;br /&gt;Running &lt;code&gt;$ git log --oneline&lt;/code&gt; on [master] i have: &lt;br /&gt;&lt;br /&gt;&lt;code&gt;518f971 third commit to master&lt;br /&gt;4a6d74b second commit to master&lt;br /&gt;0f788ce first commit to master&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;on [branch] I have:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;10e8078 second commit to branch&lt;br /&gt;0122b8b first commit to branch&lt;br /&gt;4a6d74b second commit to master&lt;br /&gt;0f788ce first commit to master&lt;br /&gt;&lt;/code&gt; &lt;br /&gt;&lt;br /&gt;I now copied the entire directory so that I have two versions of the project in exactly the same state, one of which I will rebase, the other I will merge.&lt;br /&gt;&lt;h2&gt;Rebase&lt;/h2&gt;&lt;code&gt;$ git checkout branch&lt;br /&gt;$ git rebase master&lt;br /&gt;&lt;br /&gt;7b9e782 second commit to branch&lt;br /&gt;58f65f3 first commit to branch&lt;br /&gt;518f971 third commit to master&lt;br /&gt;4a6d74b second commit to master&lt;br /&gt;0f788ce first commit to master&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-WA9T6-KDMLk/UFGTOyVFg6I/AAAAAAAADLE/W-vSirGs9HM/s1600/screenshot-40.Png&quot; /&gt;&lt;br /&gt;&lt;br /&gt;At this point, master is behind branch, but can be &#39;fast-forwarded&#39; by merging:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;$ git checkout master&lt;br /&gt;$ git merge branch&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-Cdkou4WdDro/UFGTlbnlaOI/AAAAAAAADLM/nUe9paTGT3o/s1600/screenshot-41.Png&quot; /&gt;&lt;br /&gt;&lt;h2&gt;Merge&lt;/h2&gt;&lt;code&gt;$ git checkout master&lt;br /&gt;$ git merge branch&lt;br /&gt;&lt;br /&gt;a06b559 Merge branch &#39;branch&#39;&amp;nbsp;&lt;/code&gt;&lt;br /&gt;&lt;code&gt;518f971 third commit to master&amp;nbsp;&lt;/code&gt;&lt;br /&gt;&lt;code&gt;10e8078 second commit to branch&amp;nbsp;&lt;/code&gt;&lt;br /&gt;&lt;code&gt;0122b8b first commit to branch&amp;nbsp;&lt;/code&gt;&lt;br /&gt;&lt;code&gt;4a6d74b second commit to master&amp;nbsp;&lt;/code&gt;&lt;br /&gt;&lt;code&gt;0f788ce first commit to master &lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This time you get an extra commit - which is the merge commit itself. This method preserves the exact history - i.e. the fact that development branched, and was then merged back in successfully. If this is important (and I&#39;m definitely not in the &#39;always rebase&#39; camp, then go with merge. Otherwise, rebase will give you a cleaner history. (NB like a supermodel, the better looking the history, the more deceptive the reality. You may prefer to preserve the history of branching.)&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-bmvooUXc6Fo/UFGTC6QJDII/AAAAAAAADK8/9V3uahuGh8w/s1600/screenshot-42.Png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; display: inline !important; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-bmvooUXc6Fo/UFGTC6QJDII/AAAAAAAADK8/9V3uahuGh8w/s1600/screenshot-42.Png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/2461290872441135561/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=2461290872441135561' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/2461290872441135561'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/2461290872441135561'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2012/09/experiments-in-rebase.html' title='Experiments in rebase.'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-WA9T6-KDMLk/UFGTOyVFg6I/AAAAAAAADLE/W-vSirGs9HM/s72-c/screenshot-40.Png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-3343234726804622890</id><published>2012-09-11T18:10:00.000+01:00</published><updated>2012-09-11T18:10:43.215+01:00</updated><title type='text'>GitHub goes down, and everyone just carries on...</title><content type='html'>GitHub has been on &amp;amp; off all day today, and at first one would imagine that that would cause a gigantic PITA for developers, and that productivity in all places &#39;silicon&#39; (valley, fen, glen, roundabout, savannah, desert, glacier,...) would plummet.&lt;br /&gt;&lt;br /&gt;However, since GitHub uses git, and git is a DVCS, whilst users will have lost all the shiny unicorns that GH provides, they &lt;b&gt;could&lt;/b&gt;, if their work really mattered, have simply pushed their repository to another hosted git service - like Codebase, BitBucket, Assembla, ..., and then carried on as normal. A little bit of coordination required to get teammates the new repo URL, and they&#39;d be back online in minutes.&lt;br /&gt;&lt;br /&gt;Or simply continued working locally, and committing, until GH gets back online.  I&#39;ve often done the reverse - I have private repos in BitBucket (free!) which I have periodically pushed to GH in order to take advantage of some of the branch visualisations.&lt;br /&gt;&lt;br /&gt;Try doing that with TFS or SVN. </content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/3343234726804622890/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=3343234726804622890' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/3343234726804622890'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/3343234726804622890'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2012/09/github-goes-down-and-everyone-just.html' title='GitHub goes down, and everyone just carries on...'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-6718949804163042045</id><published>2012-08-09T22:35:00.000+01:00</published><updated>2012-08-09T22:35:34.942+01:00</updated><title type='text'>Django&#39;s extended user profile...</title><content type='html'>Django, as Python&#39;s (arguably) most popular web framework, follows the &#39;batteries included&#39; philosophy of the language with a large number of built-in web features. One of these is the &#39;&lt;span style=&quot;font-family: Courier New, Courier, monospace; font-size: x-small;&quot;&gt;auth&lt;/span&gt;&#39; system, which provides simple user and group authentication and permissions management.&lt;br /&gt;&lt;br /&gt;The core User class is &#39;skeletal&#39; in nature - containing as it does just email, username, first name, last name and password profile fields (as well as some other utility attributes such as &#39;is_staff&#39; etc). This is unlikely to ever satisfy a real-world scenario, and so Django includes a documented mechanism for extending this base user record with your own application-specific extended profile. It&#39;s simple to set up and use - you define a profile model, and then add that model as the&amp;nbsp;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;AUTH_PROFILE_MODULE&lt;/span&gt;&amp;nbsp;setting. Once this is set up, you can simply call the &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;get_profile()&lt;/span&gt;&amp;nbsp;method of any user object, and it will return the profile object.&lt;br /&gt;&lt;br /&gt;Unfortunately, there is one fundamental limitation to this pattern - it is assumed that every user registering on your site has the same profile. So if, for instance, you wanted teachers and pupils, or perhaps suppliers and customers; or buyers and sellers, each with their own profile type, you are out of luck.&lt;br /&gt;&lt;br /&gt;Google this particular issue and you will find a plethora of workarounds and hacks, all of which seem to start with the same underlying assumption - that the core django framework does some kind of magic that cannot be subverted; &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;get_profile()&lt;/span&gt; is sacrosanct.&lt;br /&gt;&lt;br /&gt;One of the joys of using open source software, of course, is that you can open up frameworks and just take a look at how they work. So that&#39;s what I&#39;ve done, and this is what I&#39;ve found...&lt;br /&gt;&lt;br /&gt;When using extended user profiles within django, there are really only two key requirements - first, that when a new user signs up, the user profile is created; and second, that when you call&amp;nbsp;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;get_profile()&lt;/span&gt;&amp;nbsp;on a user, you get the correct profile object back again. Let&#39;s dig into both of those in turn, and consider how we might adapt them to use multiple profile types.&lt;br /&gt;&lt;br /&gt;1. Creating the user profile.&lt;br /&gt;&lt;br /&gt;When creating the extended user profile, the django framework provides precisely no assistance. It is entirely up to the application developer to manage this process. To quote from the docs:&lt;br /&gt;&lt;blockquote class=&quot;tr_bq&quot;&gt;&lt;span style=&quot;background-color: white; font-family: Verdana, sans-serif; line-height: 16.78333282470703px;&quot;&gt;The method&amp;nbsp;&lt;/span&gt;&lt;a class=&quot;reference internal&quot; href=&quot;https://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.models.User.get_profile&quot; style=&quot;background-color: white; color: #ab5603; font-family: Verdana, sans-serif; line-height: 16.78333282470703px; margin-left: 0px; margin-right: 0px; text-decoration: none;&quot; title=&quot;django.contrib.auth.models.User.get_profile&quot;&gt;&lt;tt class=&quot;xref py py-class docutils literal&quot; style=&quot;border-bottom-color: rgb(35, 79, 50); border-bottom-style: dotted; border-bottom-width: 1px; color: #234f32; margin-left: 0px; margin-right: 0px; white-space: nowrap;&quot;&gt;&lt;span class=&quot;pre&quot; style=&quot;margin-left: 0px; margin-right: 0px;&quot;&gt;get_profile()&lt;/span&gt;&lt;/tt&gt;&lt;/a&gt;&lt;span style=&quot;background-color: white; font-family: Verdana, sans-serif; line-height: 16.78333282470703px;&quot;&gt;&amp;nbsp;does not create a profile if one does not exist. You need to register a handler for the User model&#39;s&lt;/span&gt;&lt;a class=&quot;reference internal&quot; href=&quot;https://docs.djangoproject.com/en/dev/ref/signals/#django.db.models.signals.post_save&quot; style=&quot;background-color: white; color: #ab5603; font-family: Verdana, sans-serif; line-height: 16.78333282470703px; margin-left: 0px; margin-right: 0px; text-decoration: none;&quot; title=&quot;django.db.models.signals.post_save&quot;&gt;&lt;tt class=&quot;xref py py-attr docutils literal&quot; style=&quot;border-bottom-color: rgb(35, 79, 50); border-bottom-style: dotted; border-bottom-width: 1px; color: #234f32; margin-left: 0px; margin-right: 0px; white-space: nowrap;&quot;&gt;&lt;span class=&quot;pre&quot; style=&quot;margin-left: 0px; margin-right: 0px;&quot;&gt;django.db.models.signals.post_save&lt;/span&gt;&lt;/tt&gt;&lt;/a&gt;&lt;span style=&quot;background-color: white; font-family: Verdana, sans-serif; line-height: 16.78333282470703px;&quot;&gt;&amp;nbsp;signal and, in the handler, if&amp;nbsp;&lt;/span&gt;&lt;tt class=&quot;docutils literal&quot; style=&quot;background-color: white; color: #234f32; line-height: 16.78333282470703px; margin-left: 0px; margin-right: 0px; white-space: nowrap;&quot;&gt;&lt;span class=&quot;pre&quot; style=&quot;margin-left: 0px; margin-right: 0px;&quot;&gt;created&lt;/span&gt;&lt;/tt&gt;&lt;span style=&quot;background-color: white; font-family: Verdana, sans-serif; line-height: 16.78333282470703px;&quot;&gt;&amp;nbsp;is&amp;nbsp;&lt;/span&gt;&lt;tt class=&quot;xref docutils literal&quot; style=&quot;background-color: white; color: #234f32; line-height: 16.78333282470703px; margin-left: 0px; margin-right: 0px; white-space: nowrap;&quot;&gt;&lt;span class=&quot;pre&quot; style=&quot;margin-left: 0px; margin-right: 0px;&quot;&gt;True&lt;/span&gt;&lt;/tt&gt;&lt;span style=&quot;background-color: white; font-family: Verdana, sans-serif; line-height: 16.78333282470703px;&quot;&gt;, create the associated user profile:&lt;/span&gt;&lt;/blockquote&gt;So, in this case, it makes no difference whether you have one or more user profile classes to use - it&#39;s entirely up to you. You would probably have something similar to this in your views:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;if kwargs[&#39;type&#39;]==&#39;teacher&#39;:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;profile = TeacherProfile(user=request.user, subject=kwargs[&#39;subject&#39;])&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;elif&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;kwargs[&#39;type&#39;]==&#39;student&#39;&lt;/span&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;profile = StudentProfile(user=request.user, year=kwargs[&#39;year&#39;])&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;profile.save()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;2. Retrieving the user profile.&lt;br /&gt;&lt;br /&gt;This is where django, in theory, starts to add value. The framework includes the &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;user.get_profile() &lt;/span&gt;method, which will return the relevant profile object for the user. The problem being that it will only support one, fixed, profile class for all users in the system. This is where the source code helps us:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; def get_profile(self):&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #6aa84f; font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;&quot;&quot;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #6aa84f; font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Returns site-specific profile for this user. Raises&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #6aa84f; font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; SiteProfileNotAvailable if this site does not allow profiles.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #6aa84f; font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;&quot;&quot;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if not hasattr(self, &#39;_profile_cache&#39;):&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; from django.conf import settings&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if not getattr(settings, &#39;AUTH_PROFILE_MODULE&#39;, False):&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; raise SiteProfileNotAvailable(&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;You need to set AUTH_PROFILE_MODULE in your project &#39;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;settings&#39;)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; app_label, model_name = settings.AUTH_PROFILE_MODULE.split(&#39;.&#39;)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; except ValueError:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; raise SiteProfileNotAvailable(&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;app_label and model_name should be separated by a dot in &#39;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;the AUTH_PROFILE_MODULE setting&#39;)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; model = models.get_model(app_label, model_name)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if model is None:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; raise SiteProfileNotAvailable(&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;Unable to load the profile model, check &#39;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;AUTH_PROFILE_MODULE in your project settings&#39;)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self._profile_cache = model._default_manager.using(&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;self._state.db).get(user__id__exact=self.id)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self._profile_cache.user = self&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; except (ImportError, ImproperlyConfigured):&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; raise SiteProfileNotAvailable&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return self._profile_cache&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Observation #1 is that it&#39;s not a very complicated method; we should be able to work out what this does. Let&#39;s remove some of the error checking and reduce it to what we really want:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; def get_profile(self):&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; from django.conf import settings&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; app_label, model_name = settings.AUTH_PROFILE_MODULE.split(&#39;.&#39;)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; model = models.get_model(app_label, model_name)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return model._default_manager.using(&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;self._state.db).get(user__id__exact=self.id)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Now let&#39;s reduce that to pseudocode:&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: blue; font-family: Courier New, Courier, monospace;&quot;&gt;# pseudocode - do not attempt to run!&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;/span&gt;def get_profile(self):&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; determine_model_used_as_profile_form_settings_file()&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; get_model_from_db_using_current_user_id()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Observation #2 is that this is trivial - it does exactly what you would think, and no more. There is nothing to be scared of here. The only problem is that is only expects one value to exist in the settings file, and is fixed on that assumption.&lt;br /&gt;&lt;br /&gt;So, how do we take this knowledge and use it to our advantage. My own personal view is that, having read through the code, you should feel perfectly happy and comfortable to ignore it, and roll your own. It really isn&#39;t doing anything magic. Nothing will break.&lt;br /&gt;&lt;br /&gt;Using our teacher/student app above, you can simply create your own &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;get_profile()&lt;/span&gt; method, and hard-code the &#39;get&#39; code (you can stick the method anywhere you feel comfortable using it - I have a profiles app that defines the models, and have the &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;get_profiles()&lt;/span&gt; method in &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;__init__.py&lt;/span&gt; file):&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: blue; font-family: Courier New, Courier, monospace;&quot;&gt;# pseudocode - do not attempt to run!&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;/span&gt;def get_profile2(user):&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if user is teacher:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;return get_teacher_profile(user_id = user.id)&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; elif user is student:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;return get_student_profile(user_id = user.id)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The obvious problem in the pseudocode above is how to check if the user is a teacher or student, given that there is nothing on the user object that we can use to determine what type the user is. The simplest answer to this, and it is a hack, is to use Groups. By assigning users to relevant groups when they register you can do the following:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; def get_profile2(user):&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if user.groups.filter(name=&#39;teachers&#39;).exists():&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;return get_teacher_profile(user_id = user.id)&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; elif&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;user.groups.filter(name=&#39;students&#39;).exists()&lt;/span&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;return get_student_profile(user_id = user.id)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;This is a workable solution, but it does require hard-coding the groups, which makes it more of a copy-and-paste solution than a pluggable django app or extension.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;The ideal answer is for the core django project to support multiple profile types within a project, by changing the way it currently works. The fix is trivial:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Change the&amp;nbsp;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;AUTH_PROFILE_MODULE&lt;/span&gt;&amp;nbsp;to use a dictionary instead of a string to store a mapping of user types and their profile models&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Add an additional field to the core user model, called something like &#39;user_type&#39; (or, heaven forbid, &#39;profile&#39;)&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Update &lt;/span&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;auth.models.User.get_profile()&lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt; to look up the model based on the user_type / profile attribute and use that instead&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;I have hacked my local copy of django to do just this, and this method works as expected. A fully-integrated, multi-profile, django installation. Of course, this isn&#39;t supportable by any hosting company, so is not a viable short-term solution. In fact it&#39;s only supportable if it gets pulled into the next version of django. I&#39;d love to see this happen, so if you think this is a good idea, please add any feedback below; if enough people want it to happen...&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/6718949804163042045/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=6718949804163042045' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/6718949804163042045'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/6718949804163042045'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2012/08/djangos-extended-user-profile.html' title='Django&#39;s extended user profile...'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-4931788887402950303</id><published>2011-08-24T23:15:00.001+01:00</published><updated>2011-08-24T23:15:19.144+01:00</updated><title type='text'>Our economy is based on The Wealth of Nations, and it’s out of date…</title><content type='html'>&lt;p&gt;This is a post I’ve been meaning to write for some time, and have never got around to as it’s just too big a thing to get straight in my head. I’m writing something now (and it’s little more than the title) just because I think I should lay down a marker. Apologies in advance to anyone who was looking for something more insightful.&lt;/p&gt; &lt;p&gt;The summary is this: &lt;a href=&quot;http://en.wikipedia.org/wiki/The_Wealth_of_Nations&quot;&gt;The Wealth of Nations&lt;/a&gt; by &lt;a href=&quot;http://en.wikipedia.org/wiki/Adam_Smith&quot;&gt;Adam Smith&lt;/a&gt; is the de facto bible of western economics, and its core tenet is the Division of Labour, which he studied through the prism of industrialisation and specifically the manufacture of pins.&lt;/p&gt; &lt;p&gt;Problem is, we’re moving beyond a classic industrial society – possibly to something identified as the Information Age. And in this age, classical economics no longer apply. This is most clearly seen in my own industry – software development, which works in precisely the opposite fashion: productivity increases as work is consolidated into a smaller group of more talented (and expensive) developers. The Division of Labour is not only unproductive, but positively destructive, as anyone who has scrambled through an offshore project can attest.&lt;/p&gt; &lt;p&gt;This paradox undermines many well-meaning corporate initiatives, and until we have a clear, and open, debate on this we are, frankly, a bit f*ked.&lt;/p&gt; &lt;p&gt;More to follow.&lt;/p&gt; &lt;p&gt;&lt;sup&gt;&lt;/sup&gt;&lt;/p&gt; &lt;p&gt;&lt;sup&gt;[1]&lt;/sup&gt; Notwithstanding the “Invisible Hand”, which is the other core message.&lt;/p&gt;  </content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/4931788887402950303/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=4931788887402950303' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/4931788887402950303'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/4931788887402950303'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2011/08/our-economy-is-based-on-wealth-of.html' title='Our economy is based on The Wealth of Nations, and it’s out of date…'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-3954870899696428689</id><published>2011-07-07T20:43:00.001+01:00</published><updated>2011-07-07T20:43:02.861+01:00</updated><title type='text'>Yes, Facebook has lots of users. So what?</title><content type='html'>&lt;p&gt;Another day, another &lt;a href=&quot;http://www.telegraph.co.uk/technology/facebook/8621527/Facebooks-Skype-deal-could-see-off-Google.html&quot;&gt;article&lt;/a&gt; about Facebook&#39;s global takeover. Yes, I know that FB now has 750m members, which is clearly a lot of people. However, the obsession with registered users feels very like the rush for pixels in the digital camera business a few back, or the stampede for ever larger app stores amongst the mobile vendors.&lt;/p&gt;  &lt;p&gt;I don&#39;t care if FB has 1bn members (anecdotal evidence suggests that registrations peaks at apprx. 50% internet users within a country), a camera has 50m pixels or my mobile app store has 1m applications - if they&#39;re not my friends, my favourite apps, or the pictures still come out blurry it&#39;s a problem.&lt;/p&gt;  &lt;p&gt;Google+ is the victim in the latest article - with the Facebook-Skype deal apparently sounding the death-knell for the new Hangouts video-chat service. I don&#39;t think it will kill it, and what is more, I think the niche audience that G+ is rapidly acquiring (albeit fanned by the limited access to invites) is more valuable than the mass of humanity represented by FB*.&lt;/p&gt;  &lt;p&gt;In a similar vein, if I was a camera vendor I would pay a lot more to advertise on Flickr than Facebook (yes, I know Flickr doesn&#39;t have ads, it&#39;s a theoretical point), because people on Flickr care about photos, and people on Facebook don&#39;t. (I&#39;m also sure there are hundreds of amateur photo groups on Facebook - but if they really cared they&#39;d be on Flickr, as it&#39;s a destination defined by the quality of images available.)&lt;/p&gt;  &lt;p&gt;I am sure that it makes sense for all commercial entities to have a presence on Facebook, and to invest in a &amp;quot;social media strategy&amp;quot;, but they are supporting acts; the web is the superset of all properties, and will outlive Facebook.&lt;/p&gt;  &lt;p&gt;* NB This obviously doesn&#39;t apply to the drinks brand WKD - whose natural habitat is clearly Facebook on a Friday night.&lt;/p&gt;  </content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/3954870899696428689/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=3954870899696428689' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/3954870899696428689'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/3954870899696428689'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2011/07/yes-facebook-has-lots-of-users-so-what.html' title='Yes, Facebook has lots of users. So what?'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-5275641620475466136</id><published>2011-06-05T20:33:00.001+01:00</published><updated>2011-06-07T06:38:06.323+01:00</updated><title type='text'>What agile means to me</title><content type='html'>&lt;p&gt;&lt;em&gt;[Update: in response to some of the comments here and on HN, I thought it might be worth updating with a note on my personal experience with Agile, which appears at the bottom of the post.]&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;Agile (with a big &#39;A&#39;) has become so mainstream now that it has started to become the problem. An alarming number of people who espouse the virtues of Agile, and who quote the Agile Manifesto believe that Agile is a project management methodology, and that Agile really means SCRUM, XP, Kanban, and that it is embodied in the daily stand-up, whiteboards or writing requirements on post-it notes. I was once told by an Agile Trainer (LOL) that the correct way to phrase the requirement &amp;quot;we need advertising placeholders on the site, and some way to manage which ads appear where&amp;quot; was &amp;quot;As a User, I wish to be marketed to.&amp;quot; Needless to say his company lost a $m project on the back of such BS.&lt;/p&gt;  &lt;p&gt;As a result of all this, Agile (big &#39;A&#39; again) has become a bit of an albatross - it doesn&#39;t really work, it doesn&#39;t deliver the benefits it promised, and it inevitably involves a lot of arguing amongst the team about who should be doing what, and when.&lt;/p&gt;  &lt;p&gt;All of which distracts from the fact that agility (small &#39;a&#39; this time) is a wonderful thing, that can be achieved, that does provide enormous benefits, and that should be encouraged in everything we (as an industry) do. Agile (small &#39;a&#39;) refers to team dynamics, delivery processes, the software itself (how easy is it to change), and operations. Everyone should aim to be agile.&lt;/p&gt;  &lt;p&gt;So, a few lessons I&#39;ve learned along the (hard) way:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;The best way to achieve your goal is to start with the right people &lt;/li&gt;    &lt;li&gt;If you&#39;ve found the right people, let them get on with the job you&#39;re paying them for &lt;/li&gt;    &lt;li&gt;The people with the best ability to plan the delivery are the people doing the work &lt;/li&gt;    &lt;li&gt;A small team with a manageable backlog needs very little Project Management &lt;/li&gt;    &lt;li&gt;Everyone on the team “needs to know”. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;A few things that help this happen on a practical level:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Try and avoid building towards a pre-determined marketing / launch date &lt;/li&gt;    &lt;li&gt;If you do have a fixed time, then scope is variable &lt;/li&gt;    &lt;li&gt;Never try messing around with team sizes to massage delivery &lt;/li&gt;    &lt;li&gt;Automate everything in sight - builds, deployments, testing, progress reporting, tea-making &lt;/li&gt;    &lt;li&gt;Measure as much as you can - no feedback == no direction &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;And finally some other things that will prevent you from getting there:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;If you have to have everything on your requirements list, you can&#39;t be agile &lt;/li&gt;    &lt;li&gt;If you need to plan beyond the next sprint with any degree of accuracy, you&#39;re not agile &lt;/li&gt;    &lt;li&gt;If you think you can &amp;quot;fix&amp;quot; an iteration by adding more people, you&#39;re not agile &lt;/li&gt;    &lt;li&gt;If you are prepared to change the end date of an iteration to &amp;quot;fit something in&amp;quot;, you&#39;re not agile &lt;/li&gt;    &lt;li&gt;If you have to give a fixed date for delivery - it&#39;s very, very difficult to be agile.      &lt;br /&gt;&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;u&gt;Postscript: my personal experience with Agile.&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;I worked for several years at a company that was at the forefront of the Agile movement in the UK, and I believe that at one point they had more registered SCRUM Master practitioners than anyone else in the country. (I think Sensai Schwaber visited in person at one point, although I’d left by then.) They were generally acknowledged to be the leaders in the field.&lt;/p&gt;  &lt;p&gt;The incident in the post occurred when I returned to the company as a client, parachuted into a large project that they were running with the full SCRUM toolkit (“Agile Coach” included), that was running into trouble, principally the fact that it was &lt;strong&gt;not&lt;/strong&gt; agile. The project was rescued by hiring a team of people with the right combination of aptitude &amp;amp; attitude.&lt;/p&gt;  &lt;p&gt;As I say in the post – agility is a great goal, and everyone should aim for it – but don’t make the mistake of thinking that Agile is the means to achieve it.&lt;/p&gt;  </content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/5275641620475466136/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=5275641620475466136' title='19 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/5275641620475466136'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/5275641620475466136'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2011/06/what-agile-means-to-me.html' title='What agile means to me'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>19</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-6815469857976676894</id><published>2011-06-05T19:59:00.001+01:00</published><updated>2011-06-05T19:59:54.146+01:00</updated><title type='text'>Driving to the airport, agile-style.</title><content type='html'>&lt;p&gt;If you were travelling from A-B, and planning the journey yourself, you might look it up on Google Maps. GM would give you a route, and an estimated time for the journey - let&#39;s say it&#39;s one hour for our mythical trip. Now let&#39;s assume that the destination for our journey is the airport, as we have a plane to catch - i.e. we absolutely have to be there on time.&lt;/p&gt;  &lt;p&gt;In this scenario, would you leave the house one hour before the flight departs (let&#39;s ignore the reality of parking / check-in / security etc. - we&#39;re on the company jet, just driving up to the plane)? Of course not - if you had to be there on time, and you were taking someone else&#39;s directions, even someone with the &#39;authority&#39; of Google, you&#39;d probably add at least 50%, and probably 100% to the estimate. Even then, you&#39;re still subject to the vagaries of the traffic - something that affects everyone, regardless of whether they&#39;ve driven the route before. &lt;/p&gt;  &lt;p&gt;If you were to approach the journey from an agile point of view, you wouldn&#39;t book the airline ticket in advance - you&#39;d catch the plane when you got there. And you wouldn&#39;t plan the route either - you&#39;d hire a driver, who knew the route, and simply tell him the destination. He&#39;d make up the route as he went along, avoiding traffic problems if possible by using his previous experience. It’s pretty clear that if you just set out from A, travelling to B as fast as you could, it would take less time than if mentally started at B, then worked backwards to determine when you ought to leave A. &lt;/p&gt;  &lt;p&gt;Agile behaviour in a software development team requires a similar faith in the ‘driver’ – in this case the people in the team. The quickest way to reach your destination is to have faith in the team to deliver, and to make sure that they understand the destination. If you then insist on them following a specific route, then you have just removed their capacity to avoid delays and dead-ends – removing the reason you hired them in the first place. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;If you want to be agile, you have to trust in your team.&lt;/strong&gt; &lt;/p&gt;  &lt;p&gt;(And yes, this does mean that outsourcing a project cannot be agile. It also means that agile is NOT a project management methodology. It’s a way of working.)&lt;/p&gt;  </content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/6815469857976676894/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=6815469857976676894' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/6815469857976676894'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/6815469857976676894'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2011/06/driving-to-airport-agile-style.html' title='Driving to the airport, agile-style.'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-3433947449393449090</id><published>2011-05-16T00:49:00.000+01:00</published><updated>2011-05-17T17:33:40.375+01:00</updated><title type='text'>Application Lifecycle Management</title><content type='html'>&lt;p&gt;&lt;i&gt;[Update: a couple of people have called me out on this post, and suggested that it would only work from the bottom up if you hire good developers. This is true. This entire blog is based on the philosophy that the cheapest and most efficient way to achieve any given goal in software is to hire a smaller number of better developers. If you have a different philosophy, then you may find this site more helpful &lt;a href=&quot;http://hrb.fm/keHPzD&quot;&gt;http://hrb.fm/keHPzD&lt;/a&gt;.] &lt;/i&gt;&lt;/p&gt;  &lt;p&gt;ALM is described on Wikipedia as:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;“…a continuous process of managing the life of an application through governance, development and maintenance. ALM is the marriage of business management to software engineering made possible by tools that facilitate and integrate requirements management, architecture, coding, testing, tracking, and release management.”&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;The phrase itself is enough to make the blood run cold, but I’ve been struggling recently to justify why I think it’s a Bad Thing, and I think I’ve worked it out: it’s all about the direction in which processes are created and applied. &lt;/p&gt;  &lt;p&gt;ALM comes from the Old World (aka I.T.), where working practices are created by Architects and applied downwards to development teams. It’s a process designed to commoditise development, and to manage human beings out of the equation. If the process is sufficiently detailed and watertight, then developers can become replaceable – which in turn allows you to find cheapers versions.&amp;#160; &lt;/p&gt;  &lt;p&gt;This is lowest common denominator thinking – by creating a process that allows poor developers to work effectively, that same process will prevent good developers from doing what they’re good at. It’s mediocrity by design. Which in I.T. terms is not necessarily a bad thing – ERP, Accounting, Warehouse Management Systems – these are things that are: a.) operated in a controlled environment, b.) used by specially-trained staff, c.) defined by stability, not innovation.&lt;/p&gt;  &lt;p&gt;None of this applies to web application development – which is defined in contrast by: a.) public access, b.) self-service, c.) continuous innovation, and pace of change. In this New World (otherwise known as The Web), companies are defined by the quality of their development staff – they are the asset, and they need be allowed to flourish, and not capped by&amp;#160; process.&lt;/p&gt;  &lt;p&gt;The kicker to this is that process is still a good thing in this New World – but the new processes are designed not to prevent developers from getting things wrong, but to make it easier for them to get things right. ALM should exist in practice, but it should (in fact must) come from the bottom up – the processes are organic and designed by the developers, for the developers. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;There’s no requirement for management to even be aware of these processes – they should manage by outcome, not intervention.&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;NB If this rings any bells with you I would recommend Orson Scott Card’s “software developers as bees” article - &lt;a title=&quot;http://hrb.fm/lIOR0w &quot; href=&quot;http://hrb.fm/lIOR0w &quot;&gt;http://hrb.fm/lIOR0w &lt;/a&gt;&lt;/p&gt;  </content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/3433947449393449090/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=3433947449393449090' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/3433947449393449090'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/3433947449393449090'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2011/05/application-lifecycle-management.html' title='Application Lifecycle Management'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-9139952404513752066</id><published>2011-05-09T14:42:00.001+01:00</published><updated>2011-05-09T14:42:27.718+01:00</updated><title type='text'>Web app or native - looks like HTML is winning the war.</title><content type='html'>&lt;p&gt;When the iPhone launched, one of the major gripes was the lack of support for native apps. Steve Jobs launched the web-apps-look-like-native-apps program, was roundly criticised for the lack of a native SDK, and sure enough it turned out it was already in the pipeline. The App-Age was born.&lt;/p&gt;  &lt;p&gt;(Not long after, Palm released webOS, a web-app platform from inception, and it was roundly praised for its innovative approach.)&lt;/p&gt;  &lt;p&gt;Circle back round a few years, and the plethora of native platforms (iOS, Android, RIM, webOS, WP7) and form factors (phone, tablet) combined with the emergence of HTML5 as a &amp;quot;standard&amp;quot; (albeit one implemented (in)differently by each platform developer) means that web-apps are now back in favour.&lt;/p&gt;  &lt;p&gt;PhoneGap in particular seems to have captured the mindshare of the mobile developer community; it will be interesting to see how this battle plays out over the coming year. Gaming aside, once local storage becomes a common feature, web apps look unstoppable.&lt;/p&gt;  </content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/9139952404513752066/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=9139952404513752066' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/9139952404513752066'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/9139952404513752066'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2011/05/web-app-or-native-looks-like-html-is.html' title='Web app or native - looks like HTML is winning the war.'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-6892944250760840109</id><published>2011-04-14T14:28:00.001+01:00</published><updated>2011-04-14T14:28:28.143+01:00</updated><title type='text'>Ode to Dropbox</title><content type='html'>&lt;p&gt;I’ve been using Dropbox for about 18 months, and before that I was a Mesh fan, and in the dim and distant past I spent many, many hours fighting with Plaxo and ActiveSync (see &lt;a href=&quot;http://blog.rodger-brown.com/2004/10/synchronicity-part-2.html&quot;&gt;here&lt;/a&gt; for a post from 2004 on my frustrations at the time) - so I like to think I know a thing or two about file syncing issues.&lt;/p&gt;  &lt;p&gt;I have a separate work Dropbox account, which for the past couple of months has been so far over its (free) limit that I&#39;ve had to stop using it (there&#39;s only so much you can legitimately clear out!) In the meantime, two things have been happening - I&#39;ve been adding to the local directory, in anticipation of a future upgrade, and other people have been adding content to the remote folders. I had enough faith in Dropbox already to be able to cope with this – I’d get another 3GB of new content coming down, and Dropbox would get a few MB of changes from my local machine. Easy.&lt;/p&gt;  &lt;p&gt;Unfortunately, having upgraded, I discovered that in some overzealous spring-cleaning I had removed critical user profile files, and so the Windows user account associated with the work Dropbox account was corrupt, and I couldn&#39;t login and start Dropbox. This meant that all of my changes were now stuck in a local folder that couldn’t be synced, hidden amongst 2GB of data that had already been synced to the cloud.&lt;/p&gt;  &lt;p&gt;My solution was to create a brand new Windows account and to copy over the entire 2GB archive to this new profile before hitting the sync button - but this meant that instead of adding just my recent changes, I would be syncing the entire 2GB local directory to the new user profile, 99% of which was already on Dropbox, uploaded from a different Dropbox sync. I was convinced this would result in conflict armageddon (and about 48 hours of file transfers – 2GB up, 3GB down, merge, sync, index.)&lt;/p&gt;  &lt;p&gt;I left it running over lunch, came back and discovered... nothing. It had just worked.&lt;/p&gt;  &lt;p&gt;Dropbox is an amazing piece of software, and if they don&#39;t sell up to Microsoft (Windows 8 - Live Mesh replacement), Apple (MobileMe replacement) or Google (Chrome OS file system) for several billion dollars something ain&#39;t right.&lt;/p&gt;  </content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/6892944250760840109/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=6892944250760840109' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/6892944250760840109'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/6892944250760840109'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2011/04/ode-to-dropbox.html' title='Ode to Dropbox'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7846496.post-2330433557847079048</id><published>2011-03-31T23:53:00.001+01:00</published><updated>2011-04-05T21:43:19.385+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="google"/><category scheme="http://www.blogger.com/atom/ns#" term="microsoft"/><title type='text'>Google lives in Perpetual Beta, whilst Microsoft shoots for RTM.</title><content type='html'>&lt;p&gt;Something came up in conversation today that struck a chord with the (my) favourite topic of the moment – why is MSFT not responding to everything going on around it?&lt;/p&gt;  &lt;p&gt;I don’t really know anything at all about MSFT’s business – I’m a .net guy, I’ve been to Redmond, I’ve seen the inside of the machine, but I don’t know all the ins-and-outs. But I do know what they do – and more importantly, how they do it, and that gives me enough to form an opinion.&lt;/p&gt;  &lt;p&gt;They produce software. And the software they produce, even to this day, is distributed on physical disks. That’s right – they burn their software onto shiny plastic disks, which are then distributed around the world (“filling the channel”). And this is their problem.&lt;/p&gt;  &lt;p&gt;Culturally, MSFT is still working in a physical disk world. If you’re burning your software onto 100m disks, you can’t afford to get it wrong, and you can’t easily get it back again once it’s left the building. So they create enormous product development cycles, building towards huge, monolithic, releases. They still call their final releases RTM (Release to Manufacturing). Their development processes are built around this concept, their product development process are built around this concept. Worse still, they have partnerships with disk-pressing plants and disk-selling retail channels. And all of this baggage they haul around with themselves to this day.&lt;/p&gt;  &lt;p&gt;Back in the 90s, the talk was of how MSFT missed the boat, misunderstood the internet, and then woke up. They suddenly Got It. The built a better browser, killed Netscape, and now they owned it. Except that they didn’t. They thought that the internet &lt;strong&gt;was&lt;/strong&gt; the browser – and since they owned the desktop, they could fold in a browser for free and their job was done. Wrong.&lt;/p&gt;  &lt;p&gt;They still don’t get the internet; only 18 months ago I was being told, in Redmond, that whilst the internet was nice, desktop applications were nicer, so could we please put our internet-only Silverlight idea on hold, and just get with the Windows 7 program.&lt;/p&gt;  &lt;p&gt;Google has none of this this heritage – it was born from the internet. As was Facebook. They can innovate and release at internet speed. A Google search results page is a continuously evolving multi-variate test. When they say that they live life in beta – they’re not lying – they really are trying new ideas every single day, live. &lt;strong&gt;Google is never “RTM”.&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;And Microsoft cannot compete – because of its legacy. They have a vault of cash, and some fantastic talent, but until they start to shed some of their physical dead-weight, until they acknowledge what the internet has done to &lt;strong&gt;their&lt;/strong&gt; business (a business they invented), they are heading into retirement. The world they built is innovating away from them, and they’re just too slow to react.&lt;/p&gt;  </content><link rel='replies' type='application/atom+xml' href='http://blog.rodger-brown.com/feeds/2330433557847079048/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7846496&amp;postID=2330433557847079048' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/2330433557847079048'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7846496/posts/default/2330433557847079048'/><link rel='alternate' type='text/html' href='http://blog.rodger-brown.com/2011/03/google-lives-in-perpetual-beta-whilst.html' title='Google lives in Perpetual Beta, whilst Microsoft shoots for RTM.'/><author><name>Hugo Rodger-Brown</name><uri>http://www.blogger.com/profile/12695583613011940337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>