Posterous theme by Cory Watilo

Trouble installing Rubygems with C extensions on Mac OS X Lion (with Xcode 4.2.1)

If you're having trouble installing Rubygems with native C extensions on Mac OS X Lion, it's because the default C compiler on 10.7 (as part of Xcode 4.2.1) is llvm-gcc-4.2 - which unfortunately does not work with many gems. You can confirm this by running

ls -la /usr/bin/gcc # it will return /usr/bin/llvm-gcc-4.2

The solution is to install the regular GCC compiler, you can download it from here: https://github.com/kennethreitz/osx-gcc-installer

Once you do this, you need to set the CC compiler in your ~/.rvmrc (I assume you're using RVM right? Why aren't you?)

Add this line to your ~/.rvmrc file:

export CC=/usr/bin/gcc-4.2

This is detailed in the Phusion Blog (thanks guys)

Creating multiple ActiveRecord objects within a Rails controller (and rolling back)

Assume you're creating two ActiveRecord objects in a Rails controller, the child object belonging to a has_one relationship to the parent. Assume you need the parent object to exist before the child can be created. If the creation of the child object fails (due to a failure in valdiations for e.g.), how do you make sure that the first one does not persist, and is also deleted?

Enter ActiveRecord transactions.

As can be seen from the gist above, if the 'child' object is not valid, raise an ActiveRecord::Rollback error within the transaction, and ActiveRecord will rollback all 'commits' within the transaction and you won't be left with a dangling parent object.

Nifty.

My Screwup

So it's been a slightly rough week ('rough' is all relative, but I digress).

I've been reading a lot about user engagement, persuasive design and how engagement is the key for the survival of your web application. If people don't come back to your site, then why does it exist?

As we try to overcome with this challege, I decided (against the better judgement of my colleague Kent and my wife Sneha), that I should opt-in a lot of our users into a daily digest email.

This turned out to be a bad idea (for some people like my wife it sounded bad right from the beginning) - and I realize that now. I want to say sorry to the few people who were very upset by this; swore at us and hoped that we were inflicted with varying degrees of hurt ;)

I want to assure you that what I'm about to say is not an excuse for what I did, but just a short wade into the back story of Denso - just so you can better understand the reasoning behind this.

Hold On - Are you stupid - how does this sound like a good idea at all?

