Information is overpriced.

On the one hand information wants to be expensive, because it’s so valuable. The right information in the right place just changes your life. On the other hand, information wants to be free, because the cost of getting it out is getting lower and lower all the time. So you have these two fighting against each other.

Source: http://en.wikipedia.org/wiki/Information_wants_to_be_free

I started college around when Napster was getting big. Between Napster and the ability to browse other students’ shared files, I downloaded plenty of MP3s, mostly due to the novelty of being able to get one-off songs that I’d never otherwise pay for. Songs like AC/DC’s “Big Balls,” which I’d never heard before, but which I noticed hundreds of people had in their collections, so I downloaded it and laughed as Bon Scott dragged out a double entendre for about 3 minutes.

In the years since then I’ve purchased lots of CDs, many of them due to having downloaded the MP3 years earlier. I’m certainly not an audiophile, but I can hear MP3 compression artifacts in anything encoded in “joint stereo” or under 192 kbps, so I usually bought the CD so I could create a pristine rip with my own settings. I haven’t bought every album, only the ones I really liked – one of the things that made MP3 downloading so novel was the ability to get a single track rather than having to buy an entire CD of crap, which is what the music industry was trying to force everyone to do.

A few companies tried over the years to sell individual MP3s, but nobody really had much success until Apple rolled out the iTunes music store. Apple succeeded for a few reasons, but I think one of the most important was the simplicity of the pricing model: $0.99 for a single track, or $10 for a full album. No subscription fees or any of that annoying crap, just pay once and you own it. Yeah, there was annoying DRM, but 95% of people don’t care about that.

I happen to be one of the 5% that does care about that. There were ways to strip the DRM but that was already too annoying for me, plus I hated iTunes (still do) and didn’t own an iPod, so this wasn’t very appealing to me. But on a more basic level, I already owned almost all of the CDs I wanted. When new albums were released, I could usually grab them (in CD form) online for around $15 shipped. Compared to Apple’s $10 for a DRM-laden AAC, this was a no-brainer for me – a physical copy of the disc to serve as a permanent backup, and the ability to rip the entire album at any bitrate I wanted.

Fast-forward to 2011. I have an iPhone now and so I’m stuck with iTunes (which also sells DRM-free MP3s now), but I still don’t buy music from Apple. In the intervening years I’ve come to realize that I just find $0.99 per track overpriced. There’s a price I’ll pay for a song, greater than zero, but less significantly than $0.99. The last few albums I’ve bought have been $3.99 specials on Amazon, most recently Foo Fighters Wasting Light last week. 11 tracks for $4; about $0.36 per track. The CD version of this album normally sells for $9.99; the MP3 version normally sells for $7.99. For $9.99 you get a physical item shipped to you to do whatever you want – rip to MP3, lend to a friend, sell in a yard sale, donate to a library. If you get the MP3 version, you get it instantly and save $2, but lose all the other stuff. I don’t know if Amazon lets you redownload stuff you’ve purchased, so maybe you don’t need the physical medium in the event of a HD failure. But for $2 more I’d rather have the disc.

What got me thinking about this is the NY Times paywall. I love the NY Times and apparently by their measure qualify as a “heavy user,” someone who reads more than 20 articles a month. I probably read 5 to 10 stories a day. I’ve found it’s pretty easy to bypass their paywall – I’m sure they realize it’s trivial, but most people won’t bother – but I do feel kind of dirty doing it, and it’s kind of annoying. I was thinking I wouldn’t mind paying for it, but certainly not their ridiculous prices. For one thing they have ridiculous pricing distinctions depending on whether you’re just reading online, on an iPhone, or an iPad. Who cares? If you’re paying for the content you should be able to view it on any medium. You’re paying for the CONTENT! $3.75/week for “nytimes.com and Smartphone” or $8.75/week for “All access” are the options and both of them are horrendous.

How about this: fund your account with $20. Each article you read debits your account $0.01 to $0.05 depending on age (stories older than 30 days shouldn’t cost as much as today’s news – I mean, you couldn’t give yesterday’s paper away for free on the street). When your balance gets below $5 it auto-debits $20 again. You can keep all the rules you have about referrals from Twitter & Facebook being “free” but that seems kind of silly. Maybe people who pay get a more pleasant experience while freeloaders get bombarded with ads. Paid users should also be able to see a full report of every article they’ve “purchased” and the date.

I guess my point is that I wouldn’t mind paying for this content, but it’s overpriced. I understand that people need to be paid, but when CDs were $20, I didn’t buy any. When they dropped to $10, I bought plenty. If the NY Times is $200/year, I’m not going to pay for it. If it’s $40/year, I might. If it’s $20/year I’d definitely pay just to assuage my conscience. I want the NY Times to continue to exist and I understand someone has to pay the reporters and everyone else involved, but I’m not paying $200/year for it. I have a price in my head, what I think it’s worth. If you’re not near that, I’m not paying. A year ago my DVD player died and I wanted to get a PS3 for use as a BluRay player, but there was no way I was paying $300 for it, even though it’s probably a fair price. If it were $150 I would have snatched it up, it just was not worth $300 to me. In the end I bought a $50 Sony upscaling DVD player.

The NYTimes has overpriced itself as well, so I’m going with the cheaper alternative – bypassing the paywall and viewing the content anyway. They could tighten up the paywall, and maybe I’d find a way around it, or maybe I’d just get my news elsewhere, in which case we both lose. If there was a “name your own price” way to make it work, everyone would win.

Teaching myself node.js: Part 3

