Fear and Loathing on the Learning Curve: Observations on Life, Tech and Web Design from a Slightly Misanthropic Mind

Facebook is Shit

So over the last six months or so I’ve exper­i­enced a sour real­isa­tion. It took me a while to come to terms with it, but I think I can finally sens­ibly express what I’ve dis­covered: Facebook, the increas­ingly omni­present social wünder­kind that I so loved dur­ing my University years is now, in 2012, shit.

This rev­el­a­tion was long in com­ing, I think, thanks in no small part to my nos­tal­gic hangover from those early days. Facebook has given me so many social good­times over the years that I hardly wanted to look the truth in the eye, but now I can finally accept that the incip­i­ent little com­munity that first caught my atten­tion in 2005 has now grown to a filthy, sprawl­ing slum of irrel­ev­ance and bull­shit, and I fear the time has come to leave town.

Facebook in the early years was a dif­fer­ent beast – it had global reach, but its small fea­ture set aimed squarely at the local level – col­lege and uni­ver­sity, with accord­ing bar­ri­ers to entry. It con­nec­ted the user with people in their imme­di­ate envir­on­ment, right down to course and class level, and it all felt emin­ently manageable.

But that was then, as they say, and this is now. The Facebook of today has grown bey­ond a mere forum, pos­i­tion­ing itself as a plat­form for everything, but end­ing up as an impen­et­rable amal­gam of social net­work, cal­en­dar, phone book, photo album, event plan­ner, games con­sole, instant mes­sen­ger, loc­a­tion ser­vice, app plat­form, recom­mend­a­tion engine, mar­ket­place, and – by no means least – ad net­work. And unfor­tu­nately, it doesn’t do many of these very well.

As Facebook has per­meated the main­stream, so too has the expect­a­tion that the parts of our social graph that we care about will fol­low suit; that since we now have access to the recor­ded lives of every­one we’ve ever known in the entire world, we’ll want to keep up with the activ­it­ies of those people every­where, all the time, forever. Coupled with the seem­ingly now com­mon­place assump­tion that our Facebook net­works should be a com­plete ana­log of our real-life social graph, we’re left with a social sys­tem that rap­idly escapes our abil­ity to man­age it.

When I was first at Uni my Facebook net­work con­sisted of fewer than a hun­dred people, all of whom I knew well enough to stop to talk to in the street; out in the real world of 2012, I now have nearly five hun­dred con­tacts, des­pite some prun­ing efforts, and for me at least this has just about des­troyed Facebook’s use­ful­ness as a plat­form for any­thing. To log in to Facebook now is to be assaul­ted by a never-ending stream of irrel­ev­ance and tri­vi­al­ity from the count­less con­tacts who I don’t really know, and many of those I do. Despite numer­ous reor­gan­isa­tions of the News Feed, Facebook seems to have taken the sur­feit of avail­able social data and dis­reg­arded it com­pletely in favour of offer­ing me pro­gress­ively less rel­ev­ant inform­a­tion. The “Top Stories” met­ric is poorly tuned, boost­ing items that have attrac­ted com­ments and Likes from any­one, regard­less of my rela­tion­ship with them. It could at least pri­or­it­ise stor­ies from people I’m obvi­ously more likely to be inter­ested in, like those with whom I’ve inter­ac­ted recently, but it doesn’t.

This sort of activ­ity fire­hose was inter­est­ing and man­age­able back in the day due to the nature of the net­work – a smal­ler graph con­sist­ing mostly of people in my imme­di­ate envir­on­ment whose posts and event invites were, more often than not, rel­ev­ant to my interests – but now with hun­dreds of con­tacts scattered around the globe the Events sys­tem has become a gigantic spam machine-gun, helped in no small part by the ease with which someone can invite every single con­tact to join an event or become a fan of a page. The bull­shit echo cham­ber of my News Feed fills up with near-strangers talk­ing about their job, or the weather, or tele­vi­sion, or stand­ing next to their new car or a horse or their lunch; my inbox fills up with noti­fic­a­tions of invites to events halfway round the world, and I’m increas­ingly minded to ignore everything. With all the com­put­ing power and his­tor­ical social data in the world, Facebook remains unable to show me any­thing I actu­ally give a shit about.

Clearly the solu­tion is to aggress­ively prune my con­tact list until I’m left with only “real” friends, but where does one draw the line? And given all this data, surely it shouldn’t be neces­sary to do this? But it seems it is, because without it, Facebook becomes a ter­ri­fy­ing global memory machine that sub­verts the tra­di­tional decay pro­cess of social con­nec­tions and replaces them with a world where no-one is per­mit­ted to for­get any­one, instead being con­stantly bom­barded with minu­tiae about them. To derive any non-trivial value from it requires con­stant shit­work to main­tain friend lists, adjust pri­vacy set­tings and con­tent pref­er­ences, time argu­ably bet­ter spent actu­ally going out­side and see­ing people. And prun­ing those con­tact lists is tough, because every­one assumes your Facebook graph should mir­ror your meat­space one, and it’s tricky to get away with de-friending someone you might bump into again just to get their guff off your timeline.

