Thursday, December 27, 2007

New Just Three Words stuff

Between November 1 and now, we've been re-building Just Three Words into a really deep, functional application for Facebook.

(It is a collaborative writing application as a game - you can only enter 1 to 3 words at a time until someone else posts their words).

And it's really cool. And fun! Paul's design really works well and is simple to use.

We're constantly improving the design, and changing the features based on user's input. One of the values we maintain is that we're in an ongoing conversation with our users. A "partnership" (not in the business sense of the word, of course).

They provide content, and we provide the means for them to create and develop their content. Most of the time, we take our best guess as to what will make the best experience for the users, changing or tweaking based on their input and on the metrics we collect & analyze. Other times, we create functions that are straight from user experience and the inevitable "You know what would be cool?" email from a user.

(We use both Google Analytics and our own metrics-collection, and spend a lot of time poring over the data)

One of the things that helps us, I think, is that we love our users. We're convinced we attract the best people on Facebook, and some of the most creative. We hit the jackpot, got lucky, whatever you want to call it - and I'm not sure who's more enthused: the writers for our application or us for our writers.

If you haven't seen it yet, go check it out.

.

New Year's Resolutions

What a year. Lots of change, learning, and it's all good.

In 2007, I:
  • Lost my job,
  • Ended my music publishing company,
  • Started a new web startup, built an entire web-application twice.
  • Stopped all work on the IBM iSeries & abandoned that company/career choice,
  • Re-skilled myself almost completely by learning python, actionscript3 & Flex, javascript, linux, django, and translated my DB2 skills to mySQL,
  • Created two facebook apps with Paul,
  • Ended the new web startup,
  • Started a new new web startup,
  • Met some amazing entrepreneurs and coders in LA and SF,
  • Did more all-nighters than in the previous 10 years combined
  • Had a great time.

Now, what about 2008. Hm. I hope it's as good as 2007 was - even better, building upon the foundations set this year.

Speed up your Facebook Django app

One of the things I loved about the iSeries was the easy, easy way we could toss a job to batch from the middle of an interactive session.

It is also one of the things I miss while coding in Django.

What's this have to do with Facebook apps?

Every call to the FB API costs time - from 280ms to 475ms on average - and the more calls, the more time. Where submitting some work really comes in handy is during processing after a user-gesture. Want to publish a bunch of news after the user clicks "BITE ME" and not have the user sit there and wait (and maybe experience an FB timeout) while you send minifeed news and notifications to 4,219 20 of their BFFs?

Use a thread. Really, use a threadpool, but I haven't coded mine yet. It doesn't take long to code, but the holidays grabbed me around the ankle and forced me to take time with friends, so there you go.

But the basics are here:

import threading

def sbmthrjob(cmd, *args):
class newThread(threading.Thread):
def __init__(self, cmd, *args):
self.cmd = cmd
self.args = args
threading.Thread.__init__(self)

def run(self):
self.cmd(*args)

try:
thread1 = newThread(cmd, *args)
thread1.setDaemon(True)
thread1.start()

except Exception, e:
print 'sbmjob exception: ' , e
elsewhere:

sbmthrjob(cmd, argle, bargle)



What I want to do is to have the thread come from a pool of already-created threads - I'll get to that soon.

Django and MySql character set tip

A couple of tips. The first one is from Leah Culver via her blog:

Python lesson I learned today - use “is None” when you want to check if something exists.
[...]

Here’s where it hurt:

def get_notes(qs=None):
if not qs:
qs = self.notes.all()
[...]

The correct condition is “if qs is None” like so:

def get_notes(qs=None):
if qs is None: # no predefined queryset
qs = self.notes.all()



Next one is all mine:

Our MySql implementation was set to default to character set latin1 and collation latin1_swedish_ci. No big deal, says I, I'll change that globally to utf8 and utf8_general_ci so we can handle non-latin charsets like cyrillic and greek.

But no go - when non-latin charactersets were used, the inserts to the database would fail. What the heck? Well.. the problem was that I created the tables before I changed the global default values from latin1 to utf8. The tables still had the old default character sets. Easy enough, just alter the table to use default charset utf8.

Nope. So even though the tables' properties would show ENGINE=InnoDB DEFAULT CHARSET=utf8 some of the text columns would still had a default charset of latin1.

Eventually, I did this and now it all works fine:

ALTER TABLE tablename DEFAULT CHARSET=utf8;
ALTER TABLE tablename CHANGE col1 col1 varchar(128) character set utf8 NOT NULL;
ALTER TABLE tablename CHANGE col2 col2 varchar(1024) character set utf8 NOT NULL;


I'm sure I'M DOING IT ALL WRONG, so make a comment if you just can't hold back.

Out with the third, in with the fourth

We seen the last of Good King Richard
Ring out the past his name lives on
Roll out the bones and raise up your pitcher
Raise up your glass to Good King John*
So, the old company is now dead, and the new company is moving along nicely. And more quickly than its predecessor. You can find us now at www.scoobandgecko.com

And now that is 4 failures behind me: two absolute wrecks, and two relative failures that could be counted as successes in someone else's book (just not mine). Great. Ok, learned a lot. Next!!

Why'd we kill the old project? The site-whom-what-we-must-not-name? It wasn't scalable, and it wasn't something that lent well to a strategy that included the social networks. And, there were people issues. Enough said.

So, stay tuned.. I'll keep you updated!


* Steely Dan, Kings, Can't Buy a Thrill