Sunday, August 28, 2011

Blogging as Personal Brand Management

A question I'm often asked, or would be if anyone gave a shit, is: "Hey man, why are you blogging?"

There's a talk by Scott Hanselman called "Social Networking for Developers" on Hanselminutes On 9. The gist of the talk, indeed the title of Part 1, is that every developer needs to have a blog. Recently I've come to agree with this more and more. Now, with time on my hands, I've actually started to do something about it.



Yeah but why does a developer need to have a blog? One point made by @shanselman in his talk jumped out at me: "Personal brand management is becoming a fundamental part of being a developer." Personal Brand Management. Sounds a bit heavy, but all it really means is that everything you do online, if it's under your own name, affects how others see you. That's very obvious but worth paying a lot of attention to.

If you don't manage your online identity (I hate the word 'brand'. And 'impact', though only when used as a verb. Not wild about 'engaging' either, while we're on the subject) somebody else might. Somebody you got pissed with on facebook, or some imposter with the same name as you, or it'll be some embarrassing medical stunts forum on which you post far too frequently and didn't realise would surface (verbing, I know) your posts quite so efficiently. You can't get rid of those, but you can at least try and crowd them out by getting accounts on Flickr, YouTube, Google, Twitter, all the usual stuff.

Actually, I recommend Quora too. For a site not that well known to the general public it seems to punch above its weight, SEO-wise. In the first page of results for a vanity search (undertaken purely for research and of course self-gratification) Quora sits alongside such heavy hitters as Facebook, Twitter and Google Profiles. Not a bad return in terms of managing my online identity for a few lousy questions on Entity Framework and RSS.

As an example of feckless online branding, when I signed up to Stack Overflow, the programming Q&A uber-site, I did so as 'Rafe Lavelle', adopting the phonetic version of my name. Somewhere during the last two years, probably around the time they started to incorporate Careers 2.0 with StackOverflow, I wised up and changed my profile to reflect my real name. Stack Overflow is one of those sites where your activity should definitely accrue to your actual name, or your programming handle at least. I don't have one of those but if I did it would be RSSn8Tr. (Actually, because of the way the Stack Overflow URLs are MVCed up, I could give out stackoverflow.com/users/48791/the-one-theyve-been-waiting-for as my profile address and it'd still work. That's because I actually am the 48791.)

If this all sounds a bit Tom Peters (the 'In Search of Excellence' business guru), like developers should turn themselves into brands like Beckham or Trump and launch their own fragrance like Karl Lagerfeld when all most working stiffs are trying to do is fix up some bugs and go home, then all I can say is: you already act as your own brand, and you already do a lot of the work involved in having a blog, or should be doing if you're a dev.

Your tweets, your facebook updates, your Google+ posts, the stuff you say in front of other people in the office, the points you make in emails at work, your questions and answers on developer forums and sites, your podcast recommendations, all that stuff adds up to a certain perception of your quality - or "Qua", as they say in Jerry Maguire - that you may as well manage in a coherent, holistic way. Be your own ambassador of Qua (warning: link contains a nekkid black guy in a shower room).

Anyway, around August 2010 I started blogging at raftus.wordpress.com. That was when I thought I might maintain a separate blog for non-tech stuff, and stick it on Blogger, or Squarespace. That was before I came across one of the hidden gems in @shanselman's canonical list of blogging tips:
"Avoid Split Brain - Pick a Blog and Stay There."
This was timely advice. If I did what I was considering doing, "your two blogs will always be fighting each other on Google, splitting your virtual personality". You don't have to be Larry Page to realise the googley mess that would ensue, so I brought it all home under www.ralphlavelle.net.

It all probably doesn't matter anyway. As Jeff Atwood says, for the first year of your blog no-one will listen or care.

Thanks to Coding Pleasure, whose post 'Developers and blogs' brought that @shanselman video to my attention.

Wednesday, August 24, 2011

It's Alive! Cleaning up broken URLs with MVC Routing

If your website has been around for a while, then you've probably got some dead links out there on the web. You may have changed your site's folder structures a few times, changed technologies from classic asp, to php, to ASP.NET web forms, to MVC. I've had to tackle this problem continuously, since my site Connemara.net, maintained more or less as a hobby at this stage, has been around since 1996.

One of the benefits of using ASP.NET MVC is routing. Although routing is not confined to MVC, located as it is in System.Web.Routing, it is strongly associated with MVC. With ASP.NET Web Forms, URLs generally correspond to files on disk, so for example the address www.connemara.net/words/index.aspx?id=079 meant there was a file called "index.aspx" in the top-level folder called "Words" and it is going to look for something with an id of 079, most likely a record in a table in a database. It's the Pompidou Centre URL pattern, one where the skeleton is on the outside, for everyone to see.