Speaking of ter­ri­fy­ing memory machines, the Timeline pro­file view is a par­tic­u­lar exas­per­a­tion that while optional now will soon no doubt be stand­ard for all, with its grubby abil­ity to instantly tele­port back to any user’s first tent­at­ive shares and embar­rass­ingly youth­ful pho­tos. I know of more than one per­son who has deleted a large chunk of their early pho­tos and data in response to this. With Facebook’s insist­ence on redesign­ing its UI every few months, the lack of con­trol over this chan­ging present­a­tion of what is after all our data is pretty irrit­at­ing. In the real-life friend­ship model we focus on the present with a few memor­ies skimmed off the top of his­tory, but no more. Now our drunk pho­tos from forever are just a flick of the scroll­bar away.

A lot has been writ­ten about Facebook’s data col­lec­tion activ­it­ies and while that isn’t such a major con­cern for me, it prob­ably should be. Facebook’s huge valu­ation at their recent IPO under­lines the import­ance of their lever­aging the moun­tains of social data they retain, and we should be under no illu­sions as to their inten­tions. When the dimen­sions of data they have access to are com­bined, we reach the sort of level of intel­li­gence gov­ern­ments get very excited about, and here we are giv­ing all this inform­a­tion away for free.

So why not just leave alto­gether? That’s the million-dollar ques­tion, and the one I find most frus­trat­ing. Given Facebook’s per­vas­ive­ness, I feel like I have to retain at least a token pres­ence for fear of miss­ing out on key things. It’s mostly events, I think – since nobody seems to pro­mote those by any other means any­more – but worse, more and more sites and apps now use Facebook Connect as their sole authen­tic­a­tion sys­tem, mak­ing it even more pain­ful to avoid hav­ing a Facebook account.

The trick, then, must be to trim those con­tacts back to the bare min­imum, get rid of any sur­plus data that I don’t feel like keep­ing around, and try not to worry. Facebook will be around whether I’m on it or not, so I might as well try to sal­vage some value from it without being driven com­pletely mad. Just don’t expect me to return your pokes or Like that photo of your feet.

Thanks to Tim Anderson for read­ing drafts of this post.

Your first Heroku Django app

I few weeks ago I decided to rewrite an old PHP site in Python + Django, and settled on host­ing it on Heroku, as I didn’t have time to go through the rig­mar­ole of set­ting up my own VPS just to demo the site. Heroku is a fant­astic plat­form for app host­ing but there are a few gotchas that I ran into on the way which I thought I’d share.

A quick over­view of the plat­form, then, in case you’re not famil­iar with it – Heroku is a cloud host­ing ser­vice for applic­a­tions, rather than serv­ers, and sup­ports sev­eral soft­ware stacks, Python among the new­est added. It plugs into your git work­flow so you can deploy with a simple git push, and your app gets com­piled down into a runtime known as a “slug” which Heroku serves from its net­work of app serv­ers. Nearly all configuration/management of your app is done from your local com­mand line via the herokutool­belt” app. The pri­cing struc­ture is such that you can run a simple app with a small data­base for free.

Your applic­a­tion slug is read-only, and each of Heroku’s web server pro­cesses (known as “dynos”) runs in its own isol­ated envir­on­ment so your app itself must be state­less – you can only per­sist stuff to data­base, any­thing else (e.g. uploaded media files) must be writ­ten to and served from another envir­on­ment, such as S3. You can serve static files (e.g. JS, CSS, images) from your app, but you won’t be able to change them without doing a new deployment.

The first issue I encountered: Heroku detects your app’s soft­ware stack by look­ing for sev­eral com­mon files, and in the case of Django apps it’s look­ing for settings.py. However in my Django pro­ject struc­ture I like to main­tain sep­ar­ate con­figs for com­mon, local and pro­duc­tion in sep­ar­ate dir­ect­or­ies, so I had to expli­citly tell Heroku where to find my set­tings by set­ting the DJANGO_SETTINGS_MODULE envir­on­ment vari­able thusly: heroku config:add DJANGO_SETTINGS_MODULE=myapp.config.heroku.settings (repla­cing myapp with your app name).

Heroku expects your Python app to use virtualenv, and also pip to install and man­age packages.

I found that in order to run my app scripts on the remote side I also needed to update my Python path, which requires set­ting another envir­on­ment vari­able: heroku config:add PYTHONPATH=/app:/app/myapp

Next I needed to set up what Heroku calls the “shared data­base” addon, which is basic­ally a 5MB free PostgreSQL instance that your app can have access to. It took me a while to fig­ure out that this isn’t provided by default, you have to enable it by run­ning heroku addons:add shared-database . Heroku addons provide their access details via envir­on­ment vari­ables, so once you’ve enabled the shared data­base you’ll find (via heroku config, which lists them) that you’ve got a new vari­able called SHARED_DATABASE_URL con­tain­ing the URL and login details for your app’s PostgreSQL db (you also need psycopg2 installed, via pip install psycopg2).

Because Heroku’s stack detec­tion magic didn’t like my app struc­ture, I found I also needed to manu­ally add some bits to my set­tings file to intro­spect the envir­on­ment and set up the data­base con­nec­tion based on the SHARED_DATABASE_URL var. The code is here (at the bot­tom). A normally-structured Django app will have this code auto­mat­ic­ally added dur­ing deploy­ment, but mine didn’t.