Let me explain. Denso started off its life as a pure Instapaper-like service. I personally needed a way to bookmark long lectures, talks and sometimes short funny videos (to keep for posterity - you never know if you'll find it again). We bootstrapped a working version over a long Saturday during SHDH.sg (thanks guys for giving us the opportunity to do so) and the overwhelming response during the event was - "OMG I need this".

We even went on to win "Crowd-Favorite" (although that probably had to do with the fact that we demo'ed with a Maru video). We quickly polished up the website, bought a domain - Denso means transmission in Japanese - built an invite-only system and we were off to the races.

We invited a few of our close friends (and later many others) and the feedback was positive - I can safely say that it was the most well-received of all the tiny web apps that I've been a part of.

But something happened

We realized that people weren't bookmarking videos that often. We kept a close eye on stats such as when and how often people bookmarked videos - and it wasn't very encouraging. This is what they mean when they say "look at what users do, not at what they say". Granted it was a small-ish sample set, but we didn't want to drag our feet.

The Pivot

This is when we realized that people needed a way to lazily consume video. It was also at this time that we read Fred Wilson's 1/10/90 rule (he blogged about it again yesterday), i.e. 1% will create the content, 10% will curate the content, and the other 90% will simply consume it. We needed a way for people to use Denso without being a content creator, thereby increase the number of entrypoints to the app. Not everyone wants to be the ones bookmarking videos - in fact, most people just want to consume video without actively looking for it.

Cue the pivot to Denso being a platform for curation of video content. If people wanted to use Denso as an Instapaper service, they could continue to do so by bookmarking videos to their private 'Videos I Love' channel, but if you wanted to consume video without being a curator, you could too.

As part of this strategy, we felt that it will be a nice for users to get a daily digest email which includes a list of videos which were shared the previous day. This way, even if you didn't log on to the site, you could get a digest of what was shared and then if something caught your fancy, you could log on to Denso and view it.

Go On...

So the logic initially was - the daily digest email would only be sent to users who have explicitly 'followed' channels - i.e. you should either connect your Twitter and Facebook accounts to Denso *or* you should follow one of the many channels which have auto-generated content - such as Hacker News, TED Talks, Devour and so on.

On Tuesday, I thought to myself - why don't the users who are (by default) following the Denso Staff Favorites channel, receive the daily digest? We were sharing some pretty interesting and funny videos on the Staff Favorites channel, and at the time, I felt it was reasonable for users to be exposed to videos they might not have otherwise discovered. All very innocent.

Or so I thought

Within a day, I noticed that many of our users were unsubscribing from the daily digest. We had a very high "open-rate" for our daily digest emails before Tuesday, but after Tuesday it plummeted. Our learning was that people who followed channels implicitly understood why they were receiving the daily digest, but people who didn't were wondering why the fuck they were receiving more mail from a service which is still in beta.

We also received various forms of abuse (which again I feel we deserved), and so I switched the logic back to it's previous state, i.e. you will be eligible to receive daily digest emails only if you follow more than one channel *or* if you've connected your Twitter/Facebook accounts to Denso.

Lessons Learnt

I never ever want to lose our company's greatest strength - we act decisively and quickly. Stuff is generally completed 'yesterday' and I'm extremely proud of the way we work - for a three-man team we are extremely agile and hard-working and we live and breathe our products.

But I've also learnt that the community is a big part of social software. Most of you probably don't care and some of you might snicker, "Welcome to the real world", but life is shaped by experiences such as these, and in the long run, this will (hopefully) prove to be a turning point for us.

I'd like to apologize once again and take responsibility for this error and hope you continue to use Denso.

P.S. It isn't all bad - Garry Tan (previously of Posterous) has been sharing videos on his blog from Denso, so you know - silver lining and all that :)

 

Coming To Terms

This is an unusually (for me) personal post so bear with me. When I first came to Singapore almost eleven years ago as an 18-year old dufus, one of the first things that I had to come to terms with is that I had very few friends, and there was a *high chance* that people might not like me. I lived a cocooned life for ten years in a small town in India and had the same schoolmates for 10 years and almost all the people that I dealt with was a part of my friends circle. Call me stupid, but I found it hard to come to terms with this strange phenomenon once I landed in Singapore. Once I did come to terms however, it felt like an enormous weight had been lifted off my shoulders. Yeah so what if there are some people who don't like me or who isn't a friend - that's fine, as long as I have some close friends, it's great.

Recently I had a similar epiphany in my professional life. For the longest time, I thought that arguing with someone was a sign of disrespect to that person (even when I absolutely mean no disrespect). Maybe it is an Indian/Asian thing but I digress. I felt that I might be was insulting his/her intelligence if I pointed out a mistake and so I would conveniently agree with people in order to maintain "peace". Conversely, many times I felt small and stupid when I would lose out in arguments with my peers. I know it sounds ridiculous but that's how I felt. But this recent post by GitHub's Kyle Neath put things in perspective for me.

Arguing with your co-workers isn’t a bad thing. It’s not creating a negative work environment — it’s a tool to help you make good decisions. Being an empty cheerleader and telling everyone that their idea is great is harmful and short-sighted. Argue and make good decisions.

It's not about who is right or wrong. I have begun to feel more strongly that it's important that I be wrong more often. Because if I am wrong, then someone else (hopefully my team-mate) is right and that just means a better product or service. And that's what's most important. Argue not for the sake of arguing of course, but for a higher goal.
 
So a lesson for myself is - have the right intentions at heart, and challenge yourself and your team-mates to think of better things.

Testing websockets with Jasmine /cc @jasminebdd @pusherapp

So I've been wanting to test @pusherapp with Jasmine (in my new BDD-the-shit out of everything mode) and have been poking around at a bunch of ideas. Eventually, I found a method named 'send_local_event' defined on the Pusher object which is used to trigger events. The event name for a new message received on a Pusher channel is 'message', so the corresponding Jasmine spec is as below:

Obviously, this is not entirely robust because Pusher could change this API at any point. I'd love to hear how this test can be improved.

By the way, if you haven't heard Pusher, it's a great new 'web-sockets-as-a-service' service and it has a very helpful crew behind it. If you're playing around with any kind of real-time web stuff, do check them out.

Unscientific Node.js vs Cramp benchmarks

I know microbenchmarks don't mean shit, but I was curious to find out what the difference in performance will be. I'm wary of joining on new hotness-es for the sake of it (this is the 2011 version of me, 2 years ago I would have jumped on it in a heartbeat), since there are other things to consider such as deployment ease, testability and whether it makes sense for me as well as the rest of the team to try and learn new frameworks when existing ones exist.

Nevertheless here goes, one of them is a web-app written using the Eventmachine-based Cramp framework written by lifo, the other is a Node.js based web-framework known as express. What was revealed in benchmarks (using the trusty ApacheBench tool) is this:


Express.js seems to be getting double the throughput of Cramp.