Flickr photo
FlickrPompidou Centre, by Edward Langley. Postmodernist icon, shit URL structure

Routing, on the other hand, places resources front and centre. A resource is anything important enough to have its own address. Having URLs that reflect your resources is part of what's called the Resource-Oriented Architecture in 'RESTful Web Services', which you should read.

So, for example on Connemara.net the first 'Letter from Home' that my friend Eugene wrote for the 'words' section is a resource: it's something you'd make a link to, but one whose original address was http://www.connemara.net/words/letter/no.1.htm. That 'no.1.htm' file has long since stopped being served from that address. Which is a real pity, because ol' Euge wrote some nice stuff back then, and it'd be nice to preserve it.

Google Webmaster Tools Crawl Errors page is where links go to die, so you should buy some flowers and pay your respects from time to time. Your users, if you're lucky enough to have any, tend also to tell you all about your broken links. Connemara.net goes back to Oct. '96, so there's plenty of early defunct ".htm"s, ".html"s, and even ".tmpls" littering the far corners of the web. Then there are what I call "The PHP Years". My first ever web scripting language. Good times. But now the party's over, and it's time to clean up the condoms. That's where MVC routing comes in. How to fix up a link like www.connemara.net/words/article.php?id=075?

I can start by making a route template that contains the literal value "words", and catches anything after that (and the forward slash). So "/words/article.php?id=075" matches, and the 'oldPath' parameter gets the value 'article.php?id=075'. Just map a route like this:
routes.MapRoute("OldWordsArticle",
                "Words/{oldPath}",
                new { controller = "Words", action = "RedirectToArticle" } );

routes.MapRoute("WordsArticle",
                "Words/Articles/{id}/{hyphenatedTitle slug}",
                new { controller = "Words", action = "Article", hyphenatedTitle slug = UrlParameter.Optional } );
That route is what's called greedy. It'll catch any request to the the Words folder as long as it's positioned before the more refined route patterns. Within WordsController, I strip out the id from oldPath ('/article.php?id=123'), look up what that article's new id is, and then reroute the request to a new route, 'WordsArticles'.
public ActionResult RedirectToArticle(string oldPath)
{
    var oldId = ResolveOldId();

    // get the Id of the article to generate the correct URL
    var article = ArticleRepository.Search<Article>(oldId).FirstOrDefault();

    if(article != null)
        return new RedirectToRouteResult("WordsArticle",
                                         new RouteValueDictionary {
                                             { "controller", "Words" },
                                             { "action", "Article" },
                                             { "id", article.Id }
                                             { "slug", article.Slug}
                                         });

    // no article found?
    ViewBag.Message = "No article with an id of " + oldId + " found";
    return View("NotFound");
}
The main point here is that I'm using one route pattern to catch bad old links, and steering them to the correct route. Normally, having matched an incoming request to a route, you then hit a controller method which returns a view, which is a type of ActionResult. But in this case, I return a different type of result, a RedirectToRouteResult, which turns the original route into a recursive one. If you're not careful you could end up in an infinite route black hole and crash the internet.

So now if I browse to www.connemara.net/words/article.php?id=075, the "OldWordsArticle" route catches the request, RedirectToArticle() deals with it and routes it to "WordsArticle" which knows how to serve up a normal ActionResult/View, with a brand, spanking new, RESTy URL of http://www.connemara.net/words/articles/22/michael-gibbons--person-in-profile. "It's Alive! It's Alive!"

EDIT 23/5/2012

Since I wrote this post, Connemara.net has been revamped, courtesy of Noel at Connemara Publications. I removed the links to the old Connemara.net pages that are mentioned, but the thrust of the post is unaffected.

Thursday, August 4, 2011

Aggregating content with Twitter and Google Reader

You can chain Twitter, Google Reader, and Google Buzz to get content into your site in an unholy mashup of REST, ATOM, and WCF. Here's how I do it on my site, Connemara.net.

In an attempt to inject some much-needed currency into Connemara.net, I have decided to emphasize events. The front page will soon be a rolling, blog-like list of what's on in Connemara, in reverse chronological order. Local events shall henceforth be first-class citizens of Connemara.net.

Even though the site has traditionally had plenty of its own content, in keeping with the way it has functioned for the last 7 or so years the information for these events will be aggregated, using Google Reader, from web resources - namely other websites - and also from Twitter. There is so much local event information out there in terms of newspaper articles, local sites' blog entries, individuals' tweets, etc. The challenge is to find it all and organize it so that it becomes useful for people.