I also had to manu­ally tell South (the migra­tion lib­rary I was using) to use the PostgreSQL backend, by adding the fol­low­ing to my pro­duc­tion set­tings file: SOUTH_DATABASE_ADAPTERS = { 'default': "south.db.postgresql_psycopg2" } .

The final big obstacle came when deploy­ing – I run collectstatic on my Django apps to gather all my static files together into one loc­a­tion, and this wasn’t work­ing on Heroku. It turns out that the collectstatic com­mand runs in its own vir­tual envir­on­ment, sep­ar­ate from the one in which the web server dyno runs, so the lat­ter never has access to the col­lec­ted files (the applic­a­tion slug is read-only, remem­ber?). I found this help­ful link on the sub­ject, which explains how to com­bine the collectstatic and guni­corn start com­mands into one in your Procfile (which tells Heroku how to run your app) so they share an envir­on­ment. Check the com­ments on that post for details on how to adjust your urls.py file to prop­erly point to the static files too.

Worth not­ing that in that pre­vi­ous link, the Procfile com­mand to start guni­corn refers to a $PORT var – this is present in the web server dyno envir­on­ment: you have to bind to the port they spe­cify, or noth­ing will work – but this is pretty much handled for you.

To get media files work­ing prop­erly and serving from S3, I used django-mediasync to copy my local media files to my S3 bucket (one time only), then used these set­tings in my pro­duc­tion settings.py to hook it all up, via django-storages and boto. Note the last line, which instructs easy_thumbnails (if you’re using it) to use the same stor­age engine as the app default. Without this, easy_thumbnails won’t be able to save gen­er­ated thumb­nails to S3. Took me a while to dia­gnose that one.

A couple of other bits: if you want to send email from your app, you’ll need to use your own server or use the free Sendgrid addon which allows up to 200 mes­sages a day, bey­ond which you’ll need to hand over some cash money. Heroku don’t provide out­go­ing email facil­it­ies themselves.

Lastly, it’s worth not­ing that Heroku’s archi­tec­ture will auto­mat­ic­ally put a run­ning app into a sleep state if it’s not accessed for a while. This can have the effect of mak­ing the first request in a while quite slow. I got around this by set­ting up a free Pingdom account to ping my app every few minutes, which keeps it alive.

Hope this sheds a little light on some of the less-documented aspects of run­ning Python/Django apps on Heroku. It’s a great plat­form and I found get­ting to grips with it to be a really use­ful learn­ing exper­i­ence. Good luck!

3

On Certifications

I’m in the pro­cess of chan­ging jobs at the moment, and have been struck by the dif­fi­culties inher­ent in try­ing to quantify exper­i­ence and skill in people mov­ing between com­pan­ies in the web devel­op­ment sector.

Continued →

Posted May 26th, 2011

The Bloody Apprentice, S07E01

My view­ing of the first epis­ode of the new Apprentice series was con­duc­ted under an assump­tion, rap­idly for­mu­lated dur­ing the first ninety seconds of the show, that these people can­not pos­sibly be real; that such a con­cen­tra­tion of  frantic­ally self-aggrandising, mas­turb­at­ory gawpers can­not pos­sibly exist — out­side of fic­tion — in the space of one board­room without the entire city of London dis­ap­pear­ing up its own back­side in an enorm­ous implo­sion of bull­shit. Once I estab­lished that premise, the show imme­di­ately became sev­eral times more watchable.

Be ye warned: this post con­tains spoil­ers. By which I mean I will talk about which use­less shaft got fired last night.

Continued →

2

Book Review: “Designing with Web Standards” (1st Edition)

I’ve been mak­ing a con­scious effort to read more “industry lit­er­at­ure” lately, in as much as I’ve star­ted seek­ing out books on web/software devel­op­ment and design that I feel, as a sup­posed pro, I ought to have read (pos­sibly some time ago).

This endeav­our even­tu­ally brought me to Jeffrey Zeldman’s Designing with Web Standards, which I’d never actu­ally read des­pite it being revered as the book for any­one remotely ser­i­ous about web design. Feeling nought but dust in my pock­ets, I hunted out a second-hand copy on the Amazon Marketplace, which I found for the princely sum of £0.01 plus post­age. How do they do it?

Continued →

Posted April 5th, 2011

Introducing tweetvaultHQ

So I thought it was about time I wrote about my latest pro­ject: tweetvaultHQ. It launched a couple of weeks ago, and now things have settled down I’ve got time to write a bit about it and what the launch was like.

tweetvaultHQ is a hos­ted Twitter archiv­ing ser­vice. Some time last year I got tired of not being able to effect­ively search my own tweet his­tory (Twitter’s global search only goes back a few days, and there’s no per-profile search — you have to just keep paging through the timeline), and I thought it would be handy to have my own saved copy of my tweets. I’d also been doing quite a bit with the Twitter API at work, so I figured I’d build something.

Continued →

Posted March 27th, 2011

You can find a complete history of older posts in the Archive.