I’ve been playing around with node, JavaScript & MongoDB a bit with this project and one of the things I ran into was MongoDB’s lack of a sequential ID. MongoDB defaults to ObjectIDs for primary keys in collections, and they have good reasons for doing so, but in writing this app, I’d rather have URLs look like “/question/423” than “/question/3001024e521e9c6500000000”.

Fortunately, there’s a relatively convenient workaround for this problem, detailed here. Essentially, create a collection in which each document is a counter. The ID for the document is its “name” and the property “next” contains the next value. This is similar to creating and using a sequence in PostgreSQL, though hopefully in the future they’ll provide an easier way to do this – perhaps a built-in sequence object.

First, in the mongo shell, I created the counter(name) function as specified in the Mongo wiki:

> counter
function counter(name) {
    var ret = db.counters.findAndModify({query:{_id:name}, update:{$inc:{next:1}}, 'new':true, upsert:true});
    return ret.next;
}

Then I created the counter document for the “questions” collection (so questions can have numeric IDs):

db.counters.insert({_id:"questions", next: 1});

This is optional – the counter() function will create the document in the counters collection if needed, with an initial value of 1.

That’s it. Each call to counter() will now return an incrementing number:

> db.counters.find();
{ "_id" : "questions", "next" : 3 }
> counter("joe");
1
> db.counters.find();
{ "_id" : "questions", "next" : 3 }
{ "_id" : "joe", "next" : 1 }
> counter("joe");
2
> counter("joe");
3

Cool. So how do we save the counter() function in the DB so we can use it in queries without having to define it every time? Fortunately Mongo makes saving functions on the server easy:

> db.system.js.save( { _id : "counter" , value : counter });
> db.system.js.find();
{ "_id" : "counter", "value" : function cf__2__f_counter(name) {
    var ret = db.counters.findAndModify({query:{_id:name}, update:{$inc:{next:1}}, 'new':true, upsert:true});
    return ret.next;
} }

Calling the stored procedure directly in the shell is sort of strange, but it works:

> db.eval("return counter('question')");
3
> db.eval("return counter('question')");
4
> bye
[Wed Jun 22 13:14:10 evan@EvanMBP 3 ~]$ ~/Downloads/mongodb-osx-x86_64-1.8.1/bin/mongo questionsMongoDB shell version: 1.8.1
connecting to: questions
> db.eval("return counter('question')");
5

Unfortunately, I was unable to figure out how to call the stored function from within the mongoskin driver, and ended up writing analogous code, calling findAndModify() within the app code. I finally got it:

        db.collection('counters').findAndModify(
                {_id:'questions'},
                [],
                {$inc : {next: 1}},
                true,
                true,
                function(err, counter) {
                        if (err) { throw new Error(err); }
                        var ins = { date: new Date(),
                                author: req.body.author, body: req.body.body,
                                tags: tags, tag_count: tagCount,
                                answers: [], votes: 0,
                                _id: counter.next
                        };
                        db.collection('questions').insert(ins, {});
                        res.end('Added new question: '+req.body.body);
                }
        );

Question detail
Question detail

I tried throwing some code in there to query by either ObjectId() or integer but it didn’t really work (Edit: I tried this again on Linux and it worked fine), so I just deleted all the documents with ObjectIds and everything seems to work fine now. I think the next step is going to have to be some sort of authentication/session stuff, because typing your name in every time kind of sucks (and makes it hard to do things like list questions by user).

Full code for this revision is here.

JavaScript regex for stripping leading & trailing whitespaces

I’m sure there are a bunch of libraries that do this, but sometimes wrestling with regexes is fun.

> var str = "           This string has some mighty fine whitespace.       ";
> str
'           This string has some mighty fine whitespace.       '
> str.replace(/^(\s*)((\S+\s*?)*)(\s*)$/,"$2");
'This string has some mighty fine whitespace.'

Teaching myself node.js: Part 2

I did a little more work today on my little node.js project and added the ability to click on a tag and have the app list all questions with that tag, and a super basic method for adding new questions. Neither of these were really complicated; the most “challenging” part was figuring out how to create links and form inputs with jade. In the end I was surprised at how easy it really was.

For the tag search I just copied the app.get('/questions') route to a new app.get('/tags/:tag') route in app.js and modified the mongo find() query to search for the tag. I created a new Jade template for this, but I suppose the same list.jade template would have worked, since the only thing that was changed was the contents of the questions array.

For adding a new question I created an app.get('/question/new') route that loaded a jade template with the form (addquestion.jade), and which did a POST back to /question and the corresponding app.post('/question'). Right now the form just takes text input fields, but eventually there’ll need to be some notion of users, and other fun functionality. Also after doing the insert into Mongo, it just spits back some plaintext response, but it gets the job done.

I should add that this is my first time writing a webapp using “routes.” It’s been a while since I really did any front-end coding at all and I’m used to the “old school” method of one script per function, where the “add a new question” function would be handled by “addQuestion.php” for instance. Using routes and MVC are part of what I’m trying to learn in doing this.

Commit for this version is here

Teaching myself node.js: Part 1

I’ve been meaning for a while to learn node.js. I read a couple of books and plenty of blog posts but in the end I find, as usual, I can only learn by doing. I have most of the fundamentals to get started, I just need to have a project. The typical tutorials I’ve found focus on creating a blog; I’m thinking of something more along the lines of a Quora question/answer site. I’ll be trying to write this as I go, which will hopefully prod me into actually doing it this time. (I have a tendency to bounce around different tenses and I expect this will be worse than usual – stuff will likely be written in past/present/future at various points).
Continue reading “Teaching myself node.js: Part 1”