Flickr photo
Flickrwelcome to arts week, by Kymberly Janisch

First of all, how do you find out about local events? In my case, in 3 ways:
  1. Somebody associated with the event emails me.
  2. Somebody @ConnemaraNet follows tweets about the event.
  3. Somebody includes it in their site feed, when then appears in my Google Reader 'Connemara' subscriptions list.

In the case of item no.1, what usually happens is that someone writes to me about a local event (usually attaching a Word document and one or more photos) asking me to put it on the site. Jumping into action a week later I create the news item and publish it to an address like www.connemara.net/News/2011/05/Biggest-Names-in-Irish-Sport-Come-to-Clifden. That gives me a URL I can then tweet, so in effect this then becomes an item no.2. Which reduces that list to just two: events I find out about in Reader, or events I find out about in Twitter.

Tweets are a few of my favourite things

So how do I identify tweets or Reader items as being of interest? The first thing to note is that everything has to end up in Reader (so that it can end up in Buzz!) so that means I have to have some way of telling Reader to get event-related tweets. What are the ways you can interact with tweets? You can favourite them. The idea here is to be unobtrusive. If you favourite a tweet, that particular preference is not shown anywhere else other than by browsing to your favourites. Who's going to do that? And anyway, so what if they do? It's perfectly unobtrusive. Then all we have to do is to get a feed of those favourites and stick it in Reader, and we've turned everything into an item no.3.

We got ourselves a Reader

Everything's been funnelled into Reader. The tweets that I've favourite'd are all obviously event-related, but the rest of the subscription items form a heterogeneous list of keyword-related news events, blog entries, and lordy-knows-what, so the actual event-related ones have to be cherry-picked manually. Furthermore, having been identified as an event item, they then have to be marked up with the correct metadata.

Browsing the list of unread items, I share any item that's about a local event, then add my metadata in the form of a comment, e.g. "Events (Name: Clifden Arts Week, Date: 10 Sep 2011 - 20 Sep 2011, Location: Clifden, Url: www.clifdenartsweek.ie)". The business of entering in the metadata is ultimately the least scalable part of the whole operation. But its also the part where my human intervention gives the most value.

In any aggregation process like this, the art lies in finding the boundary between automation and manual intervention. I could automate the process more and have slightly crappier event entries on my site, or I could spend more time on each one and have better entries. In the case of one local event recently The Irish Times headline was "Holiday art auctions in Cork, Connemara". All I want for Connemara.Net/Events is "Art Auction". So I have to enter that metadata or accept the original less direct event title. Also, how could you scrape the dates? The idea, as ever, is to do the most development work up-front in order to do the least amount for each event. The gods of scale must be appeased. But there is a minimum amount of work that has to happen for each event. Sharing an item in Reader allows you to post a comment, and in this case I enter "Event (name:Art Auction, date: 2011 Aug 3, location: Ballynahinch)". If there was an official event url it'd go in there too.



Cross-posting to Buzz

Unfortunately there is no Reader API, so the only way I can ultimately 'read' items that I have shared is to cross-post that item to Google Buzz. There's no work to do here, though. As long as your Buzz account is 'connected' to your Reader account, activity on Reader will create posts in Buzz. And as long as no-one follows you in Buzz, there's no spam. Connemara.net has a twitter account, but is not asking anyone to follow its posts on Buzz (or Reader, for that matter) so that process is effectively unobtrusive.

The REST is easy

At the end of all that, I've got a nice looking Buzz feed, rich in processed event items and ready to bring some order into this chaotic world. This feed is the raw material consumed whenever anyone visits Connemara.net/Events, or even just the front page. Consumption happens by means of the RESTful Buzz API in a process thrillingly similar to the one I've already explained in my earlier post about the sadly-defunct Google Maps Data API. That's one of the selling points of REST: the uniform interface makes the web more programmable.

On my events page, each event shows
  • The name of the event
  • The date(s)
  • The official URL, if there is one
  • Photo(s)
  • The location (name of place, like 'Clifden')
  • A colour-code indicating whether it's currently on, has finished, or is in the future

and I have big plans for:
  • Extra links from news, local sites, mentioning the event
  • YouTube videos
  • Social media content, mainly tweets

Cloud Caveat

One downside of using the cloud as a database like this is that I'm subject to the restrictions imposed by both the Twitter and Buzz APIs, most importantly in terms of how far back into the past I can go. Twitter is particularly parsimonious in this respect: you can only get your 20 most recent favourites using their API. This process that I'm outlining here is suitable either for ephemeral, time-sensitive data like current events, where you don't care about the past, or as a first step before persisting that ephemera once it has been read into your app using SQL Server, for example - but that's for another post.