Friday, September 10, 2010
Last post on this blog
But nonetheless, I think it's time to retire this blog. It's been fun y'all, but I think it's time to move to another phase. I think I was more prolific before, since I was learning a lot. Nowadays, I'm still learning, but have been less inclined to write about it. But, I'm breaking that habit and making it a point to write again. If you'd like to follow me over, I'm at:
http://iamwil.posterous.com/
Thanks everyone for reading this blog. It's been fun. I'll see you on the other side.
Wil
Wednesday, September 01, 2010
Gotchas to getting started in Titanium
After promising myself to blog more, I ended up with my head down programming. But I'm back to let you know what I learned! About a week or two back, I got started using Appcelerator's Titanium. It's basically a framework that lets you to write native mobile apps in javascript that you can compile across multiple mobile platforms.
Titanium is actually set up like Shoes. Most of the application code is written in javascript, and then some of this code delegates its execution to Objective-C, like how in Shoes, it's Ruby up top, and for slow parts, the execution is proxy-ed to C. Therefore Titanium doesn't really "compile" to native Objective-C code, but rather, it's written in native Objective-C and uses the already built-in javascript to tie things together at the top application layer. On android, just replace Objective-C above with "Java".
I think it's a much better idea to have a scripting language on top of native code to tie things together at the application layer. Developer time goes much faster, and you definitely make less mistakes with memory management. However, there's a bunch of gotchas in getting started with Titanium. And while their marketing copy makes it seem like it's pretty painless, there's actually enough potholes in the way to stop you from even getting started.
To start, you need the correct combination of the iPhone SDK and the Titanium SDK in order to get it started and working. So make sure you have the latest SDK. I'm on the iPhone 4.0 SDK and Titanium Developer 1.2.1 with Titanium SDK 1.4.0. For those of you wondering, you can compile with a iPhone 4.0 baseline, but still compile to a lower iOS. I have a 2G iPhone running 3.1.3 firmware, and it runs fine since I'm not using any new APIs.
The other gotchas have to do with the javascript API. For example, when creating a button, we do something like this:
var logoutButton = Ti.UI.createButton({ title: "Logout", top: 55, left: 10, right: 10, height: 35 }); settingsWin.add(logoutButton);Seems pretty easy, except the documentation doesn't mention that while you can have left and right co-exist to set left and right margins, you can't actually have both top and bottom co-exist to set top and bottom margins. If you do, it simply won't display your button, and you have no idea why.
Alternatively, there is no mention of what are required options and what are optional options. For example, for textfields:
var blurbField = Ti.UI.createTextField({ hintText: "Enter Note", height:35, top: 65, left: 10, right: 10, borderStyle:Ti.UI.INPUT_BORDERSTYLE_ROUNDED }); contactWin.add(blurbField);It seems pretty elementary, but it's required that you specify the borderStyle, or else your button and textfield will simply not be displayed with no error. Buttons and textfields also require a height, otherwise, the chosen default is the full size of the screen! That doesn't make any sense. Also, when using tableViewRows, all rows with a different layout need to have a different className, for the native app to be able to conserve memory.
None of these gotchas are intuitive, and confuses the heck out of people, but I realize they're obvious to those that have first worked in Objective-C and then in Titanium. Given the goal of Titanium is to provide web devs an "in" to writing native apps without writing in native code, I'd say it's not there yet. Titanium is at best a leaky abstraction on top of the native code.
The worst part about using Titanium is its include mechanism. It isn't at all like include or require in other languages. It's actually more basic then that--essentially just a cut and paste of code. After I realized it's not a mature javascript development environment that you might be use to with in-browser javascript or node.js, things went a lot smoother. In a later post, I'll talk about Ti.include() and the follies that come with it, so you save yourself some headaches down the road.
Tuesday, August 10, 2010
Details of CouchDB Implementation
Found a detailed piece hidden away in the mailing list archives of how Couchdb works. It's from way back in 2008, but I think most of it still holds true. It's a good read, especially because to me, mysql and postgres has always been black boxes.
CouchDb's architecture seems simple enough to understand as a whole, and while the devil's in the details, it goes to show that CS 225 was super important
Monday, August 09, 2010
A short script to start work and block sites
Sometimes, I'm able to focus and I can have noise in the background, but at other times, it can be hard. If you find yourself starting to type "faceb..." every time you hit upon a slightly hard problem, then maybe what you need is to block websites that you frequently go to when you're bored.
There are plenty of services that lets you do this, including Rescuetime, I believe. Anyway, being a techie, you should know that you're able to do this just by editing your etc/hosts file.
But, I know that I need something where in order to block sites, the barrier is low, and in order to unblock them, the barrier should be high. That way, I can still unblock something when I need it, but really, it's mostly just an awareness that's needed. Once you hit upon "This website can't be found" a couple times, you really just buckle down every time there's a hard problem, instead of dodging it.
All you need to do is put the startwork script file somewhere in your execution path, and then edit your /etc/hosts files with the websites that you want blocked pointing to your localhost (127.0.0.1).
In order to run it, just type "sudo startwork". And then in order to unblock a site, "sudo [use your favorite editor] /etc/hosts" and then comment out the sites you do want with "#". Yay. Now get to work.
Wednesday, August 04, 2010
If MVP fell out of the sky and wiggled on your face, would you know it?
Minimum viable product. It's something that most of us working on a product or project know we should do, but hard to actually do in practice.
For me as an engineer, it was particularly hard because you have the ability to build, and you like doing it. Therefore you're use to building something first, and then showing it to people. That's because just telling people that you have something without having actually built it just seems like...lying.
Half the problem of an MVP is picking the right scope for your problem. But beyond that, there are other reasons that might derail you from building an MVP.
Often times, we don't know what actual MVP might look like. Other times, we have fear that gets in the way of letting us release MVP. I'll tackle the first, and then the latter in a future post.
When you're thinking about the solution to your problem, think of the simplest thing that could still work. Then cut and have it do less. Does it still solve the problem? If it doesn't, see if you can shrink the scope of the problem. If does, see if you can shrink the solution. Keep going until you only have one basic unit of good/help that you're giving to the world.
This basic unit of good is your core value proposition, and it's also what you're testing to see if it's something people will want and find useful, because at this point, you don't really know. You might know whether you'd find it useful, but you may not know if others will.
If the core proposition of your product is compelling, your users will forgive all sorts of un-polish. Don't fall into the trap of thinking that the next feature you add will be what brings the boys to the yard. There will not be just one feature that rockets you into the stratosphere. You'll just end up building fluff and throw pillows on top of a cornucopia of crap features. I think it's what they call polishing a turd (or putting lipstick on pigs).
Think about software for an airport control tower. What's its main core value proposition? To land planes. If it doesn't do that, it doesn't matter that it can land big planes, small planes, on any number of different frequencies, or how many it can do at once. No one will care because it doesn't land planes.
Another good example is Mixpanel's reporting. They report all their times in UTC, probably because they just didn't want to mess around with timezones. You'd think that in building the MVP for web analytics, you'd need to show the stats in the user's timezone. Apparently not. Mixpanel's core proposition is real-time tracking of events generated by your visitors.
Delicious's bookmarking (and by same reasoning, Flickr) is another good example. It's core value proposition is for me to save web pages and organize it easily for recall later. It's useful to me even if there's no other users around. However, the public nature of the bookmarks and tags has the added side-effect of having aggregations. It has to be first useful to a single user, even without other users around. Many new startups ignore this aspect and concentrate on the benefits when there are already a lot of users. Very rarely will you have a scale of users to leverage in the beginning.
You can much more easily position and market yourself when more focused. And when it comes to partnering, or being acquired, there's less chance for conflict. This is all so logical and, yet, there's a resistance to focusing. I think it comes from a fear of being trivial. Just remember: If you get to be #1 in your category, but your category is too small, then you can broaden your scope—and you can do so with leverage. - Evan Williams
Using quizzes to train your brain as an L1 cache for recalling people
Today, we launched quizzes at Noteleaf, where Noteleaf generates quizzes about people based on the notes you've taken of them. You can either start it from the web, or Noteleaf will email you a quiz every week. You can change the setting to 'every day', or if your memory is perfect you can set it to 'never'.
Quizzes came about from feedback and our own experiences. Usually, we have time to look people up on Noteleaf before we go and talk to them. However, there are times where we're expected to remember people on the spot, especially when we just run into them. In these cases, we can't take the time to look up people on our devices--until we have bio-implants with an interface to the web that is.
While we don't have bio-implants, we realized that we could use our brains as sort of a cache. It just needs a little bit of nudging for recall, and that's where quizzes come in. It's a fun way to review the things that you've written and use that to strengthen your brain cache.
As basic as it is, it's actually pretty fun to take the quizzes, especially if you have a lot of notes. Alex Trebec, eat your heart out.
It had meant to be a short quick feature that we pumped out, but it ended up taking more than twice the amount of time. In hindsight, the estimation was right. Quizzes didn't take that long at all--maybe two or three days. But what we didn't take into account was all the infrastructure that goes with it. The background processing jobs. The cron script setups. The mixpanel analytics. The copy.
So next time that you're doing estimation, take those things into account as well.
Sunday, August 01, 2010
Running Resque Web
1 2 3 4 | Rails::Initializer.run do |config| #....... config.middleware.use 'ResqueWeb' end |
Monday, July 19, 2010
If they can copy a fighter jet, they can copy your damn website
They copy computers, cellphones, cameras, video players, watches, bags, and everything man-made—but did you know that the Chinese also copy entire combat airplanes? It's simple: They buy one and measure every part and system to clone it.
Lately, I've taken note of both ASmartBear's It's not a Competitive Advantage and Sachin's assertion (can't find the quote right now) that when investors ask him about defensibility, he replies that they don't understand the internet.
If they can copy a fighter jet, they can copy your damn website.
What's missing from a copy? I'd say intent is one of them. Especially if your product iterates and always tries to do something hard. It'll take them longer to copy, and they'll always be on the defensive. For actual competitive advantages, check out another list by the bear.
Friday, July 09, 2010
More dead than alive
At some point soon we’ll start having an awful lot of dead people around. I’m very sure Google and other search engines will start to filter for deceased people search. It won’t be long at at until we have way more dead people online than live people online.
That's a thought I never thought about. But it may be true.
But what would even be more memorialistic(?) is if we could have online versions of ourselves running around the online world commenting and participating, as if we were alive.
I remember thinking this when I saw "I, Robot", where Will Smith talks to a holographic 2D projection of a dead investigator for clues, but the investigator can only reply to what he knew up to the point of his death.
Though I'm not sure we can recreate something like what's in the movie, it's not infeasible to be able to feed all the text archived in all your chat logs, emails, wall posts, and status messages into some machine learning algorithm so given some text directed at you, it would be able to generate what you would mostly likely say in reply. It may be just an elaborate hidden markov model or something else, but either way, just a elaborate quote machine--that quotes you.
It could be a service that you'd go to, in order to talk to someone dead--like at an oracle, shrine, or memorial. Or it would be a service that let your personality loose online and your facsimile would participate in the diggs, reddits, and facebooks of the day.
I don't even want to think about what this would mean for future religious figures born today. Imagine if you could talk to Jesus or Budda's quote machine.
It's a bit creepy, to be able to talk to a facsimile of a dead person. But it could be it's just because we're not use to the idea. I suppose it's the same as when photographs came out and you could see dead people.
However, just as the dead can't reach out from photographs to the present, quote machines of the dead wouldn't be able to reach out from the past into the present. At least, I don't know that they should.
Thursday, July 08, 2010
Picking the right problem
Nowadays, software can be updated continuously and iteratively. This lets us build what we often call minimum viable product, or MVP, and improve it from there. However, most of us don't end up building MVP despite best intentions.
The reasons are varied, from fear of negative feedback, not knowing what MVP really looks like, or thinking that your early customers want more than they actually do. The first problem founders-to-be come across when doing MVP is actually picking the scope for the problem.
When you're first building a product, you spend time thinking about what features should be included and what the benefits would be to the end-user. Then you start thinking of all the things that a user might need before they start using it. The vision for your product is big and could go in any number of directions. The opportunity and potential may be huge!
I think this is the wrong way to go about it. Before you start thinking about all that, you need pick one problem to focus on. This problem is what you're going learn whether people even want this problem solved.
But even when you find a problem that people want solved, the next most common pitfall is picking the wrong size of problem to work on. We often try to solve a version of the problem that's too big. Don't try save the world on first shot. If you can subdivide the problem into smaller problems, if it's a problem to different types of people, or solves related problems in different contexts, your problem is too large.
The advantages of picking the right, focused problem are many. It makes the product easy enough to do, it's tractable, and you can see an end in sight--which is extremely motivating. In addition, it's easy to explain to others. Don't underestimate the power that stems from the ease of conveying your idea and the problem it's trying to solve.
Don't worry about the problem being too shallow. All problems are interesting when you look deep enough.
Paypal on the surface seems easy enough in the beginning. It's like a web-bank that only does transfers, and you don't even need to connect to financial institutions for paypal to paypal transactions. But what people don't realize in trying to copy paypal is that fraud will increase the more popular your service gets, and that will eventually kill you unless you get it under control.
Twitter seemed easy enough as well in the beginning. Search the web and you'll read a lot of naysayers back in 2006-2008 where they can't understand why posting a message to the web generates so many fail whales. By nature of the interconnectedness of the data in producing the feeds for a given user with a specific set of followers, it's actually a non-trivial problem.
Groupon has been interesting because it changed how people thought of the intersection between local businesses and online ecommerce. With groupon, you go into group buying agreements with other strangers on the web to get deals. However, it's actually a small part of a larger set of problems that you could solve with a similar set of mechanics, such as group campaigning and petitioning. Instead of trying to solve all these problems, the founder decided to just focus on one: group buying.
Tuesday, June 22, 2010
Integrating Facebook into Rails
Recently, I integrated facebook’s new Graph API into Noteleaf. Though it’s far far easier than the old API, which almost didn’t warrant a blog post. However, authorization took a little bit of figuring out, so I thought I’d share.
If you’re using the languages blessed by Facebook, such as PHP, Javascript, Python, Objective-C and Java Android, then there’s already an SDK for you.
However, as a rubyist, we were on our own. Since we’re using Authlogic, we should be able to find a plugin for authenticating facebook.
On the Authlogic docs, it has authlogic_facebook_connect listed. It also depends on the most popular ruby gem for facebook integration, facebooker. However, facebooker’s documentation is shabby and the tutorials are out of date. I didn’t want to be digging around in something that wasn’t our core value proposition. So I didn’t end up going that route.
I was digging for alternatives, but in all the wrong places. It wasn’t until I was reading the facebook api docs more carefully, that I realized I should be looking for an OAuth2 module for Authlogic. After that, it was a breeze.
The instructions for authlogic_oauth2 are pretty clear, but here’s some tips. Beyond the instructions in authlogic_oauth2, make sure you set the oauth2_scope to request offline_access. If you don’t, when the user’s facebook session expires, your oauth_token that you stored in the user’s database will be expired. That means that after a while, the user won’t be able to log back in without requesting another token.
class UserSession < Authlogic::Session::Base oauth2_scope "offline_access,email" end
And if you do store the user’s facebook id locally, make sure it’s a big int.
class AddFacebookIdToUser < ActiveRecord::Migration def self.up add_column :users, :facebook_id, :bigint, :limit => 8 end def self.down remove_column :users, :facebook_id end end
which you can subsequently set in a before_create filter in User model.
class User < ActiveRecord::Base before_create :populate_oauth2_user private def populate_oauth2_user return if oauth2_token.blank? response = oauth2_access.get('/me') user_data = JSON.parse(response) if !user_data['id'].blank? self.facebook_id = user_data['id'] end end end
You may also want to consider using the provided javascript SDK. That way, you’d be able to load your page first, and have the client’s browser request the rest of the facebook data, so it appears you page loads faster.
Monday, June 21, 2010
APK Downloads for Android Projects - GitHub
For all you Android developers out there, we now will automatically detect when you upload an Android package (.apk) file and will give you a QR code page link on your download page list, like this:
Friday, June 18, 2010
Boosting my social memory
I've changed what I'm working on, as I decided to table Graphbug as a side project. Everyone said public data would be a good thing for lay-people to browse through easily, but no one could specifically think of what datasets they'd want. Like going to the dentist, it's something that's good for other people, but not themselves. In hindsight, I should have picked data to visualize for apartment hunting and moving as the niche problem to solve. Otherwise, I was trying to boil an ocean.
But that's ok, as I've moved on to found a startup to do something I've really wanted for a long long time. We're working on Noteleaf, something to boost your social memory by making it easy to take notes and follow up on the people you know and meet.
I have terrible social memory. It's not just not merely remembering people's names. That can be done with a little bit of effort and mental hacks. I can't remember what other people are doing in their lives, whether they're graduating, moving, or looking for a job. I can't remember what the name of their kids are, their girlfriend, or fiancé.
One fine day in May, I called my friend Amy up and said, "Congratulations!". She replied enthusiastically, but puzzled: "Thanks! But for what?" I told her, "For graduating dental school." She laughed, "Ahh, thanks, but I graduated last year, and you called me then to congratulate me too."
It's not that I don't care. I just can't remember. I have disparate groups of friends, so I don't get gossip about other friends to remind me of what they're doing. My friend ranges from college-aged to recent parents with kids. With everyone in different stages of life, I can't keep track. I have enough things to keep track in my own life that it's hard to have bandwidth to think of others.
Thoughtfulness has to be both relevant and timely. Relevant, because it makes no sense to congratulate someone on their new job when they are still at their old one. Timely, because wishing someone happy birthday on a day that's not their birthday doesn't have the same effect.
We wrote Noteleaf to help us do both. By taking simple notes about others, you can recall what you talked to them about last time and start right where you two left off. And by scheduling automatic followups you can be thoughtful on your own time, and they get it when it's timely for them.
Facebook has been nice in getting news about friends old and new. However, not everything you'd want to remember about them comes on the news feed. There's a transient nature to the news feed, that chances are, you won't remember whether a friend left for vacation this week or next, and you meant to give them some travel tips.
With business contacts, you may not even be facebook friends. While important, you may meet them even less, so the details of their lives are even more fleeting. It's a leg up to getting things done, when you can remember who they are and what's important to them.
Noteleaf is still in closed beta for the time being, but just put yourself on our email list, and we'll let you know when we open the gates. In the meanwhile, we also started a noteleaf blog.
For all of our busy lives, I think it's important that we are involved in the lives of those we care about, and keep making connections to new and interesting people. Because beyond the glory of work and career, the trappings of fame and accolades, and enticement of money and prestige, deep connections to people we care about is one aspect of life that fulfills us and makes us whole.
Sunday, June 13, 2010
How to generate forms in rails helpers
This is more of a note to myself than anything, since I find myself having to generate forms from helpers in Rails every once in a while. This is for Rails 2.3.5. I don’t know if Rails 3 has this problem.
I wrote my own in-place editor for model fields, and it generates a form through a helper.
1 # used internally to generate the form for the en_place editor 2 def en_place_form(record_or_array, field_name, 3 options = {}, html_options = {}) 4 # process the options, and other misc details 5 form_block = proc do 6 form_for(record_or_array, :html => html_options) do |f| 7 concat(f.text_field field_name, :class => "en_place") 8 concat(f.submit "Update") 9 end 10 end 11 is_haml? ? capture_haml(&form_block) : capture(&form_block) 12 end
All you have to do is capture a block that calls form_for or form_tag. And then inside of the form, you can to call concat on the fields that you want inside of the form.
Then lastly, you have to capture the block. If you’re using haml, you have to call capture_haml() instead.
Saturday, June 05, 2010
A left merge versus a right merge
Recently, I found out that you can set the default winner in a merge for a hash. I wasn’t going to write about it since I figured it was pretty basic. But then again, I hadn’t written in a while. Been busy. So something easy to get me back on the wagon.
I’ll call it a left merge and a right merge. Let’s say we have some options, like default options and new options in a method, and we want merge them together, where options override default options, but you store it into options.
1 # some options 2 default_opts = { :a => 1, :b => 2 } 3 opts = { :b => 3, :c => 4 }
Well, the normal way of merging things won’t work:
1 # the normal default merge 2 opts.merge!(default_opt) # => { :a => 1, :b => 2, :c => 4 }
Well, that’s not right. We want the default options to be overridden by the new options and store it in the opts variable. We could do it like this:
1 # one way of doing things 2 opts = default_opts.merge(opts) # => { :a => 1, :b => 3, :c => 4 }
But then here’s another
1 # the other merge 2 opts.merge!(default_opts) { |k,o| o } # => { :a => 1, :b => 3, :c => 4 }
Yay!
Tuesday, June 01, 2010
BrowserCouch is CouchDb for browsers
BrowserCouch Documentation
BrowserCouch is an attempt at an in-browser MapReduce implementation. It's written entirely in JavaScript and intended to work on all browsers, gracefully upgrading when support for better efficiency or feature set is detected.
Not coincidentally, this library is intended to mimic the functionality of CouchDB on the client-side, and may even support integration with CouchDB in the future.
Why?
This prototype is intended as a response to Vladimir Vukićević's blog post entitled HTML5 Web Storage and SQL. A CouchDB-like API seems like a nice solution to persistent storage on the Web because so many of its semantics are delegated out to the JavaScript language, which makes it potentially easy to standardize. Furthermore, the MapReduce paradigm also naturally takes advantage of multiple processor cores—something that is increasingly common in today's computing devices.
Things to do
To learn how to use BrowserCouch, check out the work-in-progress tutorial.
Aside from that, you can run the test suite and the semi-large data set test, though they're not particularly exciting. In the future, we'd like to make CouchDB's Futon client work entirely using BrowserCouch as its backend instead of a CouchDB server, but that's a ways away.
If you'd like to see more code samples of what the BrowserCouch API currently looks like, check out the annotated source code for the test suite. You can also read the primary source code documentation for more on BrowserCouch's implementation.
This is really awesome. I thought about doing this, but figured it was a massive undertaking. I'm glad someone's doing it. CouchDb's replication features (while not implemented yet in BrowserCouch), is something to really look forward to, especially if it's available for mobile browsers.
Friday, May 21, 2010
I am one of the creators of the first "synthetic" bacterial cell. AMA : IAmA
More specifically, I am one of the authors of this paper. I was primarily involved in the assembly of the synthetic genome. I will answer your questions to the best of my ability, and to the extent of which I am allowed to discuss these things.
Monday, May 10, 2010
Whenever I hear "I'm not good at math", it's like people telling "I can't read, and it's ok"
Granted, thinking statistically is tricky. We like to construct simple cause-and-effect stories to explain the world as we experience it. “You need to train in this way of thinking. It’s not easy,” says John Allen Paulos, a Temple University mathematician.
That’s precisely the point. We often say, rightly, that literacy is crucial to public life: If you can’t write, you can’t think. The same is now true in math. Statistics is the new grammar.
Whenever I hear "I'm not good at math", it's like people telling me "I can't read, and it's ok."
Thursday, May 06, 2010
Friday, April 30, 2010
Henry Ford's "Thoughts on Horses"
The idea of gas engines was by no means new, but this was the first time that a really serious effort had been made to put them on the market. They were received with interest rather than enthusiasm and I do not recall any one who thought that the internal combustion engine could ever have more than a limited use. All the wise people demonstrated conclusively that the engine could not compete with steam. They never thought that it might carve out a career for itself. That is the way with wise people--they are so wise and practical that they always know to a dot just why something cannot be done; they always know the limitation...
Recently, a friend was telling me that DSLR cameras will never be supplanted by cameraphones. As of 2010, it seems a silly proposition. But that's seeing camera technology as they are now, not what they will be.
I don't think DSLRs will ever go away, like the radio never went away. But I don't think it's infeasible that cameraphones will get better and better so that for many DSLR users now, it would get 'good enough'.
I sent him the post I wrote on this subject. But I don't think he